亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android實(shí)現(xiàn)擴(kuò)大View點(diǎn)擊區(qū)域的三種方式

 更新時(shí)間:2024年08月15日 08:27:58   作者:_小馬快跑_  
在 Android 應(yīng)用開(kāi)發(fā)中,有時(shí)候需要擴(kuò)大 View 的點(diǎn)擊區(qū)域以提高用戶交互的便利性,尤其是當(dāng)視圖元素較小或用戶界面密集時(shí),以下提供幾種擴(kuò)大點(diǎn)擊區(qū)域的思路,感興趣的小伙伴跟著小編一起來(lái)看看吧

在 Android 應(yīng)用開(kāi)發(fā)中,有時(shí)候需要擴(kuò)大 View 的點(diǎn)擊區(qū)域以提高用戶交互的便利性,尤其是當(dāng)視圖元素較小或用戶界面密集時(shí)。擴(kuò)大點(diǎn)擊區(qū)域可以讓用戶更容易點(diǎn)擊目標(biāo),改善用戶體驗(yàn)。以下提供幾種擴(kuò)大點(diǎn)擊區(qū)域的思路。

方式一:增加padding

通過(guò)設(shè)置padding來(lái)增大點(diǎn)擊區(qū)域,如:

<TextView
        android:id="@+id/tv_view_delegate2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/gray_holo_light"
        android:padding="20dp"
        android:text="TouchDelegate2" />

上面的代碼通過(guò)在XML中設(shè)置padding來(lái)擴(kuò)大點(diǎn)擊區(qū)域,當(dāng)然也可以通過(guò)代碼設(shè)置setPadding來(lái)實(shí)現(xiàn)。雖然設(shè)置padding可以起到效果,但是如果使用不當(dāng)可能會(huì)影響視圖的布局和外觀,比如對(duì)ImageView設(shè)置padding的話可能會(huì)擠壓其形狀,所以使用Padding擴(kuò)大點(diǎn)擊區(qū)域時(shí)需要確保不影響視圖的布局和外觀。

方式二:TouchDelegate

TouchDelegate 類是 Android 中的一個(gè)輔助類,用于擴(kuò)展 View 的觸摸區(qū)域,使其大于實(shí)際的 View 邊界。這對(duì)于增加某些 UI 元素的觸控便捷性非常有用,比如小按鈕。

TouchDelegate 使用示例:

/**
 * 擴(kuò)展方法,擴(kuò)大點(diǎn)擊區(qū)域
 * NOTE: 需要保證目標(biāo)targetView有父View,否則無(wú)法擴(kuò)大點(diǎn)擊區(qū)域
 *
 * @param expandSize 擴(kuò)大的大小,單位px
 */
fun View.expandTouchView(expandSize: Int = 10.dp2px()) {
    val parentView = (parent as? View)
    parentView?.post {
        val rect = Rect()
        getHitRect(rect) //getHitRect(rect)將視圖在父容器中所占據(jù)的區(qū)域存儲(chǔ)到rect中。
        log("rect = $rect")
        rect.left -= expandSize
        rect.top -= expandSize
        rect.right += expandSize
        rect.bottom += expandSize
        log("expandRect = $rect")
        parentView.touchDelegate = TouchDelegate(rect, this)
    }
}

在Activity中使用:

private val tvExpandTouch: TextView by id(R.id.tv_view_delegate)

tvExpandTouch.run {
    expandTouchView(50.dp2px()) //擴(kuò)大點(diǎn)擊區(qū)域
    setOnClickListener { showToast("通過(guò)TouchDelegate擴(kuò)大點(diǎn)擊區(qū)域") }
}

上面就實(shí)現(xiàn)了View擴(kuò)大點(diǎn)擊區(qū)域,繼續(xù)來(lái)看下TouchDelegate 的源碼:

public class TouchDelegate {
    private View mDelegateView; //需要接收觸摸事件的 View,即代理 View。
    private Rect mBounds;//本地坐標(biāo)中的代理 View 的邊界,用于初始命中測(cè)試。
    private Rect mSlopBounds;//增加一定范圍的 mBounds,用于追蹤觸摸事件是否應(yīng)被視為在代理 View 內(nèi)。
    @UnsupportedAppUsage
    private boolean mDelegateTargeted;
    private TouchDelegateInfo mTouchDelegateInfo;

    public TouchDelegate(Rect bounds, View delegateView) {
        mBounds = bounds;
        mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop();
        mSlopBounds = new Rect(bounds);
        mSlopBounds.inset(-mSlop, -mSlop);
        mDelegateView = delegateView;
    }

