Android 擴大 View 的點擊地區

來源:互聯網
上載者:User

標籤:處理   關係   group   move   actor   amp   自訂   結果   center   

有時候,按照視覺圖做出來效果後,發現點擊地區過小,不好點擊,使用者體驗肯定不好。擴大視圖,就會導致整個視覺圖變得不好看。那麼有沒有什麼辦法在不改變視圖大小的前提下擴大點擊地區呢?

答案是有!

能夠解決這個問題的前提你要對 View 的事件分發機制有一定的瞭解。

下面我將簡單介紹一下View 的事件分發機制,方便大家理解後面的解決辦法。

為了更清楚的說明整個機制,採用如下的視圖來說明點擊的事件分發機制。是一個 FrameLayout (ViewGroup) 裡麵包含著一個 ImageView (View)。

先自訂一個 MyFrameLayout,繼承FrameLayout,並實現兩個點擊相關的介面;具體代碼如下:

public class MyFrameLayout extends FrameLayout implements OnClickListener, OnTouchListener {    private static final String TAG = "Event";    public MyFrameLayout(Context context, AttributeSet attrs) {        super(context, attrs);        Log.d(TAG, "MyFrameLayout init");        setOnClickListener(this);        setOnTouchListener(this);    }    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        Log.d(TAG, "MyFrameLayout dispatchTouchEvent " + event.getAction());        return super.dispatchTouchEvent(event);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.d(TAG, "MyFrameLayout onTouchEvent " + event.getAction() );        return super.onTouchEvent(event);    }    @Override    public void onClick(View view) {        Log.d(TAG, "MyFrameLayout onClick");    }    @Override    public boolean onTouch(View view, MotionEvent event) {        Log.d(TAG, "MyFrameLayout onTouch " + event.getAction());        return true;    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        Log.d(TAG, "MyFrameLayout onInterceptTouchEvent " + ev.getAction());        return super.onInterceptTouchEvent(ev);    }}

接著,對於 ImageView 也做類似的操作,具體代碼如下:

public class MyImageView extends ImageView implements OnClickListener, OnTouchListener {    private static final String TAG = "Event";    public MyImageView(Context context, AttributeSet attrs) {        super(context, attrs);        Log.d(TAG, "MyImageView init");        setOnClickListener(this);        setOnTouchListener(this);    }    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        Log.d(TAG, "MyImageView dispatchTouchEvent  "+ event.getAction());        return super.dispatchTouchEvent(event);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.d(TAG, "MyImageView onTouchEvent "+ event.getAction());        return super.onTouchEvent(event);    }    @Override    public boolean onTouch(View arg0, MotionEvent arg1) {        Log.d(TAG, "MyImageView onTouch " + arg1.getAction());        return false;    }    @Override    public void onClick(View arg0) {        Log.d(TAG, "MyImageView onClick");    }}

這裡要說明的是,只有ViewGroup才有 onInterceptTouchEvent 方法的,普通的 View 是沒有的,它是不能對事件進行攔截的。

那這時候,如果我們點擊裡面的 ImageView,會有怎樣的輸出呢?結果如。

那如果點擊外層呢?

0,1,2分別是代表 ACTION_DOWN,ACTION_UP,ACTION_MOVE;從中也可以看出一個點擊動作包含一個Down,一個Up,還有多個Move操作。

再來看一段源碼:

public boolean dispatchTouchEvent(MotionEvent ev){    boolean consume = false;    if(onInterceptTouchEvent(ev)){        consume = onTouchEvent(ev);    } else{        consume = child.dispatchTouchEvent(ev);    }    return consume;}

上述的代碼把三者的關係說得很清楚了,對於一個對於一個 ViewGroup 來說,點擊事件產生後,首先會傳遞給它,這時候會調用 dispatchTouchEvent,如果這個 ViewGroup 的 onInterceptTouchEvent 返回 true ,則表示它要攔截該事件,也就會交給它的 onTouchEvent 來進行處理。如果這個 ViewGroup 的 onInterceptTouchEvent 返回 false 則會傳給子項目,子項目的 dispatchTouchEvent 就會被調用,如此反覆迴圈。這與上面一張圖打出的結果是一致的。

這裡還有說明的是,如果代碼設定了 OnTouchListener,那麼就會先調用 onTouch 方法,然後在調用 onTouchEvent。OnClickListener 是優先順序最低的,所以最後才會調用 onClick。

因此,從第二張結果圖也可以看出,當存在 onTouch 之後,onTouchEvent 和 onClick 兩個方法都不會在調用了。

相信到這裡,大家對於View的事件分發機制有一定的瞭解了。

這裡回到開頭提的那個問題,那麼有什麼辦法可以擴大 View 的點擊地區呢?

答案:在父 View 設定 OnTouchListener 對點擊事件進行攔截,通過判斷點擊的位置,來決定是相應子 View 的事件,還是父 View 的事件。

具體實現代碼如下:

public class TouchFactory {    /** 擴充垂直方向點擊地區尺寸 */    private static final int EXT_V_SIZE = 200;    public static View.OnTouchListener creatTouchListener(){        return new View.OnTouchListener() {            @Override            public boolean onTouch(View v, MotionEvent event) {                if (expendTouchSize(v, event)) {                    return true;                }                return false;            }        };    }    public static boolean expendTouchSize(View root, MotionEvent event) {        if (root instanceof MyFrameLayout) {            ImageView view = ((MyFrameLayout) root).getMyImageView();            if (view != null && view.getVisibility() == View.VISIBLE) {                Rect touchRect = new Rect();                view.getGlobalVisibleRect(touchRect);                int action = event.getAction();                float x = event.getRawX();                float y = event.getRawY();                if ((y >= touchRect.top - EXT_V_SIZE) && (y <= touchRect.bottom + EXT_V_SIZE)) {                    if (x >= touchRect.left) {                        if (action == MotionEvent.ACTION_UP) {                            Toast.makeText(view.getContext(), "touch", Toast.LENGTH_SHORT).show();                        }                        return true;                    }                }            }        }        return false;    }}

TouchFactory 對點擊事件進行了封裝,並通過對點擊地區的判斷,來決定要不要攔截點擊事件。

下面是 MyFrameLayout 的具體實現。由於是一個自訂 view, 因此,變數 myImageView 是一定為空白的,所以要對其進行賦值。

public class MyFrameLayout extends FrameLayout {    private static final String TAG = "Event";    private MyImageView myImageView;    public MyFrameLayout(Context context) {        this(context, null);    }    public MyFrameLayout(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public MyFrameLayout(Context context, AttributeSet attrs, int def) {        super(context, attrs, def);        init();    }    public void init() {        this.setOnTouchListener(TouchFactory.creatTouchListener());    }        public MyImageView getMyImageView() {        if (myImageView == null) {            myImageView = findViewById(R.id.mImage);        }        return myImageView;    }}

注意事項:當對子 View 設定 OnClickListener,點擊地區剛好是子 View 內部的時候,就會消耗此事見,父 View 的攔截處理就無效了,因此,一旦選擇攔截來擴大點擊地區,就不要再去子 View 設定點擊回調來消耗點擊事件了。

Android 擴大 View 的點擊地區

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.