自定義view基礎(chǔ)知識(shí)

自定義view的基礎(chǔ)知識(shí)主要包括view的位置參數(shù)、MotionEvent和TouchSlop對(duì)象、VelocityTracker、GestureDetector和Scroller對(duì)象。

<h1>一、view的位置參數(shù)</h1>
view的位置主要由它的四個(gè)頂點(diǎn)來(lái)決定,即left、top、right、bottom其中l(wèi)eft是左上角的橫坐標(biāo),top是左上角的縱坐標(biāo),right是右下角的橫坐標(biāo),bottom是右下角的縱坐標(biāo)。這里要注意一下,這些坐標(biāo)都是相對(duì)于view的父容器來(lái)說(shuō)的,因此它是一種相對(duì)的坐標(biāo)。

其它還有幾個(gè)參數(shù)x、y、translationX和translationY,其中x和y是view的左上角的坐標(biāo),而translationX和translationY是view左上角相對(duì)于父容器的偏移量。這里這幾個(gè)參數(shù)也是相對(duì)于父容器的坐標(biāo),并且translationX和translationY的默認(rèn)值是0。

這幾個(gè)參數(shù)的關(guān)系如下:

 x = left + translationX
 y = top + translationY

需要注意的是,view在平移過(guò)程中,left和top表示的是原始的左上角的位置信息,其值不會(huì)發(fā)生改變,此時(shí)發(fā)生改變的是x、y、translationX和translationY這四個(gè)參數(shù)。

<h1>二、MotionEvent和TouchSlop</h1>
<h2>1、MotionEvent</h2>
這里說(shuō)下它的四個(gè)方法getX/getY和getRawX/getRawY。它們的區(qū)別是,getX/getY返回的是相對(duì)于當(dāng)前view左上角的x和y坐標(biāo),而getRawX/getRawY返回的是相對(duì)于屏幕左上角的坐標(biāo)。
<h2>2、TouchSlop</h2>
TouchSlop是系統(tǒng)所能識(shí)別的最小滑動(dòng)距離,即當(dāng)手指在屏幕上滑動(dòng)時(shí),如果兩次滑動(dòng)的之間的距離小于這個(gè)常量,那么系統(tǒng)就不認(rèn)為你在進(jìn)行滑動(dòng)操作。原因很簡(jiǎn)單,滑動(dòng)距離太短,系統(tǒng)不認(rèn)為它在滑動(dòng)。這個(gè)常量跟設(shè)備有關(guān),在不同的設(shè)備上值可能不同??梢酝ㄟ^(guò)如下方式獲取這個(gè)常量:

ViewConfiguration.get(getContext()).getScaledTouchSlop();

<h1>三、VelocityTracker、GestureDetector和Scroller</h1>
<h2>1、VelocityTracker</h2>
VelocityTracker用來(lái)做速度追蹤,用于追蹤手指在滑動(dòng)過(guò)程中的速度,包括水平和豎直方向的速度。它的使用過(guò)程很簡(jiǎn)單,首先在view的onTouchEvent方法中追蹤當(dāng)前單擊事件的速度:

VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);

接著當(dāng)我們先知道當(dāng)前的滑動(dòng)速度時(shí),這個(gè)時(shí)候可以采用如下方式來(lái)獲得當(dāng)前的速度:

velocityTracker.computeCurrentVelocity(1000);
int xVelocity = (int) velocityTracker.getXVelocity();
int yVelocity = (int) velocityTracker.getYVelocity();

這一步中有兩點(diǎn)需要注意,第一點(diǎn)是獲取速度之前必須先計(jì)算速度,即在調(diào)用getXVelocity和getYVelocity這兩個(gè)方法前必須要先調(diào)用computeCurrentVelocity方法;第二點(diǎn),這里的速度是指一段時(shí)間內(nèi)手指滑動(dòng)的像素?cái)?shù),比如將時(shí)間間隔設(shè)為1000ms時(shí),在1s內(nèi),手指在水平方向從左向右滑過(guò)100像素,那么水平速度就是100。注意這里的速度可以為負(fù)數(shù)。當(dāng)手指從右往左滑時(shí)即為負(fù)數(shù)。速度的計(jì)算可以用公式來(lái)表示:

速度 = (終點(diǎn)位置 - 起始位置)/ 時(shí)間段

根據(jù)上面的公式和android的坐標(biāo)系可以知道手指逆著坐標(biāo)系的正方向滑動(dòng),所產(chǎn)生的速度即為負(fù)數(shù)。
另外,computeCurrentVelocity這個(gè)方法的參數(shù)表示的是一個(gè)時(shí)間單元或者說(shuō)時(shí)間間隔,它的單位是毫秒(ms),計(jì)算速度時(shí)得到的速度就是在這個(gè)時(shí)間間隔內(nèi)手指在水平或者豎直方向上所滑動(dòng)的像素?cái)?shù)。針對(duì)上面的例子,如果我們通過(guò)velocityTracker.computeCurrentVelocity(100)來(lái)獲取速度,那么得到的速度就是手指在100ms內(nèi)所滑動(dòng)的像素?cái)?shù),因些水平方向的速度就變成了10像素/100ms(這里假設(shè)滑動(dòng)過(guò)程是勻速的),即水平速度為10,這點(diǎn)需要注意下。
最后,當(dāng)不需要使用它的時(shí)候,需要調(diào)用clear方法來(lái)重置并回收內(nèi)存:

