Android按钮点击事件的Kotlin协程流示例?

0s7z1bwu  于 2023-03-30  发布在  Kotlin
关注(0)|答案(2)|浏览(203)

我曾经使用Channel从Anko View类向Activity类发送click事件,但是越来越多的Channel函数被标记为已弃用。所以我想开始使用Flow apis。
我迁移了下面的代码:

private val btnProduceChannel = Channel<Unit>()
val btnChannel : ReceiveChannel<Unit> = btnProduceChannel 

// Anko
button {
    onClick {
        btnProduceChannel.send(Unit)
    }
}

致:

lateinit var btnFlow: Flow<Unit>
    private set

button {
    btnFlow = flow { 
       onClick { 
            emit(Unit) 
       }
    }
}

我现在必须将流属性标记为var,这不像以前那么优雅。这种方式是正确的吗?我可以在定义属性时初始化一个Rx Subject,如Flow
编辑:
我带回了Channel,然后使用了consumeAsFlow()

private val btnChannel = Channel<Unit>()

// This can be collected only once
val btnFlow = btnChannel.consumeAsFlow()

// Or add get() to make property can be collected multiple times
// But the "get()" can be easily forgotten and I don't know the performance of create flow every access
val btnFlow get() = btnChannel.consumeAsFlow()

// Send event with btnChannel

这看起来比lateinit var好,但是有没有办法完全摆脱Channel呢?(虽然Flow本身和callbackFlow一样,channelFlow也在使用通道)

mmvthczy

mmvthczy1#

虽然我在我的项目中没有使用Anko,但我编写了这个函数来使用常规的按钮引用,看看它是否对你有帮助:

fun View.clicks(): Flow<Unit> = callbackFlow {
    setOnClickListener {
        offer(Unit)
    }
    awaitClose { setOnClickListener(null) }
}

可能使用的示例是:

button.clicks()
   .onEach { /*React on a click event*/ }
   .launchIn(lifecycleScope)

更新

正如@Micer在对原始答案的评论中提到的那样,Channel#offer方法已被弃用,转而支持Channel#trySend方法。
更新版本:

fun View.clicks() = callbackFlow<Unit> {
    setOnClickListener {
        trySend(Unit)
    }
    awaitClose { setOnClickListener(null)}
}
3pmvbmvn

3pmvbmvn2#

献给Kotlin爱好者

使用callbacFlow

fun View.clicks() = callbackFlow {
setOnClickListener {
    this.trySend(Unit).isSuccess
}
 awaitClose { setOnClickListener(null) }
}
  • 用途 *
bind.btn.clicks().onEach {
 // do your staff
}.launchIn(lifecycleScope)

使用Channel eventActor

fun View.setOnClick(action: suspend () -> Unit) {
 // launch one actor as a parent of the context job
 val scope = (context as? CoroutineScope) ?: AppScope
 val eventActor = scope.actor<Unit>(capacity = Channel.CONFLATED) {
     for (event in channel) action()
  }
    // install a listener to activate this actor
    setOnClickListener { eventActor.trySend(Unit).isSuccess }
}
  • 用途 *
bind.btn.setOnClick {
    // do your staff
  }

相关问题