編輯:關於Android編程
在Android中想要實現實現滑動有很多方法,這篇博客將提供一些實現滑動的思路,希望可以幫助到有需要的人。
一、Android坐標體系
在講解滑動之前,我們有必要簡單提一下Android的坐標體系,因為滑動的實質就是坐標的不斷改變,所以我們先來了解一下Android坐標系和視圖坐標系兩個概念。直接放上兩張圖片吧,一目了然。
Android坐標系
視圖坐標系
從上面的兩張圖可以看出,Android坐標系的坐標原點位於屏幕的左上角,而視圖坐標系的原點位於父視圖的左上角,既然提供了兩種不同的坐標系,那麼我們如何來獲取坐標呢,Android已經給我們提供了一些方法用於獲取這些坐標,看下面的圖便一目了然。
Android獲取坐標的各種方法
二、layout方法
在View進行繪制時,是調用onLayout()方法來確定View的位置的,同樣我們也可以調用layout()方法來傳入我們滑動後的坐標便可以實現View的滑動,當然坐標的獲取我們可以在觸控事件中進行獲取,下面我們做一個View隨手指進行滑動的小例子來進行說明。
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; } }
上面我們在觸控事件中獲取到獲取到手指按下時的坐標(lastX, lastY),然後在手指移動時不斷計算X和Y方向上的偏移量,然後再調用layout()方法來改變View的位置從而實現滑動。當然上面我們是通過getX()和getY()來獲取視圖坐標來進行修改,我們也可以通過getRawX()和getRawY()來獲取絕對坐標來實現上面的效果。代碼如下:
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); //重新設置初始坐標 mLastX = x; mLastY = y; break; } return true; }
上面一定要注意,我們在改變完View的位置後必須調用設置初始坐標,這樣才能准確獲取偏移量。
三、offsetLeftAndRight和offsetTopAndBottom
這一種方法和上一種方法大部分步驟都是相同的,只是在移動View上有所差別,代碼如下:
offsetLeftAndRight(offsetX); offsetTopAndBottom(offsetY);
上面的這種方法只是多了一層封裝,可以實現比上面實現同樣的效果。
四、設置LayoutParams
LayoutParams可以通過改變的布局參數,我們可以通過下面的代碼實現上面同樣的效果。
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams(); layoutParams.leftMargin = getLeft() + offsetX; layoutParams.topMargin = getTop() + offsetY; setLayoutParams(layoutParams);
注意:我們的LayoutParams
可以通過getLayoutParams()
方法來獲取,但是要注意,如果View的父布局是LinearLayout
,那麼我們的LayoutParams
就是LinearLayout.LayoutParams
,如果View的父布局是RelativeLayout
,則我們的LayoutParams
就是RelativeLayout.LayoutParams
。當然我們還有一種簡單的方法,不用再管父布局的布局方式。代碼如下:
ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams(); marginLayoutParams.leftMargin = getLeft() + offsetX; marginLayoutParams.topMargin = getTop() + offsetY; setLayoutParams(marginLayoutParams);
上面的這種方法不用管父布局的類型,使用起來更加方便。
五、scrollTo和scrollBy方法
關於這兩個方法我們需要仔細說一下其中的一些注意事項
1 . scrollTo的參數是具體的一個坐標點(x, y), 而scrollBy的參數是在x, y方向上的坐標偏移
2 . scrollTo和scrollBy移動的是View的內容。這一點很重要!!!!
如果我們對ViewGroup使用scrollTo和scrollBy則移動的是內部的所有子View, 如果對TextView使用scrollTo和scrollBy則移動的是其中額文本。
3 . 視圖移動還有一個不太好理解的地方在於坐標,我們下面結合圖片來說明一下:
視圖移動1
視圖移動2
我們可以這樣理解,我們的手機屏幕作為一個蓋板,在手機屏幕下面是一個巨大的畫布,我們的手機屏幕這個蓋板是透明的,導致只有和手機屏幕重合的畫布部分才會被我們看到,我們調用scrollTo
和scrollBy
也可以理解為是在移動手機上面的蓋板。如圖中所示,按鈕在ViewGroup
中的坐標是(20, 10)
,當我們調用scrollBy(20, 10)
之後,就相當於移動了屏幕上的蓋板,然後我們看到的按鈕就到了ViewGroup
的左上角。這樣如果我們想讓按鈕在水平和豎直方向上各移動20
和10
個單位,我們就必須調用scrollBy(-20, -10)
經過了上面的知識准備,我們這裡也使用scrollBy來實現前面實現的那個View隨手指移動的小例子:
((View)getParent()).scrollBy(-offsetX, -offsetY);
六、使用Scroller
Scroller也是滑動中很重要的一個角色,進過前面的scrollTo和scrollBy大家也會發現,它們的移動時瞬間完成的,滑動顯得十分突兀,Google為了改善用戶體驗,便給出了Scroller,它可以實現平滑的移動,從而使滑動過程更加真實,用戶體驗更好,下面我們先簡單說說Scroller的實現原理。
Scroller
也是滑動中很重要的一個角色,進過前面的scrollTo
和scrollBy
大家也會發現,它們的移動時瞬間完成的,滑動顯得十分突兀,Google為了改善用戶體驗,便給出了Scroller
,它可以實現平滑的移動,從而使滑動過程更加真實,用戶體驗更好,下面我們先簡單說說Scroller
的實現原理。
Scroller
的實現方式類似於scrollTo
和scrollBy
,scrollTo
和scrollBy
的移動都是從一個坐標點瞬間移動到另一個左邊點,而Scroller
則是將移動的這段距離切分成好幾段的微小的位移,然後每一段調用scrollTo
來不斷移動這些微小的位移,由於人眼的視覺暫留效果,就會給人平滑移動的視覺效果。
下面我們在上一步的基礎上增加一個小功能,第一部分還是View隨手指移動,但是當我們松開手指時,讓View自己平滑移動到最初始的位置(屏幕左上角),下面我們就來一步步介紹Scroller
的用法
1 . 聲明Scroller變量,並在構造方法中進行初始化
2 . 在觸控事件的ACTION_UP(手指抬起)事件中傳入開始滑動的坐標和需要滑動的距離並觸發Scroller的滑動事件
3 . 重寫computeScroll(),實現真正的滑動
下面是完整的代碼示例:
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); //在構造方法中初始化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; //實現View跟隨手指移動的效果 ((View)getParent()).scrollBy(-offsetX, -offsetY); //重新設置初始坐標 mLastX = x; mLastY = y; break; case MotionEvent.ACTION_UP: //當手指抬起時執行滑動過程 View view = (View) getParent(); mScroller.startScroll(view.getScrollX(), view.getScrollY(), view.getScrollX(), view.getScrollY(), 5000); //調用重繪來間接調用computeScroll()方法 invalidate(); break; } return true; } @Override public void computeScroll() { super.computeScroll(); //判斷滑動過程是否完成 if (mScroller.computeScrollOffset()){ ((View)getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); //通過重繪來不斷調用computeScroll()方法 invalidate(); } } }
上面的代碼View隨手指移動的代碼部分是與前面相同的,我們只說說Scroller的部分以及一些注意事項
1 . startScroll()方法各參數的意義,我們可以看看下面的源碼:
/** * 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
參數就是開始滾動的(x, y)
坐標,那麼我們就可以通過ViewGroup(子View的父視圖)
的getScrollX()
和getScrollY()
來獲取,這裡一定要注意,我們在滑動時的content
就是子View
,所以我們通過子View的父視圖(ViewGroup)的getScrollX()
和getScrollY()
獲取到的就是子View在X和Y方向上滑動的距離,即就是我們需要的當我們手指抬起時子View的(x, y)坐標。而如果我們對子View調用getScrollX()
和getScrollY()
方法,則獲得的是子View內部的視圖的滑動距離及坐標。
dx
和dy
分別是在X和Y方向上的偏移量,而且注釋中說了,如果我們傳入的dx
和dy
的值是正值,那麼將會向上向左移動這個content(其實就是我們這裡的View)
,即我們就可以讓子View回到左上角,這裡我們還是可以借助於上一小節中提到的視圖移動的概念,我們想讓子View向坐上方移動,其實就是想讓覆蓋在上面的蓋板向右下角移動,我們可以將dx
和dy
理解為父視圖(覆蓋在上面的蓋板)的偏移量。
假設我們剛開始是讓子View隨手指向右下方移動,那麼相當於覆蓋在上面的蓋板是向左上方移動,所以我們通過getScrollX()
和getScrollY()
獲得的值是負值,我們現在松開手指想讓子View向左上方移動(即回到屏幕左上角),那麼就相當於蓋板向右下角移動,所以我們的dx
和dy
的值必須是-getScrollX()
和-getScrollY()
,此時的兩個值都是正值。
2 . 由於我們的computeScroll()
方法不會主動調用,但是我們又需要它不斷調用從而不斷進行微小移動從而實現平滑的滑動,所以我們可以通過下面的方法。
這三個按照以下順序進行調用 invalidate()
--->onDraw()
--->computeScroll()
,所以我們可以可以在ACTION_UP
中調用完startScroll()
方法後調用invalidate()
方法,然後在computeScroll()
方法中判斷滑動是否結束,如果沒結束,則通過getCurrX()
和getCurrY()
來獲得當前需要移動的微小的位移的坐標點,然後傳入scrollTo()
方法中,這時候子View還只是移動了一小段距離,然後我們再次調用invalidate()
方法,然後接著調用onDraw()
方法,然後再次進入computeScroll()
中再次讓子View移動一小段距離,直到滑動結束,computeScrollOffset()
返回false
,則這個循環調用的過程結束,從而完成平滑移動的過程。
七、屬性動畫
屬性動畫一樣可以實現View的滑動,但是由於屬性動畫涉及到的知識點也是眾多,這裡不再展開來寫,只是提供一個思路,後續後專門寫一篇博客來說。
八、ViewDragHelper
ViewDragHelper可以幫助我們實現各種滑動需求,但是它的使用也相對較復雜,所以准備專門寫一篇博客來介紹他,這裡只是給出一個概念
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。
這個方法是通用的,不僅僅適用於EditText,也適用於TextView、AutoCompleteTextView等控件。 Google官方API並沒有給出一個直接的方法
當我們調試安卓機器時,第一次插上usb線,會彈出一個授權的對話框,(前提是打開了usb調試功能)點擊確認,才會允許調試.如果我們想機器默認就可以調試該怎麼做呢?如果我們想
我們首先從一個簡單的例子開始分析 button.setOnClickListener(new View.OnClickListener() { @
自主實現滑動指示條先上效果圖:1、XML布局布局代碼如下:<LinearLayout xmlns:android=http://schemas.android.co