Android View移動(dòng)的3種方式總結(jié)
前言
在A(yíng)ndroid開(kāi)發(fā)中,View一直是Android開(kāi)發(fā)人員的一塊心病,一方面想要進(jìn)階,一方面又害怕進(jìn)階,可以說(shuō)Android的View是進(jìn)階路上的最大絆腳石,因?yàn)樗婕暗臇|西太多了,比如本次我們此次要寫(xiě)的View移動(dòng),另外還包括View的觸摸事件的傳遞,創(chuàng)建自定義View,這些都是極其重要且不得不面對(duì)的難題。但是無(wú)論如何,現(xiàn)在不克服的困難將來(lái)就會(huì)被困難克服。
在此之前,我們還是先了解Android坐標(biāo)系的定義規(guī)則以及View的一些位置參數(shù)。
Android坐標(biāo)系
View的位置及大小是由四個(gè)參數(shù)決定,即left、top、right、bottom,并且這四個(gè)參數(shù)都是相對(duì)于其父View的。
int width = right-left; int height = bottom-top;
在A(yíng)ctivity中布局完成后,我們可以通過(guò)View一些方法獲取這些參數(shù)信息:
//left,top,right,bottom值的獲取 int left = getLeft(); int top = getTop(); int right = getRight(); int bottom = getBottom();
另外Android 3.0以后加入x,y,translationX,translationY等參數(shù)。(x,y)表示為View在ViewGroup中左上角的x,y的值,translationX,translationY在用于平移一個(gè)View。默認(rèn)是都為0,在調(diào)用了View的setTranslationX()/setTranslationY()
之后發(fā)生改變。
//x,y,translationX,translationY參數(shù)的獲取 int x = getX(); int y = getY(); int translationX = getTranslationX(); int translationY = getTranslationY();
PS:調(diào)用View的setTranslationX()
和setTranslationY()
方法雖然可以使得View平移指定距離,但是這一過(guò)程是瞬間完成的。為了使View的移動(dòng)使得更為平滑,因此可以使用View的屬性動(dòng)畫(huà)來(lái)指定translationX和translationY。
ObjectAnimator valueAnimator = ObjectAnimator.ofFloat(textView, "translationX", 200); valueAnimator.setDuration(2000); valueAnimator.start();
另外,如果給View設(shè)置setTranslationX()
和setTranslationY()
后,如果設(shè)置的值沒(méi)有發(fā)生變化,那么其只會(huì)移動(dòng)一次,即首次指定的移動(dòng)距離。查看源碼后我們發(fā)現(xiàn)原因:原來(lái)在設(shè)置值之后其會(huì)將設(shè)置進(jìn)去的值和當(dāng)前的translationX,translationY進(jìn)行對(duì)比,不一致時(shí)才進(jìn)行移動(dòng)。
了解了View的一些基本參數(shù)之后,我們看關(guān)于View的三種移動(dòng)方式。
一、使用Android系統(tǒng)提供的scrollTo()/scrollBy()方法實(shí)現(xiàn)View的移動(dòng)。
不管是scrollTo()
還是scrollBy()
其移動(dòng)的本質(zhì)都是View/ViewGroup中的內(nèi)容。并且其移動(dòng)的過(guò)程是瞬間完成的,因此,為了實(shí)現(xiàn)更好的移動(dòng)效果,他需要與Scroller類(lèi)結(jié)合使用。另外,它不同于上面的Translation,移動(dòng)的是View本身,這一點(diǎn)需要好好理解一下。
scrollTo()
和scrollBy()
都是View中的方法, 不是Scroller中的方法 ,但是控制View的平滑移動(dòng)與Scroller類(lèi)密不可分。
scrollTo() :
指是的移動(dòng)的絕對(duì)位置,如果位置沒(méi)有變化,多次調(diào)用則不會(huì)起作用。
scrollTo移動(dòng)過(guò)程示意圖
scrollBy() :
其本質(zhì)依然是調(diào)用的scrollTo()
,指的的移動(dòng)當(dāng)前位置的相對(duì)距離(每次都是先將當(dāng)前的位置和設(shè)置的距離相加之和調(diào)用scrollTo(),這樣如果你多次調(diào)用,你就會(huì)發(fā)現(xiàn)其每次都會(huì)移動(dòng)一段距離,這是和scrollTo()的本質(zhì)區(qū)別)
scrollBy移動(dòng)過(guò)程示意圖
PS:關(guān)于上面兩張圖,其實(shí)一直以來(lái),我自己都沒(méi)完全搞明白什么相對(duì)絕對(duì),所以?xún)蓮埵謭D可能會(huì)讓人更容易理解。還有就是scrollTo()
和scrollBy()
移動(dòng)方向問(wèn)題,上面我們已經(jīng)畫(huà)過(guò)Android的坐標(biāo)系,x軸左→右為正,y軸從上→下為正。但是這并不適用于scrollTo和scrollBy,scrollTo和scrollBy剛好相反,即x軸左→右為負(fù),y軸從上→下為負(fù),簡(jiǎn)直是有點(diǎn)坑爹啊。
Scroller類(lèi)分析:而為什么使用Scroller類(lèi)中的方法可以對(duì)View/ViewGroup的內(nèi)容進(jìn)行移動(dòng)呢?下面我們?cè)囍治鲆幌隆?br />
首先
我們創(chuàng)建一個(gè)Scroller類(lèi)的對(duì)象mScroller。
然后
要使View在規(guī)定的時(shí)間中移動(dòng)到指定的位置,我們會(huì)調(diào)用startScroll()
方法,startScroll()
是Scroller
類(lèi)中的方法,另外Scroller
類(lèi)中還有一個(gè)filing()
方法也是很常用的,它主要是處理平滑的移動(dòng),一般營(yíng)造滑動(dòng)之后的慣性效果,使得View的移動(dòng)更逼真。下面我們看startScroll()
的源碼:
//其接收四個(gè)/五個(gè)參數(shù)。如果duration不設(shè)置,則為默認(rèn)。這四個(gè)參數(shù)都不難理解,這里不再做解釋。 public void startScroll(int startX, int startY, int dx, int dy, int duration) { ... }
而一般我們調(diào)用這個(gè)方法后都要去調(diào)View的 invalidate()
,這個(gè)方法可以觸發(fā)View的draw()
方法。而draw()中
調(diào)用了 computeScroll()
,源碼中我們發(fā)現(xiàn)computeScroll()是
個(gè)空方法,這也是為什么我們需要重寫(xiě) computeScroll()
方法的原因。因?yàn)檎诘囊苿?dòng)操作就是在computeScroll()
中進(jìn)行的。
@Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); //必須調(diào)用View的postInvalidate()/invalidate(),如果不加會(huì)導(dǎo)致View的移動(dòng)只會(huì)第一幀。 postInvalidate(); } super.computeScroll(); }
上面我們看到Scroller類(lèi)中還有一個(gè)computeScrollOffset()
方法,它又是干啥的呢?它的主要作用就是判斷mCurrX,和mCurrY是否有改變,有則返回true,無(wú)則返回false。通過(guò)這個(gè)方法的判斷可以指點(diǎn)是否需要持續(xù)的調(diào)用scrollTo()
去移動(dòng)View。這里再給出一個(gè)示例,使用scrollTo()
讓View跟著手指移動(dòng):
public class CuView extends LinearLayout { private float mStartX; private float mStartY; private Scroller mScroller; /** * 第一次滑動(dòng)是否完成 */ private boolean isFirstFinish; public CuView(Context context) { super(context); init(context); } public CuView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } private void init(Context context) { mScroller = new Scroller(context); } public CuView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public CuView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context); } /** * 讓View跟著你的手指走吧 * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: /** * 第一次移動(dòng)完成后,我們不需要再去拿開(kāi)始的位置了,否則造成View重新移動(dòng)的最起始的位置。 */ if (!isFirstFinish) { mStartX = event.getRawX(); mStartY = event.getRawY(); } break; case MotionEvent.ACTION_MOVE: scrollTo((int) (mStartX - event.getRawX()), (int) (mStartY - event.getRawY())); break; case MotionEvent.ACTION_UP: //第一次移動(dòng)完成 isFirstFinish = true; break; } return true; } /** * 測(cè)試startScroll */ public void startScroll() { /** * 注意Scroller移動(dòng)方向, */ mScroller.startScroll(20, 20, -500, -500, 5000); invalidate(); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); invalidate(); } super.computeScroll(); } }
二、使用動(dòng)畫(huà)實(shí)現(xiàn)View的移動(dòng)。
這里包括View的Tween Animation/Frame Animation,以及3.0之后加入的Property Animation。其移動(dòng)的是View的一個(gè)映像,View本身的位置及大小并沒(méi)有發(fā)生任何改變。
三、設(shè)置View的LayoutParams來(lái)移動(dòng)View
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) textView.getLayoutParams(); layoutParams.leftMargin = 50; textView.requestLayout();
總結(jié)
以上就是總結(jié)Android View移動(dòng)的3種方式的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家開(kāi)發(fā)Android的時(shí)候能有所幫助,如果有疑問(wèn)大家可以留言交流。
- Android跟隨手指移動(dòng)的控件demo實(shí)例
- Android自定義View實(shí)現(xiàn)跟隨手指移動(dòng)的小兔子
- Android繪制跟隨手指移動(dòng)的小球
- Android自定義圓形View實(shí)現(xiàn)小球跟隨手指移動(dòng)效果
- Android實(shí)現(xiàn)拖動(dòng)小球跟隨手指移動(dòng)效果
- Android實(shí)現(xiàn)View拖拽跟隨手指移動(dòng)效果
- Android中View跟隨手指移動(dòng)效果
- Android View移動(dòng)的六種方法小結(jié)
- Android切換至SurfaceView時(shí)閃屏(黑屏閃一下)以及黑屏移動(dòng)問(wèn)題的解決方法
- Android自定義View實(shí)現(xiàn)跟隨手指移動(dòng)
相關(guān)文章
Studio 編譯報(bào)錯(cuò):compileSdkVersion ''android-24'' requires JDK 1.
今天小編就為大家分享一篇關(guān)于Studio編譯報(bào)錯(cuò):compileSdkVersion 'android-24' requires JDK 1.8 or later to compile.的解決辦法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-10-10android獲取圖片尺寸的兩種方式及bitmap的縮放操作
這篇文章主要介紹了android獲取圖片尺寸的兩種方式及bitmap的縮放操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08Android ListView下拉刷新上拉自動(dòng)加載更多DEMO示例
這篇文章主要介紹了Android ListView下拉刷新上拉自動(dòng)加載更多DEMO示例的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07Android仿微信輸入框效果的實(shí)現(xiàn)代碼
這篇文章主要介紹了Android仿微信輸入框效果的實(shí)現(xiàn)代碼,需要的朋友參考下吧2017-05-05使用Android的OkHttp包實(shí)現(xiàn)基于HTTP協(xié)議的文件上傳下載
OkHttp(GitHub主頁(yè)https://github.com/square/okhttp)是近來(lái)人氣攀升的一款安卓第三方HTTP包,這里我們來(lái)講解一下如何使用Android的OkHttp包實(shí)現(xiàn)基于HTTP協(xié)議的文件上傳下載:2016-07-07