首页 > 解决方案 > Android/Kotlin,如何减少此代码的重复部分

问题描述

我是库作者,必须通过覆盖 ViewGroup.onInterceptTouchEvent() 来拦截子视图的所有触摸事件。

首先,我编写了以下代码(简化):

interface touchIntercepter {
    // my library set this field to intercept touch event
    var touchHandler: ((MotionEvent) -> Boolean)?
}

class LinearLayoutTouchIntercepter @JvmOverloads constructor (
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
 )
    : touchIntercepter
    , LinearLayout(context, attrs, defStyleAttr, defStyleRes)
{
    override var touchHandler: ((MotionEvent) -> Boolean)? = null

    override fun onInterceptTouchEvent(event: MotionEvent) = touchHandler?.invoke(event) ?: false
}

库用户可以在他们的布局 xml 文件中使用 LinearLayoutTouchInterceptor 而不是标准的 LinearLayout,然后我的库代码可以通过 touchIntercepter 接口拦截用户布局的子视图的触摸事件。

我认为如果有 ViewGroup.setOnInterceptTouchListener() 之类的东西,View.setOnClickListener() 之类的东西,那就太好了,但我发现没有。

现在的问题是,我想为 RelativeLayout、FrameLayout 和其他 ViewGroup 后代提供相同的功能。

例如,

class RelativeLayoutTouchIntercepter @JvmOverloads constructor (
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
 )
    : touchIntercepter
    , RelativeLayout(context, attrs, defStyleAttr, defStyleRes)
{
    override var touchHandler: ((MotionEvent) -> Boolean)? = null

    override fun onInterceptTouchEvent(event: MotionEvent) = touchHandler?.invoke(event) ?: false
}

如您所见,所有代码都是相同的,但唯一的区别是继承 XXXXXLayout 而不是 LinearLayout。我不想复制和粘贴它们,但不知道如何减少重复。

似乎 Kotlin 泛型在这种情况下没有帮助,而 C++ 模板完美地可以帮助像这样的伪代码:

template <typename T>
class TouchInterceptorTmpl : public T
{
    void onInterceptTouchEvent() override;
};

using RelativeLayoutTouchInterceptor = TouchInterceptorTmpl<RelativeLayout>;
using FrameLayoutTouchInterceptor = TouchInterceptorTmpl<FrameLayout>;

在 Kotlin 中没有办法这样做吗?

标签: androidkotlin

解决方案


您可以通过对接口进行具体实现并将其用作委托来减少重复。onInterceptTouchEvent不幸的是,由于继承的工作原理,您无法避免在每个实现中重写,但您可以为您的接口创建一个扩展函数以稍微缩短该代码。

请注意,Kotlin 中的接口名称按约定大写。

设置:

interface TouchInterceptor {
    var touchInterceptionHandler: ((MotionEvent) -> Boolean)?
}

class TouchInterceptorImpl: TouchInterceptor {
    override var touchInterceptionHandler: ((MotionEvent) -> Boolean)? = null
}

fun TouchInterceptor.intercept(event: MotionEvent): Boolean = touchInterceptionHandler?.invoke(event) ?: false

用法:

class RelativeLayoutTouchIntercepter @JvmOverloads constructor (
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
 )
    : TouchInterceptor by TouchInterceptorImpl()
    , RelativeLayout(context, attrs, defStyleAttr, defStyleRes)
{
    override fun onInterceptTouchEvent(event: MotionEvent): Boolean = intercept(event)
}

推荐阅读