您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關(guān)Android項(xiàng)目中實(shí)現(xiàn)滑動(dòng)功能的方法有哪些,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。
一、Android坐標(biāo)體系
在講解滑動(dòng)之前,我們有必要簡(jiǎn)單提一下Android的坐標(biāo)體系,因?yàn)榛瑒?dòng)的實(shí)質(zhì)就是坐標(biāo)的不斷改變,所以我們先來(lái)了解一下Android坐標(biāo)系和視圖坐標(biāo)系兩個(gè)概念。直接放上兩張圖片吧,一目了然。
Android坐標(biāo)系
視圖坐標(biāo)系
從上面的兩張圖可以看出,Android坐標(biāo)系的坐標(biāo)原點(diǎn)位于屏幕的左上角,而視圖坐標(biāo)系的原點(diǎn)位于父視圖的左上角,既然提供了兩種不同的坐標(biāo)系,那么我們?nèi)绾蝸?lái)獲取坐標(biāo)呢,Android已經(jīng)給我們提供了一些方法用于獲取這些坐標(biāo),看下面的圖便一目了然。
Android獲取坐標(biāo)的各種方法
二、layout方法
在View進(jìn)行繪制時(shí),是調(diào)用onLayout()方法來(lái)確定View的位置的,同樣我們也可以調(diào)用layout()方法來(lái)傳入我們滑動(dòng)后的坐標(biāo)便可以實(shí)現(xiàn)View的滑動(dòng),當(dāng)然坐標(biāo)的獲取我們可以在觸控事件中進(jìn)行獲取,下面我們做一個(gè)View隨手指進(jìn)行滑動(dòng)的小例子來(lái)進(jìn)行說(shuō)明。
public class DragView extends View { private int mLastX; private int mLastY; public DragView(Context context) { this(context, null); } public DragView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DragView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onTouchEvent(MotionEvent event) { int x = (int) event.getX(); int y = (int) event.getY(); int lastX = 0, lastY = 0; switch (event.getAction()){ case MotionEvent.ACTION_DOWN: mLastX = x; mLastY = y; break; case MotionEvent.ACTION_MOVE: int offsetX = x - mLastX; int offsetY = y - mLastY; layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY); break; } return true; } }
上面我們?cè)谟|控事件中獲取到獲取到手指按下時(shí)的坐標(biāo)(lastX, lastY),然后在手指移動(dòng)時(shí)不斷計(jì)算X和Y方向上的偏移量,然后再調(diào)用layout()方法來(lái)改變View的位置從而實(shí)現(xiàn)滑動(dòng)。當(dāng)然上面我們是通過(guò)getX()和getY()來(lái)獲取視圖坐標(biāo)來(lái)進(jìn)行修改,我們也可以通過(guò)getRawX()和getRawY()來(lái)獲取絕對(duì)坐標(biāo)來(lái)實(shí)現(xiàn)上面的效果。代碼如下:
private int mLastX; private int mLastY; @Override public boolean onTouchEvent(MotionEvent event) { int x = (int) event.getRawX(); int y = (int) event.getRawY(); switch (event.getAction()){ case MotionEvent.ACTION_DOWN: mLastX = x; mLastY = y; break; case MotionEvent.ACTION_MOVE: int offsetX = x - mLastX; int offsetY = y - mLastY; layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY); //重新設(shè)置初始坐標(biāo) mLastX = x; mLastY = y; break; } return true; }
上面一定要注意,我們?cè)诟淖兺闢iew的位置后必須調(diào)用設(shè)置初始坐標(biāo),這樣才能準(zhǔn)確獲取偏移量。
三、offsetLeftAndRight和offsetTopAndBottom
這一種方法和上一種方法大部分步驟都是相同的,只是在移動(dòng)View上有所差別,代碼如下:
offsetLeftAndRight(offsetX); offsetTopAndBottom(offsetY);
上面的這種方法只是多了一層封裝,可以實(shí)現(xiàn)比上面實(shí)現(xiàn)同樣的效果。
四、設(shè)置LayoutParams
LayoutParams可以通過(guò)改變的布局參數(shù),我們可以通過(guò)下面的代碼實(shí)現(xiàn)上面同樣的效果。
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams(); layoutParams.leftMargin = getLeft() + offsetX; layoutParams.topMargin = getTop() + offsetY; setLayoutParams(layoutParams);
注意:我們的LayoutParams
可以通過(guò)getLayoutParams()
方法來(lái)獲取,但是要注意,如果View的父布局是LinearLayout
,那么我們的LayoutParams
就是LinearLayout.LayoutParams
,如果View的父布局是RelativeLayout
,則我們的LayoutParams
就是RelativeLayout.LayoutParams
。當(dāng)然我們還有一種簡(jiǎn)單的方法,不用再管父布局的布局方式。代碼如下:
ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams(); marginLayoutParams.leftMargin = getLeft() + offsetX; marginLayoutParams.topMargin = getTop() + offsetY; setLayoutParams(marginLayoutParams);
上面的這種方法不用管父布局的類型,使用起來(lái)更加方便。
五、scrollTo和scrollBy方法
關(guān)于這兩個(gè)方法我們需要仔細(xì)說(shuō)一下其中的一些注意事項(xiàng)
1 . scrollTo的參數(shù)是具體的一個(gè)坐標(biāo)點(diǎn)(x, y), 而scrollBy的參數(shù)是在x, y方向上的坐標(biāo)偏移
2 . scrollTo和scrollBy移動(dòng)的是View的內(nèi)容。這一點(diǎn)很重要!!!!
如果我們對(duì)ViewGroup使用scrollTo和scrollBy則移動(dòng)的是內(nèi)部的所有子View, 如果對(duì)TextView使用scrollTo和scrollBy則移動(dòng)的是其中額文本。
3 . 視圖移動(dòng)還有一個(gè)不太好理解的地方在于坐標(biāo),我們下面結(jié)合圖片來(lái)說(shuō)明一下:
視圖移動(dòng)1
視圖移動(dòng)2
我們可以這樣理解,我們的手機(jī)屏幕作為一個(gè)蓋板,在手機(jī)屏幕下面是一個(gè)巨大的畫布,我們的手機(jī)屏幕這個(gè)蓋板是透明的,導(dǎo)致只有和手機(jī)屏幕重合的畫布部分才會(huì)被我們看到,我們調(diào)用scrollTo
和scrollBy
也可以理解為是在移動(dòng)手機(jī)上面的蓋板。如圖中所示,按鈕在ViewGroup
中的坐標(biāo)是(20, 10)
,當(dāng)我們調(diào)用scrollBy(20, 10)
之后,就相當(dāng)于移動(dòng)了屏幕上的蓋板,然后我們看到的按鈕就到了ViewGroup
的左上角。這樣如果我們想讓按鈕在水平和豎直方向上各移動(dòng)20
和10
個(gè)單位,我們就必須調(diào)用scrollBy(-20, -10)
經(jīng)過(guò)了上面的知識(shí)準(zhǔn)備,我們這里也使用scrollBy來(lái)實(shí)現(xiàn)前面實(shí)現(xiàn)的那個(gè)View隨手指移動(dòng)的小例子:
((View)getParent()).scrollBy(-offsetX, -offsetY);
六、使用Scroller
Scroller也是滑動(dòng)中很重要的一個(gè)角色,進(jìn)過(guò)前面的scrollTo和scrollBy大家也會(huì)發(fā)現(xiàn),它們的移動(dòng)時(shí)瞬間完成的,滑動(dòng)顯得十分突兀,Google為了改善用戶體驗(yàn),便給出了Scroller,它可以實(shí)現(xiàn)平滑的移動(dòng),從而使滑動(dòng)過(guò)程更加真實(shí),用戶體驗(yàn)更好,下面我們先簡(jiǎn)單說(shuō)說(shuō)Scroller的實(shí)現(xiàn)原理。
Scroller
也是滑動(dòng)中很重要的一個(gè)角色,進(jìn)過(guò)前面的scrollTo
和scrollBy
大家也會(huì)發(fā)現(xiàn),它們的移動(dòng)時(shí)瞬間完成的,滑動(dòng)顯得十分突兀,Google為了改善用戶體驗(yàn),便給出了Scroller
,它可以實(shí)現(xiàn)平滑的移動(dòng),從而使滑動(dòng)過(guò)程更加真實(shí),用戶體驗(yàn)更好,下面我們先簡(jiǎn)單說(shuō)說(shuō)Scroller
的實(shí)現(xiàn)原理。
Scroller
的實(shí)現(xiàn)方式類似于scrollTo
和scrollBy
,scrollTo
和scrollBy
的移動(dòng)都是從一個(gè)坐標(biāo)點(diǎn)瞬間移動(dòng)到另一個(gè)左邊點(diǎn),而Scroller
則是將移動(dòng)的這段距離切分成好幾段的微小的位移,然后每一段調(diào)用scrollTo
來(lái)不斷移動(dòng)這些微小的位移,由于人眼的視覺(jué)暫留效果,就會(huì)給人平滑移動(dòng)的視覺(jué)效果。
下面我們?cè)谏弦徊降幕A(chǔ)上增加一個(gè)小功能,第一部分還是View隨手指移動(dòng),但是當(dāng)我們松開(kāi)手指時(shí),讓View自己平滑移動(dòng)到最初始的位置(屏幕左上角),下面我們就來(lái)一步步介紹Scroller
的用法
1 . 聲明Scroller變量,并在構(gòu)造方法中進(jìn)行初始化
2 . 在觸控事件的ACTION_UP(手指抬起)事件中傳入開(kāi)始滑動(dòng)的坐標(biāo)和需要滑動(dòng)的距離并觸發(fā)Scroller的滑動(dòng)事件
3 . 重寫computeScroll(),實(shí)現(xiàn)真正的滑動(dòng)
下面是完整的代碼示例:
public class DragView extends View { private int mLastX; private int mLastY; //聲明Scroller變量 private Scroller mScroller; public DragView(Context context) { this(context, null); } public DragView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DragView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //在構(gòu)造方法中初始化Scroller變量 mScroller = new Scroller(context); } @Override public boolean onTouchEvent(MotionEvent event) { int x = (int) event.getRawX(); int y = (int) event.getRawY(); switch (event.getAction()){ case MotionEvent.ACTION_DOWN: mLastX = x; mLastY = y; break; case MotionEvent.ACTION_MOVE: int offsetX = x - mLastX; int offsetY = y - mLastY; //實(shí)現(xiàn)View跟隨手指移動(dòng)的效果 ((View)getParent()).scrollBy(-offsetX, -offsetY); //重新設(shè)置初始坐標(biāo) mLastX = x; mLastY = y; break; case MotionEvent.ACTION_UP: //當(dāng)手指抬起時(shí)執(zhí)行滑動(dòng)過(guò)程 View view = (View) getParent(); mScroller.startScroll(view.getScrollX(), view.getScrollY(), view.getScrollX(), view.getScrollY(), 5000); //調(diào)用重繪來(lái)間接調(diào)用computeScroll()方法 invalidate(); break; } return true; } @Override public void computeScroll() { super.computeScroll(); //判斷滑動(dòng)過(guò)程是否完成 if (mScroller.computeScrollOffset()){ ((View)getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); //通過(guò)重繪來(lái)不斷調(diào)用computeScroll()方法 invalidate(); } } }
上面的代碼View隨手指移動(dòng)的代碼部分是與前面相同的,我們只說(shuō)說(shuō)Scroller的部分以及一些注意事項(xiàng)
1 . startScroll()方法各參數(shù)的意義,我們可以看看下面的源碼:
/** * Start scrolling by providing a starting point, the distance to travel, * and the duration of the scroll. * * @param startX Starting horizontal scroll offset in pixels. Positive * numbers will scroll the content to the left. * @param startY Starting vertical scroll offset in pixels. Positive numbers * will scroll the content up. * @param dx Horizontal distance to travel. Positive numbers will scroll the * content to the left. * @param dy Vertical distance to travel. Positive numbers will scroll the * content up. * @param duration Duration of the scroll in milliseconds. */ public void startScroll(int startX, int startY, int dx, int dy, int duration)
可以看出startX
和startY
參數(shù)就是開(kāi)始滾動(dòng)的(x, y)
坐標(biāo),那么我們就可以通過(guò)ViewGroup(子View的父視圖)
的getScrollX()
和getScrollY()
來(lái)獲取,這里一定要注意,我們?cè)诨瑒?dòng)時(shí)的content
就是子View
,所以我們通過(guò)子View的父視圖(ViewGroup)的getScrollX()
和getScrollY()
獲取到的就是子View在X和Y方向上滑動(dòng)的距離,即就是我們需要的當(dāng)我們手指抬起時(shí)子View的(x, y)坐標(biāo)。而如果我們對(duì)子View調(diào)用getScrollX()
和getScrollY()
方法,則獲得的是子View內(nèi)部的視圖的滑動(dòng)距離及坐標(biāo)。
dx
和dy
分別是在X和Y方向上的偏移量,而且注釋中說(shuō)了,如果我們傳入的dx
和dy
的值是正值,那么將會(huì)向上向左移動(dòng)這個(gè)content(其實(shí)就是我們這里的View)
,即我們就可以讓子View回到左上角,這里我們還是可以借助于上一小節(jié)中提到的視圖移動(dòng)的概念,我們想讓子View向坐上方移動(dòng),其實(shí)就是想讓覆蓋在上面的蓋板向右下角移動(dòng),我們可以將dx
和dy
理解為父視圖(覆蓋在上面的蓋板)的偏移量。
假設(shè)我們剛開(kāi)始是讓子View隨手指向右下方移動(dòng),那么相當(dāng)于覆蓋在上面的蓋板是向左上方移動(dòng),所以我們通過(guò)getScrollX()
和getScrollY()
獲得的值是負(fù)值,我們現(xiàn)在松開(kāi)手指想讓子View向左上方移動(dòng)(即回到屏幕左上角),那么就相當(dāng)于蓋板向右下角移動(dòng),所以我們的dx
和dy
的值必須是-getScrollX()
和-getScrollY()
,此時(shí)的兩個(gè)值都是正值。
2 . 由于我們的computeScroll()
方法不會(huì)主動(dòng)調(diào)用,但是我們又需要它不斷調(diào)用從而不斷進(jìn)行微小移動(dòng)從而實(shí)現(xiàn)平滑的滑動(dòng),所以我們可以通過(guò)下面的方法。
這三個(gè)按照以下順序進(jìn)行調(diào)用 invalidate()
--->onDraw()
--->computeScroll()
,所以我們可以可以在ACTION_UP
中調(diào)用完startScroll()
方法后調(diào)用invalidate()
方法,然后在computeScroll()
方法中判斷滑動(dòng)是否結(jié)束,如果沒(méi)結(jié)束,則通過(guò)getCurrX()
和getCurrY()
來(lái)獲得當(dāng)前需要移動(dòng)的微小的位移的坐標(biāo)點(diǎn),然后傳入scrollTo()
方法中,這時(shí)候子View還只是移動(dòng)了一小段距離,然后我們?cè)俅握{(diào)用invalidate()
方法,然后接著調(diào)用onDraw()
方法,然后再次進(jìn)入computeScroll()
中再次讓子View移動(dòng)一小段距離,直到滑動(dòng)結(jié)束,computeScrollOffset()
返回false
,則這個(gè)循環(huán)調(diào)用的過(guò)程結(jié)束,從而完成平滑移動(dòng)的過(guò)程。
以上就是Android項(xiàng)目中實(shí)現(xiàn)滑動(dòng)功能的方法有哪些,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。