中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

XListView如何實現下拉刷新和上拉加載

發布時間:2021-08-05 14:09:53 來源:億速云 閱讀:116 作者:小新 欄目:移動開發

這篇文章主要介紹XListView如何實現下拉刷新和上拉加載,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

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

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

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

    下面是修改之后的XListViewHeader代碼

public class XListViewHeader extends LinearLayout { 
 
  private static final String HINT_NORMAL = "下拉刷新"; 
  private static final String HINT_READY = "松開刷新數據"; 
  private static final String HINT_LOADING = "正在加載..."; 
 
  // 正常狀態 
  public final static int STATE_NORMAL = 0; 
  // 準備刷新狀態,也就是箭頭方向發生改變之后的狀態 
  public final static int STATE_READY = 1; 
  // 刷新狀態,箭頭變成了progressBar 
  public final static int STATE_REFRESHING = 2; 
  // 布局容器,也就是根布局 
  private LinearLayout container; 
  // 箭頭圖片 
  private ImageView mArrowImageView; 
  // 刷新狀態顯示 
  private ProgressBar mProgressBar; 
  // 說明文本 
  private TextView mHintTextView; 
  // 記錄當前的狀態 
  private int mState; 
  // 用于改變箭頭的方向的動畫 
  private Animation mRotateUpAnim; 
  private Animation mRotateDownAnim; 
  // 動畫持續時間 
  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; 
    // 初始情況下,設置下拉刷新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); 
  } 
 
  // 設置header的狀態 
  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,用來實現下拉刷新時的界面展示,可以分為三種狀態:正常、準備刷新、正在加載。
    在Linearlayout布局里面,主要有指示箭頭、說明文本、圓形加載條三個控件。在構造函數中,調用了initView()進行控件的初始化操作。在添加布局文件的時候,指定高度為0,這是為了隱藏header,然后初始化動畫,是為了完成箭頭的旋轉動作。
    setState()是設置header的狀態,因為header需要根據不同的狀態,完成控件隱藏、顯示、改變文字等操作,這個方法主要是在XListView里面調用。除此之外,還有setVisiableHeight()和getVisiableHeight(),這兩個方法是為了設置和獲取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" 
       
      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 { 
 
  // 正常狀態 
  public final static int STATE_NORMAL = 0; 
  // 準備狀態 
  public final static int STATE_READY = 1; 
  // 加載狀態 
  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); 
  } 
 
  /** 
   * 設置當前的狀態 
   * 
   * @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的拉伸和顯示效果不是通過高度來模擬的,而是通過設置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" 
       
      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的代碼實現了。
    在介紹代碼實現之前,我先介紹一下XListView的實現原理。
    首先,一旦使用XListView,Footer和Header就已經添加到我們的ListView上面了,XListView就是通過繼承ListView,然后處理了屏幕點擊事件和控制滑動實現效果的。所以,如果我們的Adapter中getCount()返回的值是20,那么其實XListView里面是有20+2個item的,這個數量即使我們關閉了XListView的刷新和加載功能,也是不會變化的。Header和Footer通過addHeaderView和addFooterView添加上去之后,如果想實現下拉刷新和上拉加載功能,那么就必須有拉伸效果,所以就像上面的那樣,Header是通過設置height,Footer是通過設置BottomMargin來模擬拉伸效果。那么回彈效果呢?僅僅通過設置高度或者是間隔是達不到模擬回彈效果的,因此,就需要用Scroller來實現模擬回彈效果。在說明原理之后,我們開始介紹XListView的核心實現原理。
    再次提示,下面的代碼經過我重構了,只是為了看起來更好的理解。

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準備狀態 
  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(); 
      // 是第一項并且標題已經顯示或者是在下拉 
      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() { 
 
    // 松手之后調用 
    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()); 
    // 未處于刷新狀態,更新箭頭 
    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; 
    // 如果是正在刷新狀態,則回滾到header的高度 
    if (isRefreashing && height > headerHeight) { 
      finalHeight = headerHeight; 
    } 
    mScrollBack = SCROLLBACK_HEADER; 
    // 回滾到指定位置 
    scroller.startScroll(0, height, 0, finalHeight - height, 
        SCROLL_DURATION); 
    // 觸發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(); 
  } 
}

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

private void updateHeaderHeight(float delta) { 
    headerView.setVisiableHeight((int) delta 
        + headerView.getVisiableHeight()); 
    // 未處于刷新狀態,更新箭頭 
    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); 
 
  }

    在移開手指之后,會調用下面的resetXXX來初始化header和footer的狀態

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

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

以上是“XListView如何實現下拉刷新和上拉加載”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

马鞍山市| 海林市| 铁岭市| 连江县| 苏尼特右旗| 宕昌县| 北海市| 鹤庆县| 师宗县| 丹棱县| 闽侯县| 大邑县| 高平市| 醴陵市| 开原市| 通城县| 安西县| 杭锦后旗| 那坡县| 昌黎县| 龙游县| 宜宾市| 平塘县| 广河县| 河间市| 嵊泗县| 岳西县| 宁南县| 宝坻区| 当涂县| 大丰市| 防城港市| 汉中市| 江川县| 新源县| 合作市| 盐津县| 民权县| 正定县| 丰顺县| 高雄市|