0

我正在学习 android 编程并且为了练习我正在尝试为一些直流电机做一个控制器,然后我做了一个 customview 来制作一个虚拟手柄,它使用一个接口和一个回调用于 ontouch 侦听器。

问题是,我正在使用单个 MainActivity 作为导航主机来开发我的应用程序,然后我正在浏览不同的片段,当我覆盖 MainActivity 上的接口方法时,我的自定义视图才有效,但我无法让它工作我的片段,我想在其中处理手柄的所有逻辑。

我进行了几天的研究,但我发现的大多数帖子都是用 Java 编写的,我无法让它在 Kotlin 上运行。

我的自定义视图类


class KanJoypadView: SurfaceView, SurfaceHolder.Callback, View.OnTouchListener{  

    ...kotlin
    var joypadCallback: JoypadListener? = null

    //the main constructor for the class
    constructor(context: Context): super(context){
        ...

        getHolder().addCallback(this)
        setOnTouchListener(this)

        ...
}

        //the interface for the main functionally of the view
        interface JoypadListener {
            fun onJoypadMove(x: Float, y: Float, src: Int){}
        }

        ...

}

我的主要活动


class NavActivity : AppCompatActivity(), KanJoypadView.joypadListener {
    ...
    
    //Overriding the Function from the interface, 
    //I just did this for debguging, but I dont want this override here
    override fun onJoypadMove(x: Float, y: Float, src: Int) {
        Log.d(src.toString(), y.toString()) //** I wanna do this in my Fragment, not in my activity **
    }

}

我的片段


class JoystickFragment : Fragment(), KanJoypadView.joypadListener {

    ...

    var enginesArray = arrayOf(0.toFloat(), 0.toFloat(), 0.toFloat(), 0.toFloat())

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
    
    ...
    
        val binding = DataBindingUtil.inflate(
                inflater, R.layout.fragment_joystick, container, false
        )
        binding.leftJoypad.joypadCallback = (container?.context as KanJoypadView.JoypadListener?)
        lJoypad = binding.leftJoypad.id
    }

    /*what I really want to do, but it is not happening as it is just happenning the
 override from the NavActivity, which I dont need, and not from here which I need*/
    override fun onJoypadMove(x: Float, y: Float, src: Int) {
        if (src == lJoypad) {
            if (y >= 0) {
                enginesArray[0] = 1.toFloat()
                enginesArray[1] = y
            } else if (y < 0) {
                enginesArray[0] = 0.toFloat()
                enginesArray[1] = y
            }
            if (src == rJoypad) {
                if (y >= 0) {
                    enginesArray[0] = 1.toFloat()
                    enginesArray[1] = y
                } else if (yAxis < 0) {
                    enginesArray[0] = 0.toFloat()
                    enginesArray[1] = y
                }
                Log.d("Engines array", enginesArray.toString())
            }
        }
    }

}

我还尝试在片段中创建一个函数,然后从 Activity 的 onMoveJoypad 方法调用该函数,但我也无法使其工作。我将不胜感激有关如何实现此功能的任何帮助或建议,在此先感谢!

4

1 回答 1

0

这个:

if (context is joypadListener){

是一种获取听众参考的非常hacky且容易出错的方法。这也是非常有限的,因为它使得侦听器不可能是创建视图的 Activity 之外的任何东西。不要这样做!

您已经有一个手柄监听器属性。只需删除private关键字,以便任何类都可以从外部设置它想要的任何侦听器。删除整个 try/catch 块。当需要调用侦听器的函数时,请使用 null 安全?.调用来执行此操作,以便在尚未设置回调的情况下优雅地不执行任何操作。

旁注:按照惯例,所有类和接口名称都应以大写字母开头。如果您不遵守此约定,您的代码将变得非常难以阅读和解释。

另外,我建议您避免使用使类实现接口的模式,以作为它正在操作的对象之一或它自己的内部函数的回调。您在上面的代码中这样做了两次。您的自定义视图为其自己的触摸侦听器执行此操作,而您的 Activity 执行此操作以充当游戏板侦听器。

原因是它公开暴露了类的内部功能并降低了模块化。它会使单元测试更加困难。它不必要地暴露了滥用您设计的类的方法。对活动来说,这样做并不是什么大不了的事,因为无论如何你很少使用外部的活动实例。但是视图类这样做很难看。

另一种方法是将接口实现为匿名对象或 lambda,以便对外部类隐藏回调的功能。


编辑:如何在您的片段中执行此操作

如果你想听从我上面的建议,不要在你的类中实现回调接口。请改用 lambda 或匿名类。

class JoystickFragment : Fragment() {

    //...

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        //...

        adapter.joypadCallback = joypadListener

        //...
    }

    private val joypadListener = KanJoypadView.JoypadListener { x, y, src ->
        if (src == lJoypad) {
            if (y >= 0) {
                enginesArray[0] = 1.toFloat()
                enginesArray[1] = y
            } else if (y < 0) {
                enginesArray[0] = 0.toFloat()
                enginesArray[1] = y
            }
            if (src == rJoypad) {
                if (y >= 0) {
                    enginesArray[0] = 1.toFloat()
                    enginesArray[1] = y
                } else if (yAxis < 0) {
                    enginesArray[0] = 0.toFloat()
                    enginesArray[1] = y
                }
                Log.d("Engines array", enginesArray.toString())
            }
        }
    }

}
于 2022-02-18T05:35:15.927 回答