1. 程式人生 > >android子view點選事件(click)和父view長點選事件(longclick)衝突

android子view點選事件(click)和父view長點選事件(longclick)衝突

工作中想要實現這麼一個效果:
這裡寫圖片描述
如圖中,當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");
            }
        });

    }
}