面试题答案
一键面试1. 使用 SAM 转换处理用户交互逻辑
- 定义函数式接口:
已经给定了如
OnDragListener
、OnLongPressListener
等函数式接口,这些接口只有一个抽象方法。例如,假设OnDragListener
定义如下:@FunctionalInterface public interface OnDragListener { void onDrag(int x, int y); }
- 在
CustomView
中使用: 在CustomView
类中,可以定义方法接收这些函数式接口的实例。比如:
在public class CustomView extends View { private OnDragListener onDragListener; private OnLongPressListener onLongPressListener; public void setOnDragListener(OnDragListener listener) { this.onDragListener = listener; } public void setOnLongPressListener(OnLongPressListener listener) { this.onLongPressListener = listener; } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { long startTime = System.currentTimeMillis(); // 模拟长按检测 new Handler().postDelayed(() -> { if (System.currentTimeMillis() - startTime > 500 && onLongPressListener != null) { onLongPressListener.onLongPress(); } }, 500); } else if (event.getAction() == MotionEvent.ACTION_MOVE) { if (onDragListener != null) { onDragListener.onDrag((int) event.getX(), (int) event.getY()); } } return true; } }
onTouchEvent
方法中,根据不同的触摸事件类型,调用相应函数式接口的方法。这里使用了 SAM 转换,当我们为CustomView
设置监听器时,可以直接使用 lambda 表达式。例如:CustomView customView = findViewById(R.id.custom_view); customView.setOnDragListener((x, y) -> { // 处理拖动逻辑 Log.d("CustomView", "Dragging to x: " + x + ", y: " + y); }); customView.setOnLongPressListener(() -> { // 处理长按逻辑 Log.d("CustomView", "Long pressed"); });
2. 处理多个函数式接口时可能遇到的问题及解决方案
- 问题:
- 混淆问题:当有多个相似的函数式接口时,可能会混淆它们的方法。例如,如果有
OnClickOnceListener
和OnClickTwiceListener
,可能会不小心在错误的地方使用错误的接口。 - 命名冲突:如果在不同的库或模块中定义了同名的函数式接口,可能会导致编译错误。
- 代码可读性:随着函数式接口数量的增加,
CustomView
类中的代码可能变得复杂,难以理解和维护。
- 混淆问题:当有多个相似的函数式接口时,可能会混淆它们的方法。例如,如果有
- 解决方案:
- 清晰命名:给函数式接口起清晰、有意义的名字,避免命名冲突和混淆。例如,
OnSingleTapListener
和OnDoubleTapListener
就比简单的OnTapListener
更清晰。 - 使用注释:在函数式接口和使用它们的地方添加详细的注释,说明每个接口的用途和期望的行为。例如:
/** * 用于处理用户在 CustomView 上的长按操作 */ @FunctionalInterface public interface OnLongPressListener { void onLongPress(); }
- 模块化代码:将与不同函数式接口相关的逻辑分离到不同的方法或类中,提高代码的可读性和可维护性。例如,可以将与拖动相关的逻辑封装到一个
DragHandler
类中,将长按相关的逻辑封装到LongPressHandler
类中。
然后在public class DragHandler { private OnDragListener onDragListener; public DragHandler(OnDragListener listener) { this.onDragListener = listener; } public void handleDrag(int x, int y) { if (onDragListener != null) { onDragListener.onDrag(x, y); } } }
CustomView
中:public class CustomView extends View { private DragHandler dragHandler; private LongPressHandler longPressHandler; public CustomView(Context context, AttributeSet attrs) { super(context, attrs); dragHandler = new DragHandler(null); longPressHandler = new LongPressHandler(null); } public void setOnDragListener(OnDragListener listener) { dragHandler = new DragHandler(listener); } public void setOnLongPressListener(OnLongPressListener listener) { longPressHandler = new LongPressHandler(listener); } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { longPressHandler.startLongPressCheck(event.getDownTime()); } else if (event.getAction() == MotionEvent.ACTION_MOVE) { dragHandler.handleDrag((int) event.getX(), (int) event.getY()); } return true; } }
- 清晰命名:给函数式接口起清晰、有意义的名字,避免命名冲突和混淆。例如,