亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

用Scroller實現簡單viewpager滑動

2019-11-09 14:27:47
字體:
來源:轉載
供稿:網友

用Scroller實現簡單viewpager滑動

看了guolin大神的一篇博客,介紹的很詳細,不適合小白。 viewpager可以左右滑動,如何做的呢,viepager的實現代碼太多了3千多行,不做深究了。我們實現簡單的滑動即可。說到滑動大家一定會想到scrollTo(x,y)和scrollBy(x,y)?,F在來看一下他們的起源,從View控件中可以找到。

public class View implements Drawable.Callback, KeyEvent.Callback,accessibilityEventSource { public void scrollBy(int x, int y) { scrollTo(mScrollX + x, mScrollY + y); }public void scrollTo(int x, int y) { if (mScrollX != x || mScrollY != y) { int oldX = mScrollX; int oldY = mScrollY; mScrollX = x; mScrollY = y; invalidateParentCaches(); onScrollChanged(mScrollX, mScrollY, oldX, oldY); if (!awakenScrollBars()) { postInvalidateOnAnimation(); } } }}

       代碼可以看到這兩個函數已經實現,不是抽象方法,而且我查了ViewGroup,LinearLayout,TextView里面都沒有這兩個方法的復寫,只在TextView中使用過,所以可以這樣認為,scrollTo(x,y)和scrollBy(x,y)在View類中就是最后的實現。所以查看它們到View中看就OK了。另一方面說明了,其他所有繼承View控件都存在這兩個方法,并且控件內容都可以移動。

       說了這么多廢話,現在進入正題,scrollTo(x,y)和scrollBy(x,y)有什么區別呢。從scrollBy(x,y)的實現上可以看到,scrollBy(x,y)其實內部調用的就是scrollTo(x,y),唯一的區別就是在原有移動距離上加上新的移動距離。假設現在x軸已經移動了sx,y軸移動sy,如果在次調用scrollBy(x,y),在x軸上的移動距離變成x+sx,y軸上的一定距離y+sy。如果是scrollTo(x,y)無論調用多少次,只會在第一次調用時移動,除非改變x,y值。

       說道現在,大家可能已經明白了,viewpager的滑動與scrollTo(x,y)和scrollBy(x,y)有關。是的,就是他們實現了viewpager的滑動。下面是我寫的滑動容器:

public class ScollerContainer extends ViewGroup { PRivate Scroller scroller; private float XDown; private float XMove; private float XLastMove; private int leftBorder; private int rightBorder; private int touchSlop; public ScollerContainer(Context context) { super(context); init(); } public ScollerContainer(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ScollerContainer(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init(){ scroller = new Scroller(getContext()); ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext()); touchSlop = viewConfiguration.getScaledTouchSlop(); } @Override public boolean onInterceptTouchEvent(MotionEvent event) { float diff = 0; switch (event.getAction()){ case MotionEvent.ACTION_DOWN: XDown = event.getRawX(); XLastMove = XDown; break; case MotionEvent.ACTION_MOVE: XMove = event.getRawX(); diff = Math.abs(XMove-XDown); XLastMove = XMove; if (diff>touchSlop){ return true; } break; } return super.onInterceptHoverEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { float scrollerX ; float diff; switch (event.getAction()) { case MotionEvent.ACTION_MOVE: scrollerX = getScrollX(); XMove = event.getRawX(); diff = XLastMove-XMove; if (scrollerX+diff<leftBorder){ scrollTo(leftBorder,0); return true; }else if (scrollerX+diff+getWidth()>rightBorder){ scrollTo(rightBorder -getWidth(),0); return true; } scrollBy((int) diff,0); XLastMove = XMove; break; case MotionEvent.ACTION_UP: // 當手指抬起時,根據當前的滾動值來判定應該滾動到哪個子控件的界面 int targetIndex = (getScrollX() + getWidth() / 2) / getWidth(); int dx = targetIndex * getWidth() - getScrollX(); // 第二步,調用startScroll()方法來初始化滾動數據并刷新界面 Log.d("moveX:","scrollX="+getScrollX()+" dx="+dx); scroller.startScroll(getScrollX(), 0, dx, 0); invalidate(); break; } return super.onTouchEvent(event); } @Override public void computeScroll() { super.computeScroll(); if (scroller.computeScrollOffset()){ scrollTo(scroller.getCurrX(),scroller.getCurrY()); invalidate(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int count = getChildCount(); View child=null; for (int i=0;i<count;i++){ child = getChildAt(i); child.measure(widthMeasureSpec,heightMeasureSpec); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = getChildCount(); View child = null; for (int i=0;i<count;i++){ child = getChildAt(i); child.layout(child.getMeasuredWidth()*i,0,child.getMeasuredWidth()*(i+1),child.getMeasuredHeight()); } leftBorder = getChildAt(0).getLeft(); rightBorder = getChildAt(count-1).getRight(); }}

上面的代碼就可以實現滑動了,看圖: 演示圖

如果只使用了scrollTo(x,y),scrollBy(x,y),雖然可以實現滑動,但是不會出現粘性滑動,就是手指離開后,控件慢慢回到原位。這是怎么做到的呢?下面開始講解。

要做到粘性滑動,就要使用Scroller,可以看下Scroller源碼,他是存粹的類,它的主要作用就是計算時間段滑動多少距離??匆欢蜸croller中的代碼,

public void startScroll(int startX, int startY, int dx, int dy) { startScroll(startX, startY, dx, dy, DEFAULT_DURATION); } public void startScroll(int startX, int startY, int dx, int dy, int duration) { mMode = SCROLL_MODE; mFinished = false; mDuration = duration; mStartTime = AnimationUtils.currentAnimationTimeMillis(); mStartX = startX; mStartY = startY; mFinalX = startX + dx; mFinalY = startY + dy; mDeltaX = dx; mDeltaY = dy; mDurationReciprocal = 1.0f / (float) mDuration; }

上述兩個函數都傳入了距離,時間。手指離開手機后,控件“粘性還原”要用到“時間”(沒有傳入時間,使用默認值DEFAULT_DURATION)和“移動的距離”。依據”時間“和“移動距離”計算出每秒移動的距離(這句話不嚴格,真正實現算法很復雜,還有加速,減速情況,只不過這樣說容易理解),然后通過scroller實例中的scroller.getCurrX()和scroller.getCurrY()方法取得計算后要移動的距離值。如此看來,Scroller就是個計算“移動距離”的工具類。看代碼,

public void computeScroll() { super.computeScroll(); if (scroller.computeScrollOffset()){//取得計算后要移動的距離值 scrollTo(scroller.getCurrX(),scroller.getCurrY()); invalidate(); } }

scroller.computeScrollOffset()判斷scroller內部處理有沒有結束(內部處理結束也就意味著,控件已經粘性復原了,因為內部處理依賴傳入的距離和時間嗎),如果結束,則scroller.computeScrollOffset()返回false

有人會問,computeScroll()為什么會循環調用呢?看到 invalidate()了嗎,這個充當循環角色, invalidate()被執行后,ui界面會被重新繪制,這樣的話,draw()函數就會被調用,我們看一下它的源碼:

boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {...if (!drawingWithRenderNode) { computeScroll(); sx = mScrollX; sy = mScrollY; }...}

draw()執行了computeScroll(),而computeScroll()中又存在invalidate()方法,所以構成了循環,不是嗎。這只是粘性滑動實現的一部分,另一部分開代碼(截取ScollerContainer中的代碼),

case MotionEvent.ACTION_UP: // 當手指抬起時,根據當前的滾動值來判定應該滾動到哪個子控件的界面 int targetIndex = (getScrollX() + getWidth() / 2) / getWidth(); int dx = targetIndex * getWidth() - getScrollX(); // 第二步,調用startScroll()方法來初始化滾動數據并刷新界面 Log.d("moveX:","scrollX="+getScrollX()+" dx="+dx); scroller.startScroll(getScrollX(), 0, dx, 0); invalidate(); break;

int targetIndex = (getScrollX() + getWidth() / 2) / getWidth(); 這段代碼如何解釋,getScrollX() 已經滑動的距離, getWidth() / 2不滑動控件的寬的1/2。沒有畫圖工具,大家自己畫圖思考,我用文字描述。 用viewpager解釋,大家方便想象。假設viewpager中有10項,可以被滑動,分別標志0,1,2,3,4,5,6,7,8,9。假如當前滑動到第4項和第5項之間,手指不離開,腦洞打開想一下。當手指離開時,是讓第4項顯示還是第5項顯示在手機屏幕上。在4,5之間,此時,getScrollX() >4*getWidth(),這里分兩種情況, 第一種情況,如果4滑動過半了,getScrollX() + getWidth() / 2>5*getWidth(),那么,(getScrollX() + getWidth() / 2) / getWidth()值是不是5.xxx,取整后=5,再看, targetIndex * getWidth() - getScrollX()不就是4沒有過半的距離值(targetIndex * getWidth()是第5項距離值),最后粘性結果手機屏幕上顯示第5項。 第二種情況,如果4沒有過半,getScrollX() + getWidth() / 2<5*getWidth(),同樣,(getScrollX() + getWidth() / 2) / getWidth()值是不是4.xxx,取整后=4,在看targetIndex * getWidth() - getScrollX()不就是4移動的沒過半的距離值。 在執行 ,scroller.startScroll(getScrollX(), 0, dx, 0); invalidate();后,手指離開后,粘性滑動并復位了嗎。

剩余代碼:

<?xml version="1.0" encoding="utf-8"?> <com.luo.usedemo.ScollerContainer xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/scrollerContainer" android:layout_height="match_parent" android:layout_width="match_parent"> <Button android:layout_width="match_parent" android:layout_height="100dp" android:text="第一個view"/> <Button android:layout_width="match_parent" android:layout_height="100dp" android:text="第二個view"/> <Button android:layout_width="match_parent" android:layout_height="100dp" android:text="第三個view"/> </com.luo.usedemo.ScollerContainer>public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美日韩一区二区在线播放| 国内免费精品永久在线视频| 日本91av在线播放| 亚洲伊人一本大道中文字幕| 中文字幕自拍vr一区二区三区| 欧美亚洲视频在线看网址| 国产一区视频在线播放| 欧美激情按摩在线| 久久久伊人欧美| 色妞欧美日韩在线| 国产视频亚洲视频| 日韩欧美国产一区二区| 欧美激情一区二区三级高清视频| 91在线精品播放| 久久视频国产精品免费视频在线| 欧美极品少妇全裸体| 精品中文字幕乱| 亚洲欧美另类在线观看| 久久乐国产精品| 日韩久久精品电影| 日本免费在线精品| 日韩国产精品亚洲а∨天堂免| 91大神福利视频在线| 国产国语刺激对白av不卡| 久久影视电视剧凤归四时歌| 91av福利视频| 成人国内精品久久久久一区| 国产偷亚洲偷欧美偷精品| 成人a级免费视频| 亚洲国产婷婷香蕉久久久久久| 国产精品久久久久久av| 日韩精品视频免费| 精品久久久久国产| 国产精品视频永久免费播放| 日韩大胆人体377p| 欧美激情视频一区二区三区不卡| 国产日本欧美在线观看| 国产精品一二区| 国产精品99久久久久久人| 国产成人在线精品| 97国产真实伦对白精彩视频8| 中文字幕一精品亚洲无线一区| 欧美成人中文字幕| 国产日韩在线精品av| 91超碰中文字幕久久精品| 久久91亚洲人成电影网站| 国产欧美日韩精品丝袜高跟鞋| 伊人久久大香线蕉av一区二区| 久久国产精品视频| 欧洲永久精品大片ww免费漫画| 日韩av在线免播放器| 91精品综合久久久久久五月天| 国产精品一区av| 一色桃子一区二区| 国产欧美va欧美va香蕉在线| 欧美另类69精品久久久久9999| 亚洲第五色综合网| 欧美一区二粉嫩精品国产一线天| 亚洲精品v天堂中文字幕| 亚洲一区二区三区久久| 国产丝袜精品视频| 亚洲3p在线观看| 亚洲精品久久久久久久久久久| 最新国产精品拍自在线播放| 日韩中文字幕视频在线| 国产美女91呻吟求| 欧美尺度大的性做爰视频| 亚洲精品成a人在线观看| 精品久久久久久久久久ntr影视| 国产精品欧美激情在线播放| 亚洲一区www| 国产欧亚日韩视频| 91精品国产综合久久久久久久久| 久久青草福利网站| 亚洲三级黄色在线观看| 668精品在线视频| 欧美视频在线观看 亚洲欧| 亚洲精品丝袜日韩| 欧美激情二区三区| 色与欲影视天天看综合网| 欧美性极品xxxx做受| 欧美大荫蒂xxx| 国产精品99久久久久久久久久久久| 亚洲久久久久久久久久| 国产69久久精品成人| 成人精品一区二区三区电影免费| 中文亚洲视频在线| 欧美乱大交做爰xxxⅹ性3| 国产一区深夜福利| 最近中文字幕日韩精品| 久久久久久久成人| 国产欧美在线观看| 久久久999国产精品| 欧美精品videosex性欧美| 国产91对白在线播放| 国产精品高潮呻吟久久av黑人| 亚洲人成在线观看| 55夜色66夜色国产精品视频| 日韩福利伦理影院免费| 欧美日韩一区二区免费视频| 日韩精品在线视频美女| 国产精品免费网站| 另类视频在线观看| 国产91久久婷婷一区二区| 日韩专区在线观看| 亚洲精品中文字| 欧美大片网站在线观看| 青草成人免费视频| 欧美性生交大片免网| 欧美日韩精品二区| 国产亚洲欧洲黄色| 久久久www成人免费精品| 97精品一区二区视频在线观看| 亚洲天堂av电影| 精品福利免费观看| 亚洲精品大尺度| 国产精品一区二区三区毛片淫片| 亚洲在线视频观看| 欧美激情在线视频二区| 成人久久一区二区| 欧洲亚洲在线视频| 日本不卡高字幕在线2019| 91chinesevideo永久地址| 久久成人免费视频| 欧美午夜宅男影院在线观看| 亚洲人a成www在线影院| 69影院欧美专区视频| 国产精品国产三级国产aⅴ浪潮| 久久99精品久久久久久噜噜| 成人黄色在线免费| 色噜噜狠狠狠综合曰曰曰88av| 亚洲免费福利视频| 亚洲人成自拍网站| 中国日韩欧美久久久久久久久| 亚洲视频777| 亚洲免费一在线| 欧美极品在线视频| 欧美日韩性生活视频| 色综合91久久精品中文字幕| 久久久亚洲国产天美传媒修理工| 97碰在线观看| 美女扒开尿口让男人操亚洲视频网站| 国产午夜精品一区理论片飘花| 亚洲第一色在线| 国产精品中文字幕在线| 亚洲乱亚洲乱妇无码| 国产精品第二页| 成人福利在线视频| 久久精品亚洲精品| 国产精品盗摄久久久| 九九热这里只有精品6| 国产精品一区电影| 欧美激情xxxxx| 国语自产精品视频在线看| 91av在线免费观看视频| 亚洲电影成人av99爱色| 中文字幕亚洲国产| 欧美在线视频观看免费网站| 日本精品va在线观看| 中文字幕久热精品在线视频| 日本19禁啪啪免费观看www| 91社影院在线观看| 国产精品伦子伦免费视频| 成人在线视频网站|