使用TouchDelegate扩大控件的点击范围

当我们的控件太小,导致我们无法准确的点击,这时候我们可以在在外面再加一层布局,但这样性能不太好,或者加上padding,但影响这个UI布局。这时候我们可以TouchDelegate扩大控件的点击范围。

这块其实是从之前的Android 事件分发机制(1)-源码分析_if (!canceled && !intercepted) {_沙滩捡贝壳的小孩的博客-CSDN博客

中的View的onTouchEvent中发现的

 public boolean onTouchEvent(MotionEvent event) {
  ...
     if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }
 ....
}

 意思是如果设置了tochDelegate的话,那么它剩下的事件分发就要在其onTouchEvent中事件分发了

原理

然后我们去看看TouchDelegate源码


    /**
     * Constructor
     *
     * @param bounds Bounds in local coordinates of the containing view that should be mapped to
     *        the delegate view
     * @param delegateView The view that should receive motion events
     */
    public TouchDelegate(Rect bounds, View delegateView) {
        mBounds = bounds;//赋值要扩大的rect区域

        mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop();
        mSlopBounds = new Rect(bounds);
        mSlopBounds.inset(-mSlop, -mSlop);
        mDelegateView = delegateView;//子View
    }
  /**
     * Forward touch events to the delegate view if the event is within the bounds
     * specified in the constructor.
     *
     * @param event The touch event to forward
     * @return True if the event was consumed by the delegate, false otherwise.
     */
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        int x = (int)event.getX();
        int y = (int)event.getY();
        boolean sendToDelegate = false;
        boolean hit = true;
        boolean handled = false;

        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mDelegateTargeted = mBounds.contains(x, y);//判断点击的位置是否在传入的范围内
                sendToDelegate = mDelegateTargeted;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
            case MotionEvent.ACTION_POINTER_UP:
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_MOVE:
                sendToDelegate = mDelegateTargeted;
                if (sendToDelegate) {
                    Rect slopBounds = mSlopBounds;
                    if (!slopBounds.contains(x, y)) {//如果手指落下的坐标点在扩大的rect区域内
                        hit = false;
                    }
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                sendToDelegate = mDelegateTargeted;
                mDelegateTargeted = false;
                break;
        }
        if (sendToDelegate) {
            if (hit) {
                // Offset event coordinates to be inside the target view
                event.setLocation(mDelegateView.getWidth() / 2, mDelegateView.getHeight() / 2);
            } else {
                // Offset event coordinates to be outside the target view (in case it does
                // something like tracking pressed state)
                int slop = mSlop;
                event.setLocation(-(slop * 2), -(slop * 2));
            }
            handled = mDelegateView.dispatchTouchEvent(event);//执行的子View的dispatchTouchEvent方法,将事件分发给子View去消费事件
        }
        return handled;
    }

从源码中我们可以清晰的看到,其实它本质就是通过传递进去的Rect范围值,然后检测点击的坐标区域是否在Rect范围里面,如果在的话,就执行View的dispatchTouchEvent方法,将事件进行消费

应用

         <androidx.appcompat.widget.AppCompatButton
                    android:id="@+id/btn_eunsure"
                    android:layout_width="20dp"
                    android:layout_height="40dp"
                    android:text="确认"
                    android:background="@color/green"
                    android:textSize="20sp"
                    android:textColor="@color/black"
                    android:layout_gravity="right"
                    android:layout_marginEnd="30dp"
                    android:layout_marginTop="15dp"
                    android:layout_marginBottom="15dp"/>
   final View parentView = (View) activityMainBinding.btnEunsure.getParent();
        parentView.post(new Runnable() {
            @Override
            public void run() {
                Rect rect = new Rect();
               activityMainBinding.btnEunsure.getHitRect(rect);

                rect.top -= 100;
                rect.left -= 100;
                rect.bottom += 100;
                rect.right += 100;

                parentView.setTouchDelegate(new TouchDelegate(rect, activityMainBinding.btnEunsure));
            }
        });

 这样就行了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值