在片段上实现带有回调的接口
implementing an interface with callback on a fragment
我正在学习 android 编程和练习我正在尝试为一些直流电机做一个控制器,然后我做了一个自定义视图来制作一个虚拟游戏手柄,它使用一个接口和一个回调ontouch 侦听器。
问题是,我正在开发我的应用程序,使用单个 MainActivity 作为导航主机,然后我在不同的片段中导航,当我重写我的接口方法时,我的自定义视图才起作用MainActivity 但我无法让它在我的片段上运行,我想在其中处理游戏手柄的所有逻辑。
我已经研究了几天,但我发现的大多数 post 都是写在 Java 上的,我无法让它在 Kotlin 上运行。
我的自定义视图class
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){}
}
...
}
我的主要Activity
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 方法调用该函数,但我也无法使其工作。对于如何实施此功能的任何帮助或建议,我将不胜感激,在此先感谢!
这个:
if (context is joypadListener){
是获取听众参考的一种非常笨拙且容易出错的方法。它也非常有限,因为它使侦听器不可能成为创建视图的 Activity 之外的任何东西。不要这样做!
您已经有一个手柄监听器 属性。只需删除 private
关键字,这样任何 class 都可以从外部设置它想要的任何侦听器。删除整个 try/catch 块。当需要调用侦听器的函数时,使用 null-safe ?.
调用来执行此操作,以便在尚未设置回调时优雅地不执行任何操作。
旁注:所有 class 和接口名称都应按照惯例以大写字母开头。如果您不遵循此约定,您的代码将变得很难阅读和解释。
此外,我建议您避免使用使 class 实现接口的模式,以用作它正在操作的对象之一或其自身内部函数的回调。您在上面的代码中执行了两次。您的自定义视图为自己的触摸侦听器执行此操作,而您的 Activity 将其用作游戏手柄侦听器。
原因是它公开暴露了class的内部功能并减少了模块化。它会使单元测试更加困难。它不必要地暴露了滥用您设计的 class 的方法。对于 Activity 来说,这样做并不是什么大不了的事情,因为无论如何你都很少从外部使用 Activity 个实例。但是视图 class 这样做很难看。
另一种方法是将接口实现为匿名对象或 lambda,这样回调的功能对外部 classes 是隐藏的。
编辑:如何在您的片段中执行此操作
如果您想听从我上面的建议,请不要在 classes 中实现回调接口。请改用 lambda 或匿名 classes。
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())
}
}
}
}
我正在学习 android 编程和练习我正在尝试为一些直流电机做一个控制器,然后我做了一个自定义视图来制作一个虚拟游戏手柄,它使用一个接口和一个回调ontouch 侦听器。
问题是,我正在开发我的应用程序,使用单个 MainActivity 作为导航主机,然后我在不同的片段中导航,当我重写我的接口方法时,我的自定义视图才起作用MainActivity 但我无法让它在我的片段上运行,我想在其中处理游戏手柄的所有逻辑。
我已经研究了几天,但我发现的大多数 post 都是写在 Java 上的,我无法让它在 Kotlin 上运行。
我的自定义视图class
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){}
}
...
}
我的主要Activity
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 方法调用该函数,但我也无法使其工作。对于如何实施此功能的任何帮助或建议,我将不胜感激,在此先感谢!
这个:
if (context is joypadListener){
是获取听众参考的一种非常笨拙且容易出错的方法。它也非常有限,因为它使侦听器不可能成为创建视图的 Activity 之外的任何东西。不要这样做!
您已经有一个手柄监听器 属性。只需删除 private
关键字,这样任何 class 都可以从外部设置它想要的任何侦听器。删除整个 try/catch 块。当需要调用侦听器的函数时,使用 null-safe ?.
调用来执行此操作,以便在尚未设置回调时优雅地不执行任何操作。
旁注:所有 class 和接口名称都应按照惯例以大写字母开头。如果您不遵循此约定,您的代码将变得很难阅读和解释。
此外,我建议您避免使用使 class 实现接口的模式,以用作它正在操作的对象之一或其自身内部函数的回调。您在上面的代码中执行了两次。您的自定义视图为自己的触摸侦听器执行此操作,而您的 Activity 将其用作游戏手柄侦听器。
原因是它公开暴露了class的内部功能并减少了模块化。它会使单元测试更加困难。它不必要地暴露了滥用您设计的 class 的方法。对于 Activity 来说,这样做并不是什么大不了的事情,因为无论如何你都很少从外部使用 Activity 个实例。但是视图 class 这样做很难看。
另一种方法是将接口实现为匿名对象或 lambda,这样回调的功能对外部 classes 是隐藏的。
编辑:如何在您的片段中执行此操作
如果您想听从我上面的建议,请不要在 classes 中实现回调接口。请改用 lambda 或匿名 classes。
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())
}
}
}
}