工作中想要实现这么一个效果:
如图中,当child有一个click事件,parent有一个longclick事件,当长按child的时候能够触发parent的longclick。
遇到的问题:
当child设置click事件时,长按child不会触发parent的longclick事件。
解决方案
1.当时我的解决方案是为child设置一个和parent一样的longclick事件,这样能够解决该问题,但是实际应用中,parent不止一个child,会有3个以上的child,那么将要为所有的child设置longclick事件,并且写出的代码不美观。
2.第二种解决方式能否让代码自动识别你的意图,是想要触发parent的longclick还是child的click事件。
接下来说明一下第二种解决方式。
对于android事件的派发机制这里不多说,网上能够找到很多的相关博客。在view的onTouchEvent方法中会看到这么一段代码
public boolean onTouchEvent(MotionEvent event) {
……
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
……
return true;
}
return false;
}
其中的判断语句能够很明显的看出来,不论view设置了click还是longclick事件,该view都会消费该点击事件,否则才会把事件再交由父布局的onTouchEvent。需求中,child消费了click事件,所以parent的longclick自然不会被触发。
这里我给出的解决方案是重写parent的dispatchevent方法,根据判断用户点击时间的长短来判断是否将事件派发给child。
public class MyRelativeLayout extends RelativeLayout {
public final static String TAG = "ClickTestActivity";
Activity mActivity;
int mTouchSlop; //最短滑动距离
public MyRelativeLayout(Context context) {
super(context);
init(context);
}
public MyRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
mActivity = (Activity) context;
mTouchSlop = ViewConfiguration.get(mActivity).getScaledTouchSlop();
}
private boolean isLongClick = false; //是否是长点击事件
private boolean isRelease = false; //是否已经释放
private static int LONG_CLICK_TIME = 600;
private LongClickListener longClickListener;
//自定义长点击事件接口
public interface LongClickListener {
void OnLongClick();
}
public void setLongClickListener(LongClickListener l) {
this.longClickListener = l;
}
private Runnable countDownRunnable = new Runnable() {
@Override
public void run() {
isLongClick = true;
//当用户在LONG_CLICK_TIME时间内没有做抬起滑动等取消动作,则触发longclick事件
if(!isRelease) {
return;
}
isRelease = true;
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
if(longClickListener != null) {
longClickListener.OnLongClick();
}
}
});
}
};
//记录按下时的坐标
int downX = 0;
int downY = 0;
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
downY = (int) event.getY();
isRelease = false;
isLongClick = false;
//延迟LONG_CLICK_TIME毫秒的时间,触发长点击事件
postDelayed(countDownRunnable, LONG_CLICK_TIME);
break;
case MotionEvent.ACTION_MOVE:
//当横移或纵移的长度大于系统规定的滑动最短距离时,则视为用户取消了longclick事件
if(Math.abs(event.getX() - downX) < mTouchSlop || Math.abs(event.getY() - downY) < mTouchSlop || isRelease) {
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_OUTSIDE:
isRelease = true;
if(isLongClick) {
//当已经是longclick事件时,parent则拦截该事件,child不会再收到该事件
return true;
}
break;
}
boolean isDispatch = super.dispatchTouchEvent(event);
if(event.getAction() == MotionEvent.ACTION_DOWN && !isDispatch) {
//当down事件返回false时 不触发up事件 所以返回true强制触发UP事件,否则会出现click父布局出现longclick的效果
return true;
}
return isDispatch;
}
}
具体的实现方案就是这样,接下来把所有代码贴上:
click_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.lqy.testagain.view.MyRelativeLayout
android:id="@+id/main_layout"
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_centerInParent="true"
android:background="@color/abc_search_url_text_normal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="parent"
/>
<TextView
android:id="@+id/tx"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true"
android:background="@android:color/black"
android:gravity="center"
android:text="child"
android:textColor="@android:color/white"
android:textSize="16sp" />
</com.example.lqy.testagain.view.MyRelativeLayout>
</RelativeLayout>
ClickTestActivity .java
public class ClickTestActivity extends Activity{
public static final String TAG = "ClickTestActivity";
MyRelativeLayout mainlayout;
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.click_layout);
initView();
}
private void initView() {
mainlayout = (MyRelativeLayout) findViewById(R.id.main_layout);
textView = (TextView) findViewById(R.id.tx);
mainlayout.setLongClickListener(new MyRelativeLayout.LongClickListener() {
@Override
public void OnLongClick() {
Log.d(TAG, "parent long click");
}
});
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "child click");
}
});
}
}