android子view點選事件(click)和父view長點選事件(longclick)衝突
阿新 • • 發佈:2019-02-07
工作中想要實現這麼一個效果:
如圖中,當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");
}
});
}
}