編輯:關於Android編程
listview經常結合下來刷新和上拉加載更多使用,本文總結了三種常用到的方案分別作出說明。
方案一:添加頭布局和腳布局
android系統為listview提供了addfootview和addheadview兩個API。這樣可以直接自定義一個View,以添加視圖的形式實現下來刷新和上拉加載。
實現步驟
1、創建一個類繼承ListView:class PullToRefreshListView extends ListView;
2、在構造方法中添加HeadView:addHeaderView(headView);
3、獲取HeadView的高。測量控件的高可以有兩方法getMeasuredHeight和getHeight,getMeasuredHeight()在onMeasure方法執行之後才能獲取到;getHeight() 在onLayout方法執行之後才能獲取到值;
4、顯示和隱藏headView,通過setpadding實現,當向下滑,且第一條可見item是第0條的時候才需要設置HeadView的paddingTop來顯示HeadView。
顯示:headView.setPadding(0,0,0,0);
隱藏:headView.setPadding(0,-headViewHeight,0,0);
5、下拉刷新三種狀態的判斷,移動的時候,當paddingTop < 0 的時候,說明HeadView沒有完全顯示出來,進入下拉刷新狀態;移動的時候,當paddingTop >= 0 的時候, 說明HeadView已經完全顯示出來了,進入松開以新狀態;手指抬起的時候,且當前狀態是松開刷新狀態的時候,進入正在刷新狀態; 當已經是“正在刷新”狀態時, 則不允許再做”下拉刷新”和”松開刷新”的操作了,在Move事件中加入判斷,如果已經是正在刷新狀態了,則不處理下拉的操作了。
6、下拉箭頭的轉動。下拉刷新是向下,松開刷新時向上。旋轉動畫通過屬性動畫實現。隱藏箭頭的時候要清除動畫:iv_arrow.clearAnimation(); 如果不隱藏動畫效果,設置View.GONE之後還是看得見的。
7、HeadView顯示時,當手指松開時的處理,松開時如果是“正在刷新”狀態,則把headVie完全顯示;松開時如果是“下拉刷新”狀態,則把HeadView完全隱藏
8、增加FooterView:addFooterView(footerView)。當ListView處於空閒狀態,並且最後一條可見item是ListView中的最後一條數據時顯示footview, footerView顯示出來後,ListView不會自動上滑把FooterView顯示出來的,所以需要手動設置:setSelection(getCount() - 1);即選中最後一條。
9、增加回調監聽器。當ListView處於刷新狀態的時候會調用onRefreshing()方法;當ListView處於加載更多的時候會調用onLoadMore()。加載完成後通知控件加載完成。
具體實現:
import com.itheima.pulltorefreshlistview.R; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.animation.RotateAnimation; import android.widget.AbsListView; import android.widget.ImageView; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; public class PullToRefreshListView extends ListView { private View headerView; private float downY; private int headerViewHeight; /** 狀態:下拉刷新 */ private static final int STATE_PULL_TO_REFRESH = 0; /** 狀態:松開刷新 */ private static final int STATE_RELEASE_REFRESH = 1; /** 狀態:正在刷新 */ private static final int STATE_REFRESHING = 2; /** 當前狀態 */ private int currentState = STATE_PULL_TO_REFRESH; // 默認是下拉刷新狀態 private ImageView iv_arrow; private ProgressBar progress_bar; private TextView tv_state; private RotateAnimation upAnim; private RotateAnimation downAnim; private OnRefreshingListener mOnRefreshingListener; private View footerView; private int footerViewHeight; /** 正在加載更多 */ private boolean loadingMore; public PullToRefreshListView(Context context, AttributeSet attrs) { super(context, attrs); initHeaderView(); initFooterView(); } private void initHeaderView() { headerView = View.inflate(getContext(), R.layout.header_view, null); iv_arrow = (ImageView) headerView.findViewById(R.id.iv_arrow); progress_bar = (ProgressBar) headerView.findViewById(R.id.progress_bar); showRefreshingProgressBar(false); tv_state = (TextView) headerView.findViewById(R.id.tv_state); headerView.measure(0, 0); // 主動觸發測量,mesure內部會調用onMeasure headerViewHeight = headerView.getMeasuredHeight(); hideHeaderView(); super.addHeaderView(headerView); upAnim = createRotateAnim(0f, -180f); downAnim = createRotateAnim(-180f, -360f); } private void initFooterView() { footerView = View.inflate(getContext(), R.layout.footer_view, null); footerView.measure(0, 0);// 主動觸發測量,mesure內部會調用onMeasure footerViewHeight = footerView.getMeasuredHeight(); hideFooterView(); super.addFooterView(footerView); super.setOnScrollListener(new OnScrollListener() { // 當ListView滾動的狀態發生改變的時候會調用這個方法 @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == OnScrollListener.SCROLL_STATE_IDLE // ListView處於空閒狀態 && getLastVisiblePosition() == getCount() - 1 // 界面上可見的最後一條item是ListView中最後的一條item && loadingMore == false // 如果當前沒有去做正在加載更多的事情 ) { loadingMore = true; showFooterView(); setSelection(getCount() - 1); if (mOnRefreshingListener != null) { mOnRefreshingListener.onLoadMore(); } } } // 當ListView滾動的時候會調用這個方法 @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }); } private void hideFooterView() { int paddingTop = -footerViewHeight; setFooterViewPaddingTop(paddingTop); } private void showFooterView() { int paddingTop = 0; setFooterViewPaddingTop(paddingTop); } private void setFooterViewPaddingTop(int paddingTop) { footerView.setPadding(0, paddingTop, 0, 0); } /** * 設置顯示進度的圈圈 * @param showProgressBar 如果是true,則顯示ProgressBar,否則的話顯示箭頭 */ private void showRefreshingProgressBar(boolean showProgressBar) { progress_bar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE); iv_arrow.setVisibility(!showProgressBar ? View.VISIBLE : View.GONE); if (showProgressBar) { iv_arrow.clearAnimation(); // 有動畫的View要清除動畫才能真正的隱藏 } } /** * 創建旋轉動畫 * @param fromDegrees 從哪個角度開始轉 * @param toDegrees 轉到哪個角度 * @return */ private RotateAnimation createRotateAnim(float fromDegrees, float toDegrees) { int pivotXType = RotateAnimation.RELATIVE_TO_SELF; // 旋轉點的參照物 int pivotYType = RotateAnimation.RELATIVE_TO_SELF; // 旋轉點的參照物 float pivotXValue = 0.5f; // 旋轉點x方向的位置 float pivotYValue = 0.5f; // 旋轉點y方向的位置 RotateAnimation ra = new RotateAnimation(fromDegrees, toDegrees, pivotXType, pivotXValue, pivotYType, pivotYValue); ra.setDuration(300); ra.setFillAfter(true); // 讓動畫停留在結束位置 return ra; } /** 隱藏HeaderView */ private void hideHeaderView() { int paddingTop = -headerViewHeight; setHeaderViewPaddingTop(paddingTop); } /** 顯示HeaderView */ private void showHeaderView() { int paddingTop = 0; setHeaderViewPaddingTop(paddingTop); } /** * 設置HeaderView的paddingTop * @param paddingTop */ private void setHeaderViewPaddingTop(int paddingTop) { headerView.setPadding(0, paddingTop, 0, 0); } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: downY = ev.getY(); break; case MotionEvent.ACTION_MOVE: if (currentState == STATE_REFRESHING) { // 如果當前已經是“正在刷新“的狀態了,則不用去處理下拉刷新了 return super.onTouchEvent(ev); } int fingerMoveDistanceY = (int) (ev.getY() - downY); // 手指移動的距離 // 如果是向下滑動,並且界面上可見的第一條item是ListView的索引為0的item時我們才處理下拉刷新的操作 if (fingerMoveDistanceY > 0 && getFirstVisiblePosition() == 0) { int paddingTop = -headerViewHeight + fingerMoveDistanceY; setHeaderViewPaddingTop(paddingTop); if (paddingTop < 0 && currentState != STATE_PULL_TO_REFRESH) { // 如果paddingTop小於0,說明HeaderView沒有完全顯示出來,則進入下拉刷新的狀態 currentState = STATE_PULL_TO_REFRESH; tv_state.setText("下拉刷新"); iv_arrow.startAnimation(downAnim); showRefreshingProgressBar(false); // 讓箭頭轉一下 } else if (paddingTop >= 0 && currentState != STATE_RELEASE_REFRESH) { // 如果paddingTop>=0,說明HeaderView已經完全顯示出來,則進入松開刷新的狀態 currentState = STATE_RELEASE_REFRESH; tv_state.setText("松開刷新"); iv_arrow.startAnimation(upAnim); showRefreshingProgressBar(false); } return true; } break; case MotionEvent.ACTION_UP: if (currentState == STATE_RELEASE_REFRESH) { // 如果當前狀態是松開刷新,並且抬起了手,則進入正在刷新狀態 currentState = STATE_REFRESHING; tv_state.setText("正在刷新"); showRefreshingProgressBar(true); showHeaderView(); if (mOnRefreshingListener != null) { mOnRefreshingListener.onRefreshing(); } } else if (currentState == STATE_PULL_TO_REFRESH) { // 如果抬起手時是下拉刷新狀態,則把HeaderView完成隱藏 hideHeaderView(); } break; } return super.onTouchEvent(ev); } public void setOnRefreshingListener(OnRefreshingListener mOnRefreshingListener) { this.mOnRefreshingListener = mOnRefreshingListener; } /** ListView刷新的監聽器 */ public interface OnRefreshingListener { /** 當ListView可以刷新數據的時候會調用這個方法 */ void onRefreshing(); /** 當ListView可以加載更多 的時候會調用這個方法 */ void onLoadMore(); } /** 聯網刷新數據的操作已經完成了 */ public void onRefreshComplete() { hideHeaderView(); currentState = STATE_PULL_TO_REFRESH; showRefreshingProgressBar(false); } /** 加載更多新數據的操作已經完成了 */ public void onLoadmoreComplete() { hideFooterView(); loadingMore = false; } }
方案二: listview的多種樣式顯示
設置listview的適配器的時候可以實現兩個方法: getViewTypeCount()和getItemViewType(),前者指定條目的種類,後者返回具體的類型,這樣可以根據不同的類型設計相關的樣式,包括上拉加載更多,和下拉刷新,兩者類似,因此這裡僅僅給出加載更多的寫法。具體實現如下:
1、重寫getViewTypeCount()和getItemViewType(),這裡包括普通的item條目和加載更多的條目,所以getViewTypeCount()返回值為2;
@Override public int getViewTypeCount() { return super.getViewTypeCount() + 1; } @Override public int getItemViewType(int position) { if (position == getCount() - 1) { return 0; } else { return addViewType(position); //構造一個方法出來,方便子類修改,添加更多的樣式 } } public int addViewType(int position) { return 1; }
2、在getview()中針對不同的類型添加布局:
@Override public View getView(int position, View convertView, ViewGroup parent) { BaseHoldle holdle; if (convertView == null) { if (getItemViewType(position) == 0) { //type為0 表示應該加載加載更多的視圖 holdle = getLoadmoreHoldle(); } else { //否則為普通視圖 holdle = getSpecialBaseHoldle(position); } } else { holdle = (BaseHoldle) convertView.getTag(); } if (getItemViewType(position) == 0) { //加載更多視圖,請求網絡獲取數據 if (havemore()) { holdle.setDataAndRefreshHoldleView(LoadmoreHoldle.LOADMORE_LODING); triggleLoadMoreData(); } else { holdle.setDataAndRefreshHoldleView(LoadmoreHoldle.LOADMORE_NONE); } } else { //普通視圖視圖,請求網絡獲取數據 T data = (T) mdata.get(position); holdle.setDataAndRefreshHoldleView(data); } mHoldleView = holdle.mHoldleView; mHoldleView.setScaleX(0.6f); mHoldleView.setScaleY(0.6f); ViewCompat.animate(mHoldleView).scaleX(1).scaleY(1).setDuration(400).setInterpolator(new OvershootInterpolator(4)).start(); return mHoldleView; }
3、具體的加載更多視圖的實現
private BaseHoldle getLoadmoreHoldle() { if (mLoadmoreHoldle == null) { mLoadmoreHoldle = new LoadmoreHoldle(); } return mLoadmoreHoldle; } public class LoadmoreHoldle extends BaseHoldle { @Bind(R.id.item_loadmore_container_loading) LinearLayout itemloadmorecontainerloading; @Bind(R.id.item_loadmore_container_retry) LinearLayout itemloadmorecontainerretry; @Bind(R.id.item_loadmore_tv_retry) TextView item_loadmore_tv_retry; public static final int LOADMORE_LODING = 0; public static final int LOADMORE_ERROR = 1; public static final int LOADMORE_NONE = 2; private int mCurretState; @Override public void refreshHoldleView(Object data) { itemloadmorecontainerloading.setVisibility(View.GONE); itemloadmorecontainerretry.setVisibility(View.GONE); mCurretState = (int) data; switch (mCurretState) { case LOADMORE_LODING: itemloadmorecontainerloading.setVisibility(View.VISIBLE); break; case LOADMORE_ERROR: itemloadmorecontainerretry.setVisibility(View.VISIBLE); break; case LOADMORE_NONE: break; } } @Override public View ininViewHoldle() { View view = View.inflate(UiUtils.getContext(), R.layout.itemloadmore, null); ButterKnife.bind(this, view); return view; } } //holder基類,提取公共的方法 public abstract class BaseHoldle<T> { public View mHoldleView; public T mdata; public BaseHoldle() { mHoldleView = ininViewHoldle(); mHoldleView.setTag(this); } public void setDataAndRefreshHoldleView(T mdata) { this.mdata = mdata; refreshHoldleView(mdata); } public abstract void refreshHoldleView(T data); public abstract View ininViewHoldle(); }
方案三: SwipeRefreshLayout實現下來刷新
SwipeRefreshLayout對下不兼容,且只有下拉刷新功能沒有上拉加載更多的功能。當時作為Andriod5.0之後的新特性,使用起來方便,可以直接調用系統的API。使用方法也較為簡單。具體實現如下:
首先聲明控件,設置顏色:
refreshLayout = (SwipeRefreshLayout) findViewById(R.id.refresh); refreshLayout.setOnRefreshListener(this); refreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright, android.R.color.holo_green_light,android.R.color.holo_orange_light, android.R.color.holo_red_light); refreshLayout.setProgressBackgroundColor(R.color.refresh_bg); refreshLayout.setProgressBackgroundColor(R.color.refresh_bg);
寫一個類實現SwipeRefreshLayout.OnRefreshListener,重寫onRefresh()方法:
@Override public void onRefresh() { refreshLayout.postDelayed(new Runnable() { @Override public void run() { //請求網絡,獲取數據 refreshLayout.setRefreshing(false); } },3000); }
以上所述是小編給大家介紹的Android中Listview下拉刷新和上拉加載更多的多種實現方案,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對本站網站的支持!
本文使用Matrix實現Android實現圖片縮放與旋轉。示例代碼如下:復制代碼 代碼如下:package com.android.matrix;import andro
本文實例講述了Android編程滑動效果之Gallery仿圖像集浏覽實現方法。分享給大家供大家參考,具體如下:Android系統自帶一個Gallery浏覽圖片的應用,通過
對於從事Android開發的人來說,遇到ANR(Application Not Responding)是比較常見的問題。一般情況下,如果有ANR發生,系統都會在/data
很多Android手機隨機都預裝了很多無法卸載的第三方APP,這些APP既浪費資源還有偷跑流量的隱患。那麼,在不Root系統的前提下如何將它們“