Space+onTouchEvent=Hitarea:像Flash那樣在Android中擴(kuò)大點擊區(qū)域

1 思考

我是一個資深的Flash迷,小學(xué)開始學(xué)習(xí)做動畫,本科時寫下了第一行ActionScript 3代碼。在Android開發(fā)中經(jīng)常遇到點擊區(qū)域太小的問題,我思考著能不能像ActionScirpt 3那樣給一個Sprite設(shè)置一個hitArea Sprite,所有在hitArea Sprite上的鼠標(biāo)事件都會被傳遞到Sprite上。

2 TouchDelegate

Android中可以通過對View的parent設(shè)置TouchDelegate來實現(xiàn)這個功能,但是必須在代碼中實現(xiàn),且一個Parent最多只能有一個TouchDelegate,非常不方便,靈活性也有欠缺。

3 Space + onTouchEvent

Android 4.0引入了Space作為省略繪圖功能的空白區(qū)域,它將自身設(shè)置為INVISIBLE,并且重寫了View的draw函數(shù),所以Space比較輕量級,代碼如下:

public final class Space extends View {

    public Space(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        if (getVisibility() == VISIBLE) {
            setVisibility(INVISIBLE);
        }
    }

    /**
     * Draw nothing.
     *
     * @param canvas an unused parameter.
     */
    @Override
    public void draw(Canvas canvas) {
    }
    ...
}

在這個基礎(chǔ)上,我們重寫Space的onTouchEvent函數(shù),同時為了接收到TouchEvent事件,把View設(shè)置為VISIBLE,在接收到的事件時做一下坐標(biāo)轉(zhuǎn)化并傳遞給目標(biāo)View,這樣就誕生了Hitarea。

4 如何做坐標(biāo)的轉(zhuǎn)化?

我們知道Hitarea measure后的寬高,目標(biāo)View measure后的寬高,利用這個寬高,生成一個scale矩陣,代碼如下:

private void updateTransformMatrix(HitareaDelegate delegate) {
    if ( mTransformMatrix == null ){
        mTransformMatrix = new Matrix();
    }
    View hitarea = delegate.getHitareaView();
    float scaleX = mTargetView.getMeasuredWidth() * 1.0f / hitarea.getMeasuredWidth();
    float scaleY = mTargetView.getMeasuredHeight() * 1.0f / hitarea.getMeasuredHeight();
    mTransformMatrix.setScale(scaleX,scaleY);
}

隨后,對于Android4.0及以上的版本,MotionEvent有一個MotionEvent#transform方法可以直接完成坐標(biāo)轉(zhuǎn)換;對于4.0以下版本,用Matrix#mapPoints方法做坐標(biāo)轉(zhuǎn)換,然后用MotionEvent#setLocation設(shè)置新的坐標(biāo)點,代碼如下:

private void transformMotionEvent(MotionEvent event){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        event.transform(mTransformMatrix);
    } else {
        if ( mPointSrc == null ){
            mPointSrc = new float[2];
            mPointDst = new float[2];
        }
        mPointSrc[0] = event.getX();
        mPointSrc[1] = event.getY();
        mTransformMatrix.mapPoints(mPointDst,mPointSrc);
        event.setLocation(mPointDst[0],mPointDst[1]);
    }
}

5 Demo

HitareaPreview.gif

6 用法

6.1 引入庫

compile 'com.asha:hitarealib:0.1'

6.2 Hitarea

目標(biāo)View和Hitarea可以是同級關(guān)系,對Hitarea指定app:targetId=@id/buttonTest即可:

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <Button
        android:id="@+id/buttonTest"
        android:text="list view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <com.asha.Hitarea
        app:debug="true"
        app:targetId="@id/buttonTest"
        android:layout_width="match_parent"
        android:layout_height="50dp" />
</FrameLayout>

也可以是比較深層次的關(guān)系:

<FrameLayout
    android:layout_marginTop="5dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <com.asha.hitarea.DemoView
        android:id="@+id/test"
        android:background="#CCCCCC"
        android:layout_width="200dp"
        android:layout_height="70dp" />
</FrameLayout>
<com.asha.Hitarea
    app:targetId="@id/test"
    app:debug="true"
    android:layout_width="match_parent"
    android:layout_height="120dp" />

6.3 HitareaWrapper

有時候我們只需要對一個button增加點擊區(qū)域,但是父容器是LinaerLayout的情況下非常難把Hitarea區(qū)域與目標(biāo)View重合,這個時候可以使用HitareaWrapper。HitareaWrapper繼承自RelativeLayout,布局上靈活自如。HitareaWrapper默認(rèn)會選擇子View中的第一個View作為目標(biāo)View,用法如下:

<com.asha.HitareaWrapper
        app:debug="true"
        android:layout_marginTop="5dp"
        android:layout_width="200dp"
        android:layout_height="200dp">
    <com.asha.hitarea.DemoView
        android:layout_gravity="center"
        android:background="#CCCCCC"
        android:layout_width="100dp"
        android:layout_height="100dp" />
</com.asha.HitareaWrapper>

當(dāng)然也可以在HitareaWrapper中包含多個View,它會尋找所有子View中tag值為@string/tag_hitarea的View作為目標(biāo)View,用法如下:

<com.asha.HitareaWrapper
        app:debug="true"
        android:layout_marginTop="5dp"
        android:layout_width="200dp"
        android:layout_height="200dp">
    <com.asha.hitarea.DemoView
        android:tag="@string/tag_hitarea"
        android:layout_gravity="center"
        android:background="#CCCCCC"
        android:layout_width="100dp"
        android:layout_height="100dp" />
    <Button
        android:text="other"
        android:layout_alignParentRight="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</com.asha.HitareaWrapper>

6.4 Debug Mode

com.asha.Hitareacom.asha.HitareaWrapper都有一個屬性app:debug,如果設(shè)置為true,運行時會繪制出Hitarea區(qū)域,方便調(diào)試。

7 Github

https://github.com/ashqal/Hitarea
這個庫非常簡單,但是希望對大家有所幫助_

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,326評論 25 708
  • 3.4 View詳解 3.10.1 概述 View系統(tǒng)定義了從用戶輸入消息到消息處理的全過程:用戶通過觸摸屏或者鍵...
    jianhuih閱讀 729評論 0 0
  • layout: postdate: 2016-01-08title: Android開發(fā)藝術(shù)探索-第三章-View...
    KuTear閱讀 1,974評論 2 18
  • 今天上班,一個妹妹和我吐槽:昨晚她們部門聚餐,她的領(lǐng)導(dǎo)一直在灌她白酒,把她給氣死了。她一邊罵罵咧咧,一邊又很無奈,...
    言吾小姐閱讀 626評論 0 0
  • 腰痛腿麻二十年,遷延不愈路難行。 輾轉(zhuǎn)求醫(yī)三五家,療效不佳念俱灰。 他人介紹慕名來,手術(shù)治療果不凡。 術(shù)后三年再無...
    徐一村閱讀 464評論 0 5

友情鏈接更多精彩內(nèi)容