velocityTracker.clear();
velocityTracker.recycle();

<h2>2、GestureDetector</h2>
手勢(shì)檢測(cè),用于輔助檢測(cè)用戶的單擊、滑動(dòng)、長(zhǎng)按、雙擊等行為。要使用GestureDetector也不復(fù)雜,參考如下過(guò)程。
首先,需要?jiǎng)?chuàng)建一個(gè)GestureDetector對(duì)象并實(shí)現(xiàn)OnGestureListener接口,根據(jù)需要我們還可以實(shí)現(xiàn)OnDoubleTapListener從而能夠監(jiān)聽(tīng)雙擊行為:

GestureDetector mGestureDetector  = new GestureDetector(this);
//解決長(zhǎng)按屏幕后無(wú)法拖動(dòng)的現(xiàn)象
mGestureDetector.setIsLongpressEnabled(false);

接著,接管目標(biāo)view的onTouchEvent方法,在待監(jiān)聽(tīng)view的onTouchEvent方法中添加如下實(shí)現(xiàn):

boolean consume = mGestureDetector.onTouchEvent(event);
return consume;

做完了上面兩步,就可以有選擇的實(shí)現(xiàn)OnGestureListener和OnDoubleTapListener中的方法了,這兩個(gè)接口中的方法介紹如下:
OnGestureListener接口:

(1) onDown(MotionEvent e):down事件;
(2) onSingleTapUp(MotionEvent e):一次點(diǎn)擊up事件;
(3) onShowPress(MotionEvent e):down事件發(fā)生而move或則up還沒(méi)發(fā)生前觸發(fā)該事件;
(4) onLongPress(MotionEvent e):長(zhǎng)按事件;
(5) onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY):滑動(dòng)手勢(shì)事件;
(6) onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY):在屏幕上拖動(dòng)事件。

OnDoubleTapListener接口:

(1) onDoubleTap(MotionEvent e):雙擊事件。
(2) onDoubleTapEvent(MotionEvent e):雙擊間隔中還發(fā)生其他的動(dòng)作。通知DoubleTap手勢(shì)中的事件,包含down、up和move事件(這里指的是在雙擊之間發(fā)生的事件,例如在同一個(gè)地方雙擊會(huì)產(chǎn)生DoubleTap手勢(shì),而在DoubleTap手勢(shì)里面還會(huì)發(fā)生down和up事件,這兩個(gè)事件由該函數(shù)通知);
(3) onSingleTapConfirmed(MotionEvent e):?jiǎn)螕羰录?。用?lái)判定該次點(diǎn)擊是SingleTap而不是DoubleTap,如果連續(xù)點(diǎn)擊兩次就是DoubleTap手勢(shì),如果只點(diǎn)擊一次,系統(tǒng)等待一段時(shí)間后沒(méi)有收到第二次點(diǎn)擊則判定該次點(diǎn)擊為SingleTap而不是DoubleTap,然后觸發(fā)SingleTapConfirmed事件。

注意:關(guān)于onSingleTapConfirmed和onSingleTapUp的一點(diǎn)區(qū)別: OnGestureListener有這樣的一個(gè)方法onSingleTapUp,和onSingleTapConfirmed容易混淆。二者的區(qū)別是:onSingleTapUp,只要手抬起就會(huì)執(zhí)行,而對(duì)于onSingleTapConfirmed來(lái)說(shuō),如果雙擊的話,則onSingleTapConfirmed不會(huì)執(zhí)行。

建議:如果只是監(jiān)聽(tīng)滑動(dòng)相關(guān)的,建議自己在onTouchEvent中實(shí)現(xiàn),如果要監(jiān)聽(tīng)雙擊這種行為的話,那么就使用GestureDetector。

<h2>3、Scroller</h2>
彈性滑動(dòng)對(duì)象,用于實(shí)現(xiàn)view的彈性滑動(dòng)。我們知道,當(dāng)使用view的scrollTo/scrollBy方法來(lái)進(jìn)行滑動(dòng)時(shí),其過(guò)程是瞬間完成的,沒(méi)有過(guò)渡效果用戶體驗(yàn)不好。這時(shí)候我們就需要用到Scroller來(lái)實(shí)現(xiàn)有過(guò)渡效果的滑動(dòng),其過(guò)程不是瞬間完成,而是在一定的時(shí)間間隔內(nèi)完成的。Scroller本身無(wú)法讓view彈性滑動(dòng),它需要和view的computeScroll方法配合使用才能共同完成這個(gè)功能。那么如何使用Scroller呢?它的典型代碼是固定的,如下所示。

    Scroller scroller = new Scroller(getContext());

    public void smoothScrollTo() {
        int scrollX = getScrollX();
//        int delta = destX - scrollX;
        // 1000ms 內(nèi)滑向destX,效果就是慢慢滑動(dòng)
        scroller.startScroll(scrollX, 0, -300, -200, 3000);
    }

    @Override
    public void computeScroll() {
        if(scroller.computeScrollOffset()){
            scrollTo(scroller.getCurrX(), scroller.getCurrY());
            postInvalidate();
        }
    }

完結(jié)。。。

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

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

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