    //接收并處理觸摸事件。若事件在 mBounds 內(nèi),則會(huì)將其轉(zhuǎn)發(fā)到 mDelegateView。
    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)) {
                        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));
            }
            //NOTE:重點(diǎn)看這里,最終是調(diào)用的代理View去處理事件了。
            handled = mDelegateView.dispatchTouchEvent(event);
        }
        return handled;
    }
}

View.java 源碼中使用 TouchDelegate

   private TouchDelegate mTouchDelegate = null;
    
   public void setTouchDelegate(TouchDelegate delegate) {
       mTouchDelegate = delegate;
   }

   public TouchDelegate getTouchDelegate() {
       return mTouchDelegate;
   }

   public boolean onTouchEvent(MotionEvent event) {
      //......
      if (mTouchDelegate != null) {
          if (mTouchDelegate.onTouchEvent(event)) {
              return true;
          }
      }
      switch (action) {
          case MotionEvent.ACTION_DOWN:
          //...省略...
          case MotionEvent.ACTION_CANCEL:
          case MotionEvent.ACTION_UP:
      }
    }     

可以看到在onTouchEvent中,優(yōu)先去判斷是否有TouchDelegate,如果有的話會(huì)先去找對(duì)應(yīng)的代理View去處理事件。使用TouchDelegate的注意事項(xiàng):

  • 目標(biāo)View必須有父View
  • 給多個(gè)目標(biāo)View擴(kuò)大點(diǎn)擊區(qū)域時(shí),不能是同一個(gè)父View,從View類的源碼中可以看到,設(shè)置setTouchDelegate時(shí),會(huì)把之前的覆蓋掉。

方式三:RectF & getLocationOnScreen

RectF 是一個(gè)用于表示浮點(diǎn)坐標(biāo)的矩形區(qū)域的類,而 getLocationOnScreen 則用于獲取視圖在整個(gè)屏幕中的絕對(duì)坐標(biāo)。結(jié)合兩者,可以檢查觸摸事件是否在子視圖的“擴(kuò)展區(qū)域”內(nèi),然后執(zhí)行相應(yīng)的操作。代碼示例:

class ParentInnerTouchView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {

    private val tvChildView: TextView

    init {
        inflate(context, R.layout.expand_touch_view, this)
        tvChildView = findViewById(R.id.tv_expand_view)
        tvChildView.setOnClickListener { showToast("擴(kuò)大了點(diǎn)擊事件") }
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        event?.let { ev ->
            if (ev.action == MotionEvent.ACTION_DOWN) {
                val isChildHit = isHitExpandChildView(tvChildView, Pair(ev.rawX, ev.rawY))
                if (isChildHit) {
                    //將事件傳遞給子控件
                    tvChildView.performClick()
                }
            }
        }
        return super.onTouchEvent(event)
    }

    /**
     * 判斷是否點(diǎn)擊到了子 View 的擴(kuò)大區(qū)域
     * @param childView 子 View
     * @param touchPair 點(diǎn)擊的位置 (x, y)
     * @param expandSize 擴(kuò)大區(qū)域的大小
     * @return 是否命中
     */
    private fun isHitExpandChildView(
        childView: View,
        touchPair: Pair<Float, Float>,
        expandSize: Int = 50.dp2px()
    ): Boolean {

        // 獲取子 View 在屏幕上的位置
        val location = IntArray(2)
        childView.getLocationOnScreen(location)

        val childX = location[0].toFloat()
        val childY = location[1].toFloat()
        val touchX = touchPair.first
        val touchY = touchPair.second

        // 擴(kuò)大點(diǎn)擊區(qū)域
        val rect = RectF()
        rect.set(
            childX - expandSize,
            childY - expandSize,
            childX + childView.width + expandSize,
            childY + childView.height + expandSize
        )
        // 判斷點(diǎn)擊是否在擴(kuò)大的子 View 區(qū)域內(nèi)
        return rect.contains(touchX, touchY)
    }
}
  • getLocationOnScreen: 用于獲取子視圖在屏幕上的絕對(duì)坐標(biāo),返回一個(gè)包含 x 和 y 坐標(biāo)的數(shù)組。利用這些坐標(biāo)計(jì)算出子視圖在屏幕上的位置。
  • RectF: 創(chuàng)建一個(gè)矩形區(qū)域,通過(guò)調(diào)用 set 方法擴(kuò)展矩形的上下左右邊界,從而擴(kuò)大點(diǎn)擊區(qū)域。
  • onTouchEvent: 監(jiān)聽(tīng)觸摸事件,如果點(diǎn)擊位置在擴(kuò)大的區(qū)域內(nèi),則調(diào)用 performClick 觸發(fā)子視圖的點(diǎn)擊事件。

以上就是Android實(shí)現(xiàn)擴(kuò)大View點(diǎn)擊區(qū)域的三種方式的詳細(xì)內(nèi)容,更多關(guān)于Android View點(diǎn)擊區(qū)域的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android開(kāi)發(fā)之圖片切割工具類定義與用法示例

    Android開(kāi)發(fā)之圖片切割工具類定義與用法示例

    這篇文章主要介紹了Android開(kāi)發(fā)之圖片切割工具類定義與用法,結(jié)合實(shí)例形式分析了Android圖片切割工具類的定義與簡(jiǎn)單使用方法,需要的朋友可以參考下
    2017-11-11
  • Android Studio 3.0中mipmap-anydpi-v26是什么東東

    Android Studio 3.0中mipmap-anydpi-v26是什么東東

    在Android Studio 3.0中一旦我們創(chuàng)建了一個(gè)項(xiàng)目,一個(gè)名為mipmap-anydpi-v26自動(dòng)創(chuàng)建的文件夾在res文件夾下。它究竟能干什么?為什么我們需要這個(gè)?我們?cè)陂_(kāi)發(fā)時(shí)該如何利用它,下面通過(guò)本文給大家介紹下
    2017-12-12
  • Android基礎(chǔ)之獲取LinearLayout的寬高

    Android基礎(chǔ)之獲取LinearLayout的寬高

    LinearLayout是線性布局控件,它包含的子控件將以橫向或豎向的方式排列,按照相對(duì)位置來(lái)排列所有的widgets或者其他的containers,超過(guò)邊界時(shí),某些控件將缺失或消失。有的時(shí)候,我們需要想獲取LinearLayout寬高,下面通過(guò)這篇文章來(lái)跟著小編一起學(xué)習(xí)學(xué)習(xí)吧。
    2016-11-11
  • Android中ImageView實(shí)現(xiàn)選擇本地圖片并顯示功能

    Android中ImageView實(shí)現(xiàn)選擇本地圖片并顯示功能

    本文主要介紹了android中ImageView實(shí)現(xiàn)選擇本地圖片并顯示功能的示例代碼。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧
    2017-04-04
  • Android中FileProvider的各種場(chǎng)景應(yīng)用詳解

    Android中FileProvider的各種場(chǎng)景應(yīng)用詳解

    這篇文章主要為大家介紹了Android中FileProvider的各種場(chǎng)景應(yīng)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • Android 暫停和恢復(fù)Activity

    Android 暫停和恢復(fù)Activity

    在正常的應(yīng)用程序使用,前臺(tái)activity有時(shí)會(huì)被其他可視化組件遮擋,從而 造成activity的暫停。例如,當(dāng)一個(gè)半透明的activity打開(kāi)時(shí)(如在一個(gè)風(fēng)格對(duì)話框),以前的activity就暫停了。只要 activity仍然是部分可見(jiàn),但目前沒(méi)有獲得焦點(diǎn),它就依然處于暫停狀態(tài)
    2016-03-03
  • Android中g(shù)oogle Zxing實(shí)現(xiàn)二維碼與條形碼掃描

    Android中g(shù)oogle Zxing實(shí)現(xiàn)二維碼與條形碼掃描

    這篇文章主要介紹了Android中g(shù)oogle Zxing實(shí)現(xiàn)二維碼與條形碼掃描的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Android開(kāi)發(fā)中使用Intent打開(kāi)第三方應(yīng)用及驗(yàn)證可用性的方法詳解

    Android開(kāi)發(fā)中使用Intent打開(kāi)第三方應(yīng)用及驗(yàn)證可用性的方法詳解

    這篇文章主要介紹了Android開(kāi)發(fā)中使用Intent打開(kāi)第三方應(yīng)用及驗(yàn)證可用性的方法,結(jié)合實(shí)例形式分析了Android使用Intent打開(kāi)第三方應(yīng)用的三種常用方式及使用注意事項(xiàng),需要的朋友可以參考下
    2017-11-11
  • Android GridView實(shí)現(xiàn)滾動(dòng)到指定位置的方法

    Android GridView實(shí)現(xiàn)滾動(dòng)到指定位置的方法

    這篇文章主要介紹了Android GridView實(shí)現(xiàn)滾動(dòng)到指定位置的方法,本文介紹了4個(gè)相關(guān)的方法,分別對(duì)它們做了講解,需要的朋友可以參考下
    2015-06-06
  • 解決EditText編輯時(shí)hint 在6.0 手機(jī)上顯示不出來(lái)的問(wèn)題

    解決EditText編輯時(shí)hint 在6.0 手機(jī)上顯示不出來(lái)的問(wèn)題

    下面小編就為大家?guī)?lái)一篇解決EditText編輯時(shí)hint 在6.0 手機(jī)上顯示不出來(lái)的問(wèn)題。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05

最新評(píng)論