最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android Scroller完全解析

 更新時間:2016年10月22日 16:27:19   作者:huaxun66  
這篇文章主要為大家詳細(xì)介紹了Android Scroller源碼,區(qū)分scrollTo()和scrollBy(),感興趣的小伙伴們可以參考一下

在Android中,任何一個控件都是可以滾動的,因?yàn)樵赩iew類當(dāng)中有scrollTo()和scrollBy()這兩個方法,如下圖所示:

這兩個方法的主要作用是將View/ViewGroup移至指定的坐標(biāo)中,并且將偏移量保存起來。另外:

mScrollX 代表X軸方向的偏移坐標(biāo)
mScrollY 代表Y軸方向的偏移坐標(biāo)

這兩個方法都是用于對View進(jìn)行滾動的,那么它們之間有什么區(qū)別呢?簡單點(diǎn)講,scrollBy()方法是讓View相對于當(dāng)前的位置滾動某段距離,而scrollTo()方法則是讓View相對于初始的位置滾動某段距離。

關(guān)于偏移量的設(shè)置我們可以參看下源碼:

public class View { 
  .... 
  protected int mScrollX; //該視圖內(nèi)容相當(dāng)于視圖起始坐標(biāo)的偏移量,X軸方向   
  protected int mScrollY; //該視圖內(nèi)容相當(dāng)于視圖起始坐標(biāo)的偏移量,Y軸方向 
  //返回值 
  public final int getScrollX() { 
    return mScrollX; 
  } 
  public final int getScrollY() { 
    return mScrollY; 
  } 
  public void scrollTo(int x, int y) { 
    //偏移位置發(fā)生了改變 
    if (mScrollX != x || mScrollY != y) { 
      int oldX = mScrollX; 
      int oldY = mScrollY; 
      mScrollX = x; //賦新值,保存當(dāng)前便宜量 
      mScrollY = y; 
      //回調(diào)onScrollChanged方法 
      onScrollChanged(mScrollX, mScrollY, oldX, oldY); 
      if (!awakenScrollBars()) { 
        invalidate(); //一般都引起重繪 
      } 
    } 
  } 
  // 看出區(qū)別了吧 。 mScrollX 與 mScrollY 代表我們當(dāng)前偏移的位置 , 在當(dāng)前位置繼續(xù)偏移(x ,y)個單位 
  public void scrollBy(int x, int y) { 
    scrollTo(mScrollX + x, mScrollY + y); 
  } 
  //... 
} 

于是,在任何時刻我們都可以獲取該View/ViewGroup的偏移位置了,即調(diào)用getScrollX()方法和getScrollY()方法。

下面我們寫個例子看下它們的區(qū)別吧:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/layout"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">

  <Button
    android:id="@+id/scroll_to_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="scrollTo"/>

  <Button
    android:id="@+id/scroll_by_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="10dp"
    android:text="scrollBy"/>
</LinearLayout>

外層使用了一個LinearLayout,在里面包含了兩個按鈕,一個用于觸發(fā)scrollTo邏輯,一個用于觸發(fā)scrollBy邏輯。

public class MainActivity extends AppCompatActivity {

  private LinearLayout layout;
  private Button scrollToBtn;
  private Button scrollByBtn;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    layout = (LinearLayout) findViewById(R.id.layout);
    scrollToBtn = (Button) findViewById(R.id.scroll_to_btn);
    scrollByBtn = (Button) findViewById(R.id.scroll_by_btn);
    scrollToBtn.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        layout.scrollTo(getResources().getDimensionPixelOffset(R.dimen.horizontal_scroll),
            getResources().getDimensionPixelOffset(R.dimen.horizontal_scroll));
      }
    });
    scrollByBtn.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        layout.scrollBy(getResources().getDimensionPixelOffset(R.dimen.horizontal_scroll),
            getResources().getDimensionPixelOffset(R.dimen.horizontal_scroll));
      }
    });
  }
}



<resources>
  <dimen name="horizontal_scroll">-20dp</dimen>
  <dimen name="vertical_scroll">-30dp</dimen>
</resources>

當(dāng)點(diǎn)擊了scrollTo按鈕時,我們調(diào)用了LinearLayout的scrollTo()方法,當(dāng)點(diǎn)擊了scrollBy按鈕時,調(diào)用了LinearLayout的scrollBy()方法。那有的朋友可能會問了,為什么都是調(diào)用的LinearLayout中的scroll方法?這里一定要注意,不管是scrollTo()還是scrollBy()方法,滾動的都是該View內(nèi)部的內(nèi)容,而LinearLayout中的內(nèi)容就是我們的兩個Button,如果你直接調(diào)用button的scroll方法的話,那結(jié)果一定不是你想看到的。

另外還有一點(diǎn)需要注意,就是兩個scroll方法中傳入的參數(shù),第一個參數(shù)x表示相對于當(dāng)前位置橫向移動的距離,正值向左移動,負(fù)值向右移動。第二個參數(shù)y表示相對于當(dāng)前位置縱向移動的距離,正值向上移動,負(fù)值向下移動。
運(yùn)行一下程序:

當(dāng)我們點(diǎn)擊scrollTo按鈕時,兩個按鈕會一起向右下方滾動,之后再點(diǎn)擊scrollTo按鈕就沒有任何作用了,界面不會再繼續(xù)滾動,只有點(diǎn)擊scrollBy按鈕界面才會繼續(xù)滾動,并且不停點(diǎn)擊scrollBy按鈕界面會一起滾動下去。

Scroller類

從上面例子運(yùn)行結(jié)果可以看出,利用scrollTo()/scrollBy()方法把一個View偏移至指定坐標(biāo)(x,y)處,整個過程是直接跳躍的,沒有對這個偏移過程有任何控制,對用戶而言不太友好。于是,基于這種偏移控制,Scroller類被設(shè)計出來了,該類的主要作用是為偏移過程制定一定的控制流程,從而使偏移更流暢,更完美。
我們分析下源碼里去看看Scroller類的相關(guān)方法,其源代碼(部分)如下: 路徑位于 \frameworks\base\core\Java\android\widget\Scroller.java

public class Scroller { 

  private int mStartX;  //起始坐標(biāo)點(diǎn) , X軸方向 
  private int mStartY;  //起始坐標(biāo)點(diǎn) , Y軸方向 
  private int mCurrX;   //當(dāng)前坐標(biāo)點(diǎn) X軸, 即調(diào)用startScroll函數(shù)后,經(jīng)過一定時間所達(dá)到的值 
  private int mCurrY;   //當(dāng)前坐標(biāo)點(diǎn) Y軸, 即調(diào)用startScroll函數(shù)后,經(jīng)過一定時間所達(dá)到的值 

  private float mDeltaX; //應(yīng)該繼續(xù)滑動的距離, X軸方向 
  private float mDeltaY; //應(yīng)該繼續(xù)滑動的距離, Y軸方向 
  private boolean mFinished; //是否已經(jīng)完成本次滑動操作, 如果完成則為 true 

  //構(gòu)造函數(shù) 
  public Scroller(Context context) { 
    this(context, null); 
  } 
  public final boolean isFinished() { 
    return mFinished; 
  } 
  //強(qiáng)制結(jié)束本次滑屏操作 
  public final void forceFinished(boolean finished) { 
    mFinished = finished; 
  } 
  public final int getCurrX() { 
    return mCurrX; 
  } 
   /* Call this when you want to know the new location. If it returns true, 
   * the animation is not yet finished. loc will be altered to provide the 
   * new location. */  
  //根據(jù)當(dāng)前已經(jīng)消逝的時間計算當(dāng)前的坐標(biāo)點(diǎn),保存在mCurrX和mCurrY值中 
  public boolean computeScrollOffset() { 
    if (mFinished) { //已經(jīng)完成了本次動畫控制,直接返回為false 
      return false; 
    } 
    int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime); 
    if (timePassed < mDuration) { 
      switch (mMode) { 
      case SCROLL_MODE: 
        float x = (float)timePassed * mDurationReciprocal; 
        ... 
        mCurrX = mStartX + Math.round(x * mDeltaX); 
        mCurrY = mStartY + Math.round(x * mDeltaY); 
        break; 
      ... 
    } 
    else { 
      mCurrX = mFinalX; 
      mCurrY = mFinalY; 
      mFinished = true; 
    } 
    return true; 
  } 
  //開始一個動畫控制,由(startX , startY)在duration時間內(nèi)前進(jìn)(dx,dy)個單位,即到達(dá)坐標(biāo)為(startX+dx , startY+dy)出 
  public void startScroll(int startX, int startY, int dx, int dy, int duration) { 
    mFinished = false; 
    mDuration = duration; 
    mStartTime = AnimationUtils.currentAnimationTimeMillis(); 
    mStartX = startX;    mStartY = startY; 
    mFinalX = startX + dx; mFinalY = startY + dy; 
    mDeltaX = dx;      mDeltaY = dy; 
    ... 
  } 
} 

其中比較重要的兩個方法為:

public boolean computeScrollOffset()
函數(shù)功能說明:根據(jù)當(dāng)前已經(jīng)消逝的時間計算當(dāng)前的坐標(biāo)點(diǎn),保存在mCurrX和mCurrY值中。

public void startScroll(int startX, int startY, int dx, int dy, int duration)
函數(shù)功能說明:開始一個動畫控制,由(startX , startY)在duration時間內(nèi)前進(jìn)(dx,dy)個單位,到達(dá)坐標(biāo)為(startX+dx , startY+dy)處。

computeScroll()方法介紹:
為了易于控制滑屏控制,Android框架提供了 computeScroll()方法去控制這個流程。在繪制View時,會在draw()過程調(diào)用該方法。因此, 再配合使用Scroller實(shí)例,我們就可以獲得當(dāng)前應(yīng)該的偏移坐標(biāo),手動使View/ViewGroup偏移至該處。
computeScroll()方法原型如下,該方法位于ViewGroup.java類中

/** 
   * Called by a parent to request that a child update its values for mScrollX and mScrollY if necessary. This will typically be done if the child is animating a scroll using a {@link android.widget.Scroller Scroller} 
   * object. 
   * 由父視圖調(diào)用用來請求子視圖根據(jù)偏移值 mScrollX,mScrollY重新繪制 */
  public void computeScroll() { //空方法 ,自定義ViewGroup必須實(shí)現(xiàn)方法體     
  } 

為了實(shí)現(xiàn)偏移控制,一般自定義View/ViewGroup都需要重載該方法 。其調(diào)用過程位于View繪制流程draw()過程中,如下:

@Override 
protected void dispatchDraw(Canvas canvas){ 
  ... 

  for (int i = 0; i < count; i++) { 
    final View child = children[getChildDrawingOrder(count, i)]; 
    if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 
      more |= drawChild(canvas, child, drawingTime); 
    } 
  } 
} 
protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 
  ... 
  child.computeScroll(); 
  ... 
} 

實(shí)例演示

ViewPager相信每個人都再熟悉不過了,因此它實(shí)在是太常用了,我們可以借助ViewPager來輕松完成頁面之間的滑動切換效果,但是如果問到它是如何實(shí)現(xiàn)的話,我感覺大部分人還是比較陌生的。其實(shí)說到ViewPager最基本的實(shí)現(xiàn)原理主要就是兩部分內(nèi)容,一個是事件分發(fā),一個是Scroller。對于事件分發(fā),不了解的同學(xué)可以參考我這篇博客Android事件的分發(fā)、攔截和執(zhí)行。
接下來我將結(jié)合事件分發(fā)和Scroller來實(shí)現(xiàn)一個簡易版的ViewPager。首先自定義一個ViewGroup,不了解的可以參考Android自定義ViewGroup(一)之CustomGridLayout這篇文章。平滑偏移的主要做法如下:

第一、調(diào)用Scroller實(shí)例去產(chǎn)生一個偏移控制(對應(yīng)于startScroll()方法)
第二、手動調(diào)用invalid()方法去重新繪制,剩下的就是在computeScroll()里根據(jù)當(dāng)前已經(jīng)逝去的時間,獲取當(dāng)前應(yīng)該偏移的坐標(biāo)(由Scroller實(shí)例對應(yīng)的computeScrollOffset()計算而得)
第三、當(dāng)前應(yīng)該偏移的坐標(biāo),調(diào)用scrollBy()方法去緩慢移動至該坐標(biāo)處。

新建一個ScrollerLayout并讓它繼承自ViewGroup來作為我們的簡易ViewPager布局,代碼如下所示:

public class ScrollerLayout extends ViewGroup {

  private Scroller mScroller; //用于完成滾動操作的實(shí)例
  private VelocityTracker mVelocityTracker = null ; //處理觸摸的速率
  public static int SNAP_VELOCITY = 600 ; //最小的滑動速率
  private int mTouchSlop = 0 ;      //最小滑動距離,超過了,才認(rèn)為開始滑動
  private float mLastionMotionX = 0 ;  //上次觸發(fā)ACTION_MOVE事件時的屏幕坐標(biāo)
  private int curScreen = 0 ; //當(dāng)前屏幕
  private int leftBorder;  //界面可滾動的左邊界
  private int rightBorder; //界面可滾動的右邊界

  //兩種狀態(tài): 是否處于滑屏狀態(tài)
  private static final int TOUCH_STATE_REST = 0; //什么都沒做的狀態(tài)
  private static final int TOUCH_STATE_SCROLLING = 1; //開始滑屏的狀態(tài)
  private int mTouchState = TOUCH_STATE_REST; //默認(rèn)是什么都沒做的狀態(tài)

  public ScrollerLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    // 創(chuàng)建Scroller的實(shí)例
    mScroller = new Scroller(context);
    //初始化一個最小滑動距離
    mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
      View childView = getChildAt(i);
      // 為ScrollerLayout中的每一個子控件測量大小
      measureChild(childView, widthMeasureSpec, heightMeasureSpec);
    }
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if (changed) {
      int childCount = getChildCount();
      for (int i = 0; i < childCount; i++) {
        View childView = getChildAt(i);
        // 為ScrollerLayout中的每一個子控件在水平方向上進(jìn)行布局
        childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(), childView.getMeasuredHeight());
      }
    }
    // 初始化左右邊界值
    leftBorder = getChildAt(0).getLeft();
    rightBorder = getChildAt(getChildCount() - 1).getRight();
  }

  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {
    final int action = ev.getAction();
    //表示已經(jīng)開始滑動了,不需要走該Action_MOVE方法了(第一次時可能調(diào)用)。
    //該方法主要用于用戶快速松開手指,又快速按下的行為。此時認(rèn)為是處于滑屏狀態(tài)的。
    if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
      return true;
    }
    final float x = ev.getX();
    switch (action) {
      case MotionEvent.ACTION_MOVE:
        final int xDiff = (int) Math.abs(mLastionMotionX - x);
        //超過了最小滑動距離,就可以認(rèn)為開始滑動了
        if (xDiff > mTouchSlop) {
          mTouchState = TOUCH_STATE_SCROLLING;
        }
        break;
      case MotionEvent.ACTION_DOWN:
        mLastionMotionX = x;
        mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
        break;
      case MotionEvent.ACTION_CANCEL:
      case MotionEvent.ACTION_UP:
        mTouchState = TOUCH_STATE_REST;
        break;
    }
    return mTouchState != TOUCH_STATE_REST;
  }

  public boolean onTouchEvent(MotionEvent event){
    super.onTouchEvent(event);
    //獲得VelocityTracker對象,并且添加滑動對象
    if (mVelocityTracker == null) {
      mVelocityTracker = VelocityTracker.obtain();
    }
    mVelocityTracker.addMovement(event);
    //觸摸點(diǎn)
    float x = event.getX();
    switch(event.getAction()){
      case MotionEvent.ACTION_DOWN:
        //如果屏幕的動畫還沒結(jié)束,你就按下了,我們就結(jié)束上一次動畫,即開始這次新ACTION_DOWN的動畫
        if(mScroller != null){
          if(!mScroller.isFinished()){
            mScroller.abortAnimation();
          }
        }
        mLastionMotionX = x ; //記住開始落下的屏幕點(diǎn)
        break ;
      case MotionEvent.ACTION_MOVE:
        int detaX = (int)(mLastionMotionX - x ); //每次滑動屏幕,屏幕應(yīng)該移動的距離
        if (getScrollX() + detaX < leftBorder) {  //防止用戶拖出邊界這里還專門做了邊界保護(hù),當(dāng)拖出邊界時就調(diào)用scrollTo()方法來回到邊界位置
          scrollTo(leftBorder, 0);
          return true;
        } else if (getScrollX() + getWidth() + detaX > rightBorder) {
          scrollTo(rightBorder - getWidth(), 0);
          return true;
        }
        scrollBy(detaX, 0);//開始緩慢滑屏咯。 detaX > 0 向右滑動 , detaX < 0 向左滑動
        mLastionMotionX = x ;
        break ;
      case MotionEvent.ACTION_UP:
        final VelocityTracker velocityTracker = mVelocityTracker ;
        velocityTracker.computeCurrentVelocity(1000);
        //計算速率
        int velocityX = (int) velocityTracker.getXVelocity() ;
        //滑動速率達(dá)到了一個標(biāo)準(zhǔn)(快速向右滑屏,返回上一個屏幕) 馬上進(jìn)行切屏處理
        if (velocityX > SNAP_VELOCITY && curScreen > 0) {
          // Fling enough to move left
          snapToScreen(curScreen - 1);
        }
        //快速向左滑屏,返回下一個屏幕
        else if(velocityX < -SNAP_VELOCITY && curScreen < (getChildCount()-1)){
          snapToScreen(curScreen + 1);
        }
        //以上為快速移動的 ,強(qiáng)制切換屏幕
        else{
          //我們是緩慢移動的,因此先判斷是保留在本屏幕還是到下一屏幕
          snapToDestination();
        }
        //回收VelocityTracker對象
        if (mVelocityTracker != null) {
          mVelocityTracker.recycle();
          mVelocityTracker = null;
        }
        //修正mTouchState值
        mTouchState = TOUCH_STATE_REST ;
        break;
      case MotionEvent.ACTION_CANCEL:
        mTouchState = TOUCH_STATE_REST ;
        break;
    }
    return true ;
  }

  //我們是緩慢移動的,因此需要根據(jù)偏移值判斷目標(biāo)屏是哪個
  private void snapToDestination(){
    //判斷是否超過下一屏的中間位置,如果達(dá)到就抵達(dá)下一屏,否則保持在原屏幕
    //公式意思是:假設(shè)當(dāng)前滑屏偏移值即 scrollCurX 加上每個屏幕一半的寬度,除以每個屏幕的寬度就是我們目標(biāo)屏所在位置了。
    int destScreen = (getScrollX() + getWidth() / 2 ) / getWidth() ;
    snapToScreen(destScreen);
  }

  //真正的實(shí)現(xiàn)跳轉(zhuǎn)屏幕的方法
  private void snapToScreen(int whichScreen){
    //簡單的移到目標(biāo)屏幕,可能是當(dāng)前屏或者下一屏幕,直接跳轉(zhuǎn)過去,不太友好,為了友好性,我們在增加一個動畫效果
    curScreen = whichScreen ;
    //防止屏幕越界,即超過屏幕數(shù)
    if(curScreen > getChildCount() - 1)
      curScreen = getChildCount() - 1 ;
    //為了達(dá)到下一屏幕或者當(dāng)前屏幕,我們需要繼續(xù)滑動的距離.根據(jù)dx值,可能向左滑動,也可能向右滑動
    int dx = curScreen * getWidth() - getScrollX() ;
    mScroller.startScroll(getScrollX(), 0, dx, 0, Math.abs(dx) * 2);
    //由于觸摸事件不會重新繪制View,所以此時需要手動刷新View 否則沒效果
    invalidate();
  }

  @Override
  public void computeScroll() {
    //重寫computeScroll()方法,并在其內(nèi)部完成平滑滾動的邏輯
    if (mScroller.computeScrollOffset()) {
      scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
      invalidate();
    }
  }
}

代碼比較長,但思路比較清晰。
(1)首先在ScrollerLayout的構(gòu)造函數(shù)里面我們創(chuàng)建Scroller的實(shí)例,由于Scroller的實(shí)例只需創(chuàng)建一次,因此我們把它放到構(gòu)造函數(shù)里面執(zhí)行。另外在構(gòu)建函數(shù)中我們還初始化的TouchSlop的值,這個值在后面將用于判斷當(dāng)前用戶的操作是否是拖動。
(2)接著重寫onMeasure()方法和onLayout()方法,在onMeasure()方法中測量ScrollerLayout里的每一個子控件的大小,在onLayout()方法中為ScrollerLayout里的每一個子控件在水平方向上進(jìn)行布局,布局類似于方向?yàn)閔orizontal的LinearLayout。
(3) 接著重寫onInterceptTouchEvent()方法, 在這個方法中我們記錄了用戶手指按下時的X坐標(biāo)位置,以及用戶手指在屏幕上拖動時的X坐標(biāo)位置,當(dāng)兩者之間的距離大于TouchSlop值時,就認(rèn)為用戶正在拖動布局,置狀態(tài)為TOUCH_STATE_SCROLLING,當(dāng)用戶手指抬起,重置狀態(tài)為TOUCH_STATE_REST。這里當(dāng)狀態(tài)值為TOUCH_STATE_SCROLLING時返回true,將事件在這里攔截掉,阻止事件傳遞到子控件當(dāng)中。
(4)那么當(dāng)我們把事件攔截掉之后,就會將事件交給ScrollerLayout的onTouchEvent()方法來處理。
如果當(dāng)前事件是ACTION_MOVE,說明用戶正在拖動布局,那么我們就應(yīng)該對布局內(nèi)容進(jìn)行滾動從而影響拖動事件,實(shí)現(xiàn)的方式就是使用我們剛剛所學(xué)的scrollBy()方法,用戶拖動了多少這里就scrollBy多少。另外為了防止用戶拖出邊界這里還專門做了邊界保護(hù),當(dāng)拖出邊界時就調(diào)用scrollTo()方法來回到邊界位置。
如果當(dāng)前事件是ACTION_UP時,說明用戶手指抬起來了,但是目前很有可能用戶只是將布局拖動到了中間,我們不可能讓布局就這么停留在中間的位置,因此接下來就需要借助Scroller來完成后續(xù)的滾動操作。首先計算滾動速率,判斷當(dāng)前動作是scroll還是fling。如果是fling,再根據(jù)fling的方向跳轉(zhuǎn)到上一頁或者下一頁,調(diào)用函數(shù)snapToScreen。如果是scroll,就調(diào)用函數(shù)snapToDestination,函數(shù)中首先根據(jù)當(dāng)前的滾動位置來計算布局應(yīng)該繼續(xù)滾動到哪一頁,滾動到哪一頁同樣調(diào)用snapToScreen。再來看看snapToScreen寫法吧,其實(shí)是調(diào)用startScroll()方法來滾動數(shù)據(jù),緊接著調(diào)用invalidate()方法來刷新界面。
(5)重寫computeScroll()方法,并在其內(nèi)部完成平滑滾動的邏輯 。在整個后續(xù)的平滑滾動過程中,computeScroll()方法是會一直被調(diào)用的,因此我們需要不斷調(diào)用Scroller的computeScrollOffset()方法來進(jìn)行判斷滾動操作是否已經(jīng)完成了,如果還沒完成的話,那就繼續(xù)調(diào)用scrollTo()方法,并把Scroller的curX和curY坐標(biāo)傳入,然后刷新界面從而完成平滑滾動的操作。

現(xiàn)在ScrollerLayout已經(jīng)準(zhǔn)備好了,接下來我們修改activity_main.xml布局中的內(nèi)容,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<com.hx.scroller.ScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <ImageView
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:background="@drawable/crazy_1" />

  <ImageView
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:background="@drawable/crazy_2" />

  <ImageView
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:background="@drawable/crazy_3" />

</com.hx.scroller.ScrollerLayout>

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Android 更改 Toast 的默認(rèn)位置方法

    Android 更改 Toast 的默認(rèn)位置方法

    下面小編就為大家?guī)硪黄狝ndroid 更改 Toast 的默認(rèn)位置方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • Android用動畫顯示或隱藏視圖

    Android用動畫顯示或隱藏視圖

    大家好,本篇文章主要講的是Android用動畫顯示或隱藏視圖,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-01-01
  • Android制作漂亮自適布局鍵盤的方法

    Android制作漂亮自適布局鍵盤的方法

    最近做了個自定義鍵盤,但面對不同分辨率的機(jī)型其中數(shù)字鍵盤不能根據(jù)界面大小自已鋪滿,但又不能每種機(jī)型都做一套吧,所以要做成自適應(yīng),那這里主講思路,感興趣的朋友一起學(xué)習(xí)吧
    2015-12-12
  • Android中使用pull解析器操作xml文件的解決辦法

    Android中使用pull解析器操作xml文件的解決辦法

    本篇文章介紹了,在Android中使用pull解析器操作xml文件的解決辦法。需要的朋友參考下
    2013-04-04
  • Android開發(fā)中用Kotlin編寫LiveData組件教程

    Android開發(fā)中用Kotlin編寫LiveData組件教程

    LiveData是Jetpack組件的一部分,更多的時候是搭配ViewModel來使用,相對于Observable,LiveData的最大優(yōu)勢是其具有生命感知的,換句話說,LiveData可以保證只有在組件( Activity、Fragment、Service)處于活動生命周期狀態(tài)的時候才會更新數(shù)據(jù)
    2022-12-12
  • Unity同步/異步調(diào)用Android的方法實(shí)例

    Unity同步/異步調(diào)用Android的方法實(shí)例

    unity在Android端開發(fā)的時候,免不了要調(diào)用Java,下面這篇文章主要給大家介紹了關(guān)于Unity同步/異步調(diào)用Android的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-08-08
  • Android三種方式實(shí)現(xiàn)ProgressBar自定義圓形進(jìn)度條

    Android三種方式實(shí)現(xiàn)ProgressBar自定義圓形進(jìn)度條

    這篇文章主要介紹了Android三種方式實(shí)現(xiàn)ProgressBar自定義圓形進(jìn)度條的相關(guān)資料,需要的朋友可以參考下
    2016-03-03
  • Android自定義星星評分控件

    Android自定義星星評分控件

    這篇文章主要為大家詳細(xì)介紹了Android自定義星星評分控件的使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • Android基于ListView實(shí)現(xiàn)類似Market分頁加載效果示例

    Android基于ListView實(shí)現(xiàn)類似Market分頁加載效果示例

    這篇文章主要介紹了Android基于ListView實(shí)現(xiàn)類似Market分頁加載效果,結(jié)合完整實(shí)例形式分析了ListView的OnScroll方法來實(shí)現(xiàn)分頁與滾動加載的操作步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2016-10-10
  • Android Tab標(biāo)簽的使用基礎(chǔ)

    Android Tab標(biāo)簽的使用基礎(chǔ)

    Android程序中,Tab標(biāo)簽窗口是一種常用的UI界面元素。這篇文章主要介紹了Android Tab標(biāo)簽的使用基礎(chǔ),有興趣的可以了解一下。
    2016-12-12

最新評論

凤凰县| 都兰县| 安龙县| 横山县| 洛阳市| 渭南市| 凤山市| 武宁县| 石台县| 登封市| 平舆县| 洛阳市| 随州市| 玉山县| 开封市| 芦山县| 婺源县| 嵊州市| 阿拉善盟| 尖扎县| 东乡| 玉林市| 临武县| 婺源县| 成安县| 甘肃省| 浠水县| 安庆市| 姚安县| 金华市| 剑河县| 福建省| 唐河县| 连南| 卫辉市| 大同市| 广安市| 闸北区| 县级市| 抚松县| 蕲春县|