国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

XListView實現(xiàn)下拉刷新和上拉加載原理解析

2019-10-22 18:21:45
字體:
供稿:網(wǎng)友

XListview是一個非常受歡迎的下拉刷新控件,但是已經(jīng)停止維護了。之前寫過一篇XListview的使用介紹,用起來非常簡單,這兩天放假無聊,研究了下XListview的實現(xiàn)原理,學(xué)到了很多,今天分享給大家。

    提前聲明,為了讓代碼更好的理解,我對代碼進行了部分刪減和重構(gòu),如果大家想看原版代碼,請去github自行下載。

    Xlistview項目主要是三部分:XlistView,XListViewHeader,XListViewFooter,分別是XListView主體、header、footer的實現(xiàn)。下面我們分開來介紹。

    下面是修改之后的XListViewHeader代碼

public class XListViewHeader extends LinearLayout {    private static final String HINT_NORMAL = "下拉刷新";   private static final String HINT_READY = "松開刷新數(shù)據(jù)";   private static final String HINT_LOADING = "正在加載...";    // 正常狀態(tài)   public final static int STATE_NORMAL = 0;   // 準備刷新狀態(tài),也就是箭頭方向發(fā)生改變之后的狀態(tài)   public final static int STATE_READY = 1;   // 刷新狀態(tài),箭頭變成了progressBar   public final static int STATE_REFRESHING = 2;   // 布局容器,也就是根布局   private LinearLayout container;   // 箭頭圖片   private ImageView mArrowImageView;   // 刷新狀態(tài)顯示   private ProgressBar mProgressBar;   // 說明文本   private TextView mHintTextView;   // 記錄當前的狀態(tài)   private int mState;   // 用于改變箭頭的方向的動畫   private Animation mRotateUpAnim;   private Animation mRotateDownAnim;   // 動畫持續(xù)時間   private final int ROTATE_ANIM_DURATION = 180;    public XListViewHeader(Context context) {     super(context);     initView(context);   }    public XListViewHeader(Context context, AttributeSet attrs) {     super(context, attrs);     initView(context);   }    private void initView(Context context) {     mState = STATE_NORMAL;     // 初始情況下,設(shè)置下拉刷新view高度為0     LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(         LayoutParams.MATCH_PARENT, 0);     container = (LinearLayout) LayoutInflater.from(context).inflate(         R.layout.xlistview_header, null);     addView(container, lp);     // 初始化控件     mArrowImageView = (ImageView) findViewById(R.id.xlistview_header_arrow);     mHintTextView = (TextView) findViewById(R.id.xlistview_header_hint_textview);     mProgressBar = (ProgressBar) findViewById(R.id.xlistview_header_progressbar);     // 初始化動畫     mRotateUpAnim = new RotateAnimation(0.0f, -180.0f,         Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,         0.5f);     mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);     mRotateUpAnim.setFillAfter(true);     mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f,         Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,         0.5f);     mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);     mRotateDownAnim.setFillAfter(true);   }    // 設(shè)置header的狀態(tài)   public void setState(int state) {     if (state == mState)       return;      // 顯示進度     if (state == STATE_REFRESHING) {       mArrowImageView.clearAnimation();       mArrowImageView.setVisibility(View.INVISIBLE);       mProgressBar.setVisibility(View.VISIBLE);     } else {       // 顯示箭頭       mArrowImageView.setVisibility(View.VISIBLE);       mProgressBar.setVisibility(View.INVISIBLE);     }      switch (state) {     case STATE_NORMAL:       if (mState == STATE_READY) {         mArrowImageView.startAnimation(mRotateDownAnim);       }       if (mState == STATE_REFRESHING) {         mArrowImageView.clearAnimation();       }       mHintTextView.setText(HINT_NORMAL);       break;     case STATE_READY:       if (mState != STATE_READY) {         mArrowImageView.clearAnimation();         mArrowImageView.startAnimation(mRotateUpAnim);         mHintTextView.setText(HINT_READY);       }       break;     case STATE_REFRESHING:       mHintTextView.setText(HINT_LOADING);       break;     }      mState = state;   }    public void setVisiableHeight(int height) {     if (height < 0)       height = 0;     LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) container         .getLayoutParams();     lp.height = height;     container.setLayoutParams(lp);   }    public int getVisiableHeight() {     return container.getHeight();   }    public void show() {     container.setVisibility(View.VISIBLE);   }    public void hide() {     container.setVisibility(View.INVISIBLE);   }  } 

    XListViewHeader繼承自linearLayout,用來實現(xiàn)下拉刷新時的界面展示,可以分為三種狀態(tài):正常、準備刷新、正在加載。
    在Linearlayout布局里面,主要有指示箭頭、說明文本、圓形加載條三個控件。在構(gòu)造函數(shù)中,調(diào)用了initView()進行控件的初始化操作。在添加布局文件的時候,指定高度為0,這是為了隱藏header,然后初始化動畫,是為了完成箭頭的旋轉(zhuǎn)動作。
    setState()是設(shè)置header的狀態(tài),因為header需要根據(jù)不同的狀態(tài),完成控件隱藏、顯示、改變文字等操作,這個方法主要是在XListView里面調(diào)用。除此之外,還有setVisiableHeight()和getVisiableHeight(),這兩個方法是為了設(shè)置和獲取Header中根布局文件的高度屬性,從而完成拉伸和收縮的效果,而show()和hide()則顯然就是完成顯示和隱藏的效果。
    下面是Header的布局文件

<?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:layout_width="match_parent"   android:layout_height="wrap_content"   android:gravity="bottom" >    <RelativeLayout     android:id="@+id/xlistview_header_content"     android:layout_width="match_parent"     android:layout_height="60dp"     tools:ignore="UselessParent" >      <TextView       android:id="@+id/xlistview_header_hint_textview"       android:layout_width="100dp"       android:layout_height="wrap_content"       android:layout_centerInParent="true"       android:gravity="center"       android:text="正在加載"       android:textColor="@android:color/black"       android:textSize="14sp" />      <ImageView       android:id="@+id/xlistview_header_arrow"       android:layout_width="30dp"       android:layout_height="wrap_content"       android:layout_centerVertical="true"       android:layout_toLeftOf="@id/xlistview_header_hint_textview"       android:src="@drawable/xlistview_arrow" />      <ProgressBar       android:id="@+id/xlistview_header_progressbar"       style="@style/progressbar_style"       android:layout_width="30dp"       android:layout_height="30dp"       android:layout_centerVertical="true"       android:layout_toLeftOf="@id/xlistview_header_hint_textview"       android:visibility="invisible" />   </RelativeLayout>  </LinearLayout> 

    說完了Header,我們再看看Footer。Footer是為了完成加載更多功能時候的界面展示,基本思路和Header是一樣的,下面是Footer的代碼

public class XListViewFooter extends LinearLayout {    // 正常狀態(tài)   public final static int STATE_NORMAL = 0;   // 準備狀態(tài)   public final static int STATE_READY = 1;   // 加載狀態(tài)   public final static int STATE_LOADING = 2;    private View mContentView;   private View mProgressBar;   private TextView mHintView;    public XListViewFooter(Context context) {     super(context);     initView(context);   }    public XListViewFooter(Context context, AttributeSet attrs) {     super(context, attrs);     initView(context);   }    private void initView(Context context) {      LinearLayout moreView = (LinearLayout) LayoutInflater.from(context)         .inflate(R.layout.xlistview_footer, null);     addView(moreView);     moreView.setLayoutParams(new LinearLayout.LayoutParams(         LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));      mContentView = moreView.findViewById(R.id.xlistview_footer_content);     mProgressBar = moreView.findViewById(R.id.xlistview_footer_progressbar);     mHintView = (TextView) moreView         .findViewById(R.id.xlistview_footer_hint_textview);   }    /**    * 設(shè)置當前的狀態(tài)    *    * @param state    */   public void setState(int state) {      mProgressBar.setVisibility(View.INVISIBLE);     mHintView.setVisibility(View.INVISIBLE);      switch (state) {     case STATE_READY:       mHintView.setVisibility(View.VISIBLE);       mHintView.setText(R.string.xlistview_footer_hint_ready);       break;      case STATE_NORMAL:       mHintView.setVisibility(View.VISIBLE);       mHintView.setText(R.string.xlistview_footer_hint_normal);       break;      case STATE_LOADING:       mProgressBar.setVisibility(View.VISIBLE);       break;      }    }    public void setBottomMargin(int height) {     if (height > 0) {        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView           .getLayoutParams();       lp.bottomMargin = height;       mContentView.setLayoutParams(lp);     }   }    public int getBottomMargin() {     LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView         .getLayoutParams();     return lp.bottomMargin;   }    public void hide() {     LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView         .getLayoutParams();     lp.height = 0;     mContentView.setLayoutParams(lp);   }    public void show() {     LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContentView         .getLayoutParams();     lp.height = LayoutParams.WRAP_CONTENT;     mContentView.setLayoutParams(lp);   }  } 

    從上面的代碼里面,我們可以看出,footer和header的思路是一樣的,只不過,footer的拉伸和顯示效果不是通過高度來模擬的,而是通過設(shè)置BottomMargin來完成的。
    下面是Footer的布局文件 

<?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:layout_width="fill_parent"   android:layout_height="wrap_content" >    <RelativeLayout     android:id="@+id/xlistview_footer_content"     android:layout_width="fill_parent"     android:layout_height="wrap_content"     android:padding="5dp"     tools:ignore="UselessParent" >      <ProgressBar       android:id="@+id/xlistview_footer_progressbar"       style="@style/progressbar_style"       android:layout_width="30dp"       android:layout_height="30dp"       android:layout_centerInParent="true"       android:visibility="invisible" />      <TextView       android:id="@+id/xlistview_footer_hint_textview"       android:layout_width="wrap_content"       android:layout_height="wrap_content"       android:layout_centerInParent="true"       android:text="@string/xlistview_footer_hint_normal"       android:textColor="@android:color/black"       android:textSize="14sp" />   </RelativeLayout>  </LinearLayout> 

    在了解了Header和footer之后,我們就要介紹最核心的XListView的代碼實現(xiàn)了。
    在介紹代碼實現(xiàn)之前,我先介紹一下XListView的實現(xiàn)原理。
    首先,一旦使用XListView,F(xiàn)ooter和Header就已經(jīng)添加到我們的ListView上面了,XListView就是通過繼承ListView,然后處理了屏幕點擊事件和控制滑動實現(xiàn)效果的。所以,如果我們的Adapter中g(shù)etCount()返回的值是20,那么其實XListView里面是有20+2個item的,這個數(shù)量即使我們關(guān)閉了XListView的刷新和加載功能,也是不會變化的。Header和Footer通過addHeaderView和addFooterView添加上去之后,如果想實現(xiàn)下拉刷新和上拉加載功能,那么就必須有拉伸效果,所以就像上面的那樣,Header是通過設(shè)置height,F(xiàn)ooter是通過設(shè)置BottomMargin來模擬拉伸效果。那么回彈效果呢?僅僅通過設(shè)置高度或者是間隔是達不到模擬回彈效果的,因此,就需要用Scroller來實現(xiàn)模擬回彈效果。在說明原理之后,我們開始介紹XListView的核心實現(xiàn)原理。
    再次提示,下面的代碼經(jīng)過我重構(gòu)了,只是為了看起來更好的理解。

public class XListView extends ListView {    private final static int SCROLLBACK_HEADER = 0;   private final static int SCROLLBACK_FOOTER = 1;   // 滑動時長   private final static int SCROLL_DURATION = 400;   // 加載更多的距離   private final static int PULL_LOAD_MORE_DELTA = 100;   // 滑動比例   private final static float OFFSET_RADIO = 2f;   // 記錄按下點的y坐標   private float lastY;   // 用來回滾   private Scroller scroller;   private IXListViewListener mListViewListener;   private XListViewHeader headerView;   private RelativeLayout headerViewContent;   // header的高度   private int headerHeight;   // 是否能夠刷新   private boolean enableRefresh = true;   // 是否正在刷新   private boolean isRefreashing = false;   // footer   private XListViewFooter footerView;   // 是否可以加載更多   private boolean enableLoadMore;   // 是否正在加載   private boolean isLoadingMore;   // 是否footer準備狀態(tài)   private boolean isFooterAdd = false;   // total list items, used to detect is at the bottom of listview.   private int totalItemCount;   // 記錄是從header還是footer返回   private int mScrollBack;    private static final String TAG = "XListView";    public XListView(Context context) {     super(context);     initView(context);   }    public XListView(Context context, AttributeSet attrs) {     super(context, attrs);     initView(context);   }    public XListView(Context context, AttributeSet attrs, int defStyle) {     super(context, attrs, defStyle);     initView(context);   }    private void initView(Context context) {      scroller = new Scroller(context, new DecelerateInterpolator());      headerView = new XListViewHeader(context);     footerView = new XListViewFooter(context);      headerViewContent = (RelativeLayout) headerView         .findViewById(R.id.xlistview_header_content);     headerView.getViewTreeObserver().addOnGlobalLayoutListener(         new OnGlobalLayoutListener() {           @SuppressWarnings("deprecation")           @Override           public void onGlobalLayout() {             headerHeight = headerViewContent.getHeight();             getViewTreeObserver()                 .removeGlobalOnLayoutListener(this);           }         });     addHeaderView(headerView);    }    @Override   public void setAdapter(ListAdapter adapter) {     // 確保footer最后添加并且只添加一次     if (isFooterAdd == false) {       isFooterAdd = true;       addFooterView(footerView);     }     super.setAdapter(adapter);    }    @Override   public boolean onTouchEvent(MotionEvent ev) {      totalItemCount = getAdapter().getCount();     switch (ev.getAction()) {     case MotionEvent.ACTION_DOWN:       // 記錄按下的坐標       lastY = ev.getRawY();       break;     case MotionEvent.ACTION_MOVE:       // 計算移動距離       float deltaY = ev.getRawY() - lastY;       lastY = ev.getRawY();       // 是第一項并且標題已經(jīng)顯示或者是在下拉       if (getFirstVisiblePosition() == 0           && (headerView.getVisiableHeight() > 0 || deltaY > 0)) {         updateHeaderHeight(deltaY / OFFSET_RADIO);       } else if (getLastVisiblePosition() == totalItemCount - 1           && (footerView.getBottomMargin() > 0 || deltaY < 0)) {         updateFooterHeight(-deltaY / OFFSET_RADIO);       }       break;      case MotionEvent.ACTION_UP:        if (getFirstVisiblePosition() == 0) {         if (enableRefresh             && headerView.getVisiableHeight() > headerHeight) {           isRefreashing = true;           headerView.setState(XListViewHeader.STATE_REFRESHING);           if (mListViewListener != null) {             mListViewListener.onRefresh();           }         }         resetHeaderHeight();       } else if (getLastVisiblePosition() == totalItemCount - 1) {         if (enableLoadMore             && footerView.getBottomMargin() > PULL_LOAD_MORE_DELTA) {           startLoadMore();         }         resetFooterHeight();       }       break;     }     return super.onTouchEvent(ev);   }    @Override   public void computeScroll() {      // 松手之后調(diào)用     if (scroller.computeScrollOffset()) {        if (mScrollBack == SCROLLBACK_HEADER) {         headerView.setVisiableHeight(scroller.getCurrY());       } else {         footerView.setBottomMargin(scroller.getCurrY());       }       postInvalidate();     }     super.computeScroll();    }    public void setPullRefreshEnable(boolean enable) {     enableRefresh = enable;      if (!enableRefresh) {       headerView.hide();     } else {       headerView.show();     }   }    public void setPullLoadEnable(boolean enable) {     enableLoadMore = enable;     if (!enableLoadMore) {       footerView.hide();       footerView.setOnClickListener(null);     } else {       isLoadingMore = false;       footerView.show();       footerView.setState(XListViewFooter.STATE_NORMAL);       footerView.setOnClickListener(new OnClickListener() {         @Override         public void onClick(View v) {           startLoadMore();         }       });     }   }    public void stopRefresh() {     if (isRefreashing == true) {       isRefreashing = false;       resetHeaderHeight();     }   }    public void stopLoadMore() {     if (isLoadingMore == true) {       isLoadingMore = false;       footerView.setState(XListViewFooter.STATE_NORMAL);     }   }    private void updateHeaderHeight(float delta) {     headerView.setVisiableHeight((int) delta         + headerView.getVisiableHeight());     // 未處于刷新狀態(tài),更新箭頭     if (enableRefresh && !isRefreashing) {       if (headerView.getVisiableHeight() > headerHeight) {         headerView.setState(XListViewHeader.STATE_READY);       } else {         headerView.setState(XListViewHeader.STATE_NORMAL);       }     }    }    private void resetHeaderHeight() {     // 當前的可見高度     int height = headerView.getVisiableHeight();     // 如果正在刷新并且高度沒有完全展示     if ((isRefreashing && height <= headerHeight) || (height == 0)) {       return;     }     // 默認會回滾到header的位置     int finalHeight = 0;     // 如果是正在刷新狀態(tài),則回滾到header的高度     if (isRefreashing && height > headerHeight) {       finalHeight = headerHeight;     }     mScrollBack = SCROLLBACK_HEADER;     // 回滾到指定位置     scroller.startScroll(0, height, 0, finalHeight - height,         SCROLL_DURATION);     // 觸發(fā)computeScroll     invalidate();   }    private void updateFooterHeight(float delta) {     int height = footerView.getBottomMargin() + (int) delta;     if (enableLoadMore && !isLoadingMore) {       if (height > PULL_LOAD_MORE_DELTA) {         footerView.setState(XListViewFooter.STATE_READY);       } else {         footerView.setState(XListViewFooter.STATE_NORMAL);       }     }     footerView.setBottomMargin(height);    }    private void resetFooterHeight() {     int bottomMargin = footerView.getBottomMargin();     if (bottomMargin > 0) {       mScrollBack = SCROLLBACK_FOOTER;       scroller.startScroll(0, bottomMargin, 0, -bottomMargin,           SCROLL_DURATION);       invalidate();     }   }    private void startLoadMore() {     isLoadingMore = true;     footerView.setState(XListViewFooter.STATE_LOADING);     if (mListViewListener != null) {       mListViewListener.onLoadMore();     }   }    public void setXListViewListener(IXListViewListener l) {     mListViewListener = l;   }    public interface IXListViewListener {      public void onRefresh();      public void onLoadMore();   } } 

    在三個構(gòu)造函數(shù)中,都調(diào)用initView進行了header和footer的初始化,并且定義了一個Scroller,并傳入了一個減速的插值器,為了模仿回彈效果。在initView方法里面,因為header可能還沒初始化完畢,所以通過GlobalLayoutlistener來獲取了header的高度,然后addHeaderView添加到了listview上面。
    通過重寫setAdapter方法,保證Footer最后天假,并且只添加一次。
    最重要的,要屬onTouchEvent了。在方法開始之前,通過getAdapter().getCount()獲取到了item的總數(shù),便于計算位置。這個操作在源代碼中是通過scrollerListener完成的,因為ScrollerListener在這里沒大有用,所以我直接去掉了,然后把位置改到了這里。如果在setAdapter里面獲取的話,只能獲取到?jīng)]有header和footer的item數(shù)量。
    在ACTION_DOWN里面,進行了lastY的初始化,lastY是為了判斷移動方向的,因為在ACTION_MOVE里面,通過ev.getRawY()-lastY可以計算出手指的移動趨勢,如果>0,那么就是向下滑動,反之向上。getRowY()是獲取元Y坐標,意思就是和Window和View坐標沒有關(guān)系的坐標,代表在屏幕上的絕對位置。然后在下面的代碼里面,如果第一項可見并且header的可見高度>0或者是向下滑動,就說明用戶在向下拉動或者是向上拉動header,也就是指示箭頭顯示的時候的狀態(tài),這時候調(diào)用了updateHeaderHeight,來更新header的高度,實現(xiàn)header可以跟隨手指動作上下移動。這里有個OFFSET_RADIO,這個值是一個移動比例,就是說,你手指在Y方向上移動400px,如果比例是2,那么屏幕上的控件移動就是400px/2=200px,可以通過這個值來控制用戶的滑動體驗。下面的關(guān)于footer的判斷與此類似,不再贅述。
   當用戶移開手指之后,ACTION_UP方法就會被調(diào)用。在這里面,只對可見位置是0和item總數(shù)-1的位置進行了處理,其實正好對應(yīng)header和footer。如果位置是0,并且可以刷新,然后當前的header可見高度>原始高度的話,就說明用戶確實是要進行刷新操作,所以通過setState改變header的狀態(tài),如果有監(jiān)聽器的話,就調(diào)用onRefresh方法,然后調(diào)用resetHeaderHeight初始化header的狀態(tài),因為footer的操作如出一轍,所以不再贅述。但是在footer中有一個PULL_LOAD_MORE_DELTA,這個值是加載更多觸發(fā)條件的臨界值,只有footer的間隔超過這個值之后,才能夠觸發(fā)加載更多的功能,因此我們可以修改這個值來改變用戶體驗。
    說到現(xiàn)在,大家應(yīng)該明白基本的原理了,其實XListView就是通過對用戶手勢的方向和距離的判斷,來動態(tài)的改變Header和Footer實現(xiàn)的功能,所以如果我們也有類似的需求,就可以參照這種思路進行自定義。
    下面再說幾個比較重要的方法。
    前面我們說道,在ACTION_MOVE里面,會不斷的調(diào)用下面的updateXXXX方法,來動態(tài)的改變header和fooer的狀態(tài),

private void updateHeaderHeight(float delta) {     headerView.setVisiableHeight((int) delta         + headerView.getVisiableHeight());     // 未處于刷新狀態(tài),更新箭頭     if (enableRefresh && !isRefreashing) {       if (headerView.getVisiableHeight() > headerHeight) {         headerView.setState(XListViewHeader.STATE_READY);       } else {         headerView.setState(XListViewHeader.STATE_NORMAL);       }     }    }  private void updateFooterHeight(float delta) {     int height = footerView.getBottomMargin() + (int) delta;     if (enableLoadMore && !isLoadingMore) {       if (height > PULL_LOAD_MORE_DELTA) {         footerView.setState(XListViewFooter.STATE_READY);       } else {         footerView.setState(XListViewFooter.STATE_NORMAL);       }     }     footerView.setBottomMargin(height);    } 

    在移開手指之后,會調(diào)用下面的resetXXX來初始化header和footer的狀態(tài)

private void resetHeaderHeight() {     // 當前的可見高度     int height = headerView.getVisiableHeight();     // 如果正在刷新并且高度沒有完全展示     if ((isRefreashing && height <= headerHeight) || (height == 0)) {       return;     }     // 默認會回滾到header的位置     int finalHeight = 0;     // 如果是正在刷新狀態(tài),則回滾到header的高度     if (isRefreashing && height > headerHeight) {       finalHeight = headerHeight;     }     mScrollBack = SCROLLBACK_HEADER;     // 回滾到指定位置     scroller.startScroll(0, height, 0, finalHeight - height,         SCROLL_DURATION);     // 觸發(fā)computeScroll     invalidate();   }  private void resetFooterHeight() {     int bottomMargin = footerView.getBottomMargin();     if (bottomMargin > 0) {       mScrollBack = SCROLLBACK_FOOTER;       scroller.startScroll(0, bottomMargin, 0, -bottomMargin,           SCROLL_DURATION);       invalidate();     }   } 

    我們可以看到,滾動操作不是通過直接的設(shè)置高度來實現(xiàn)的,而是通過Scroller.startScroll()來實現(xiàn)的,通過調(diào)用此方法,computeScroll()就會被調(diào)用,然后在這個里面,根據(jù)mScrollBack區(qū)分是哪一個滾動,然后再通過設(shè)置高度和間隔,就可以完成收縮的效果了。
    至此,整個XListView的實現(xiàn)原理就完全的搞明白了,以后如果做滾動類的自定義控件,應(yīng)該也有思路了。

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


注:相關(guān)教程知識閱讀請移步到Android開發(fā)頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 积石山| 汶上县| 临朐县| 清水河县| 镇坪县| 大冶市| 大安市| 慈利县| 昌乐县| 南木林县| 德惠市| 常熟市| 武邑县| 深泽县| 阳原县| 大化| 潞城市| 东海县| 太谷县| 尉犁县| 临沂市| 防城港市| 花垣县| 阳新县| 固阳县| 黑河市| 苏尼特左旗| 都江堰市| 宜宾县| 政和县| 荆州市| 靖江市| 万全县| 晋宁县| 广元市| 大石桥市| 桐庐县| 方城县| 遂平县| 镇江市| 金平|