Android RecyclerView實(shí)現(xiàn)下拉刷新和上拉加載
RecyclerView已經(jīng)出來很久了,許許多多的項(xiàng)目都開始從ListView轉(zhuǎn)戰(zhàn)RecyclerView,那么,上拉加載和下拉刷新是一件很有必要的事情。
在ListView上,我們可以通過自己添加addHeadView和addFootView去添加頭布局和底部局實(shí)現(xiàn)自定義的上拉和下拉,或者使用一些第三方庫來簡單的集成,例如Android-pulltorefresh或者android-Ultra-Pull-to-Refresh,后者的自定義更強(qiáng),但需要自己實(shí)現(xiàn)上拉加載。
而在下面我們將用兩種方式來實(shí)現(xiàn)上拉加載和下拉刷新
第一種方式:SwipeRefreshLayout+滑動(dòng)底部自動(dòng)加載
第二種方式:使用第三方庫SwipeToLoadLayout實(shí)現(xiàn)上拉加載和下拉刷新。
第一種方式:SwipeRefreshLayout+滑動(dòng)底部自動(dòng)加載
SwipeRefreshLayout實(shí)現(xiàn)很簡單,重點(diǎn)是滑動(dòng)到底部自動(dòng)加載應(yīng)該如何實(shí)現(xiàn),其實(shí)其實(shí)現(xiàn)的方式類似于ListView的實(shí)現(xiàn)方式。
看一下activity_recycle_swiperefresh.xml文件:
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/swipe_refresh" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/swipe_target" android:layout_width="match_parent" android:layout_height="wrap_content" android:scrollbars="none" /> </android.support.v4.widget.SwipeRefreshLayout>
布局文件就兩個(gè)控件,SwipeRefreshLayout中嵌套R(shí)ecyclerView。
在代碼中初始化RecyclerView以及實(shí)現(xiàn)adapter等,這不是重點(diǎn),不再貼代碼。
在RecyclerView中有方法addOnScrollListener,該方法類似于ListView的setOnScrollListener方法,OnScrollListener中有兩個(gè)方法的回調(diào)
*onScrolled(RecyclerView recyclerView, int dx, int dy) :滾動(dòng)的回調(diào),dx和dy表示手指滑動(dòng)水平和垂直的偏移量。
*onScrollStateChanged(RecyclerView recyclerView, int newState):滑動(dòng)狀態(tài)的回調(diào)。
那么,我們的著重點(diǎn)就在這個(gè)兩個(gè)方法上了。
對于向上加載更多,我們需要有如下判斷
--是否是向上滑動(dòng)
--是否滑動(dòng)到底部
--當(dāng)前是否正在加載數(shù)據(jù)
--當(dāng)前狀態(tài)是否是滑動(dòng)停止的狀態(tài)
實(shí)現(xiàn)比較復(fù)雜,定義一個(gè)類LoadDataScrollController,繼承類RecyclerView.OnScrollListener,
因?yàn)閛nScrollStateChanged實(shí)在狀態(tài)改變時(shí)的回調(diào),無法時(shí)時(shí)的獲取顯示的條目以及位置,所以我們在onScrolled中獲取相應(yīng)位置,
@Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { /** * 獲取布局參數(shù) */ RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); //如果為null,第一次運(yùn)行,確定布局類型 if (mLayoutManagerType == null) { if (layoutManager instanceof LinearLayoutManager) { mLayoutManagerType = LayoutManagerType.LINEAR_LAYOUT; } else if (layoutManager instanceof GridLayoutManager) { mLayoutManagerType = LayoutManagerType.GRID_LAYOUT; } else if (layoutManager instanceof StaggeredGridLayoutManager) { mLayoutManagerType = LayoutManagerType.STAGGERED_GRID_LAYOUT; } else { throw new RuntimeException("LayoutManager should be LinearLayoutManager,GridLayoutManager,StaggeredGridLayoutManager"); } } //對于不太能夠的布局參數(shù),不同的方法獲取到當(dāng)前顯示的最后一個(gè)條目數(shù) switch (mLayoutManagerType) { case LINEAR_LAYOUT: mLastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); break; case GRID_LAYOUT: mLastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition(); break; case STAGGERED_GRID_LAYOUT: StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; if (mLastPostions == null) { mLastPostions = new int[staggeredGridLayoutManager.getSpanCount()]; } staggeredGridLayoutManager.findLastVisibleItemPositions(mLastPostions); mLastVisibleItemPosition = findMax(mLastPostions); break; default: break; } }
首先獲取布局管理器,并判斷是那種類型的,因?yàn)橛腥N類型,定義枚舉來保存布局類型的參數(shù)
/** * * RecycleView的布局管理器的類型 * Created by Alex_MaHao on 2016/5/10. */ public enum LayoutManagerType { LINEAR_LAYOUT, GRID_LAYOUT, STAGGERED_GRID_LAYOUT }
然后根據(jù)布局慣例其的類型獲取其當(dāng)前顯示的最大條目,對于瀑布流來說,他如果是垂直的兩列瀑布的話,我們需要獲取兩列中分別最大條目數(shù),進(jìn)行比較,選出最大條目數(shù)。
/** * 當(dāng)是瀑布流時(shí),獲取到的是每一個(gè)瀑布最下方顯示的條目,通過條目進(jìn)行對比 */ private int findMax(int[] lastPositions) { int max = lastPositions[0]; for (int value : lastPositions) { if (value > max) { max = value; } } return max; }
拿到當(dāng)前最大的條目數(shù)之后,在onScrollStateChange中進(jìn)行判斷狀態(tài)等,
@Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); //RecycleView 顯示的條目數(shù) int visibleCount = layoutManager.getChildCount(); //顯示數(shù)據(jù)總數(shù) int totalCount = layoutManager.getItemCount(); // 四個(gè)條件,分別是是否有數(shù)據(jù),狀態(tài)是否是滑動(dòng)停止?fàn)顟B(tài),顯示的最大條目是否大于整個(gè)數(shù)據(jù)(注意偏移量),是否正在加載數(shù)據(jù) if(visibleCount>0 &&newState==RecyclerView.SCROLL_STATE_IDLE &&mLastVisibleItemPosition>=totalCount-1 &&!isLoadData){ //可以加載數(shù)據(jù) isLoadData = true; } }
注釋很清楚,在加載數(shù)據(jù)的地方,我們將isLoadData設(shè)為true,同時(shí)利用接口回調(diào)加載數(shù)據(jù),等數(shù)據(jù)加載完成,通過setLoadDataStatus方法設(shè)置為false
public void setLoadDataStatus(boolean isLoadData){ this.isLoadData = isLoadData; }
如果這樣就結(jié)束了,感覺很麻煩,對于刷新和加載更多,我們需要在調(diào)用的地方分別設(shè)置監(jiān)聽,那么我們可以讓LoadDataScrollController實(shí)現(xiàn)SwipeRefreshLayout的刷新監(jiān)聽方法,在利用我們定義的統(tǒng)一的上拉刷新和加載數(shù)據(jù)接口進(jìn)行處理
/** * 實(shí)現(xiàn)上拉加載的監(jiān)聽:加載條件:滑動(dòng)到最后,且是停止?fàn)顟B(tài),則開始加載數(shù)據(jù) * Created by Alex_MaHao on 2016/5/10. */ public class LoadDataScrollController extends RecyclerView.OnScrollListener implements SwipeRefreshLayout.OnRefreshListener { /** * 當(dāng)前布局管理器的類型 */ private LayoutManagerType mLayoutManagerType; /** * 當(dāng)前RecycleView顯示的最大條目 */ private int mLastVisibleItemPosition; /** * 每列的最后一個(gè)條目 */ private int[] mLastPostions; /** * 是否正在加載數(shù)據(jù) 包括刷新和向上加載更多 */ private boolean isLoadData = false; /** * 回調(diào)接口 */ private OnRecycleRefreshListener mListener; public LoadDataScrollController(OnRecycleRefreshListener onRecycleRefreshListener) { this.mListener = onRecycleRefreshListener; } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { /** * 獲取布局參數(shù) */ RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); //如果為null,第一次運(yùn)行,確定布局類型 if (mLayoutManagerType == null) { if (layoutManager instanceof LinearLayoutManager) { mLayoutManagerType = LayoutManagerType.LINEAR_LAYOUT; } else if (layoutManager instanceof GridLayoutManager) { mLayoutManagerType = LayoutManagerType.GRID_LAYOUT; } else if (layoutManager instanceof StaggeredGridLayoutManager) { mLayoutManagerType = LayoutManagerType.STAGGERED_GRID_LAYOUT; } else { throw new RuntimeException("LayoutManager should be LinearLayoutManager,GridLayoutManager,StaggeredGridLayoutManager"); } } //對于不太能夠的布局參數(shù),不同的方法獲取到當(dāng)前顯示的最后一個(gè)條目數(shù) switch (mLayoutManagerType) { case LINEAR_LAYOUT: mLastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); break; case GRID_LAYOUT: mLastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition(); break; case STAGGERED_GRID_LAYOUT: StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; if (mLastPostions == null) { mLastPostions = new int[staggeredGridLayoutManager.getSpanCount()]; } staggeredGridLayoutManager.findLastVisibleItemPositions(mLastPostions); mLastVisibleItemPosition = findMax(mLastPostions); break; default: break; } } @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); //RecycleView 顯示的條目數(shù) int visibleCount = layoutManager.getChildCount(); //顯示數(shù)據(jù)總數(shù) int totalCount = layoutManager.getItemCount(); // 四個(gè)條件,分別是是否有數(shù)據(jù),狀態(tài)是否是滑動(dòng)停止?fàn)顟B(tài),顯示的最大條目是否大于整個(gè)數(shù)據(jù)(注意偏移量),是否正在加載數(shù)據(jù) if(visibleCount>0 &&newState==RecyclerView.SCROLL_STATE_IDLE &&mLastVisibleItemPosition>=totalCount-1 &&!isLoadData){ //可以加載數(shù)據(jù) if(mListener!=null){ isLoadData = true; mListener.loadMore(); } } } /** * 當(dāng)是瀑布流時(shí),獲取到的是每一個(gè)瀑布最下方顯示的條目,通過條目進(jìn)行對比 */ private int findMax(int[] lastPositions) { int max = lastPositions[0]; for (int value : lastPositions) { if (value > max) { max = value; } } return max; } public void setLoadDataStatus(boolean isLoadData){ this.isLoadData = isLoadData; } @Override public void onRefresh() { //刷新數(shù)據(jù)的方法 if(mListener!=null){ isLoadData = true; mListener.refresh(); } } /** * 數(shù)據(jù)加載接口回調(diào) */ interface OnRecycleRefreshListener{ void refresh(); void loadMore(); } }
最后看一下main的代碼
/** * 使用原生的SwipeRefreshLayout和代碼判斷 * 實(shí)現(xiàn)RecyclewView 的刷新和加載更多 * * Created by Alex_MaHao on 2016/5/10. */ public class SwipeRefreshActivity extends AppCompatActivity implements LoadDataScrollController.OnRecycleRefreshListener { private SwipeRefreshLayout mSwipeRefresh; private RecyclerView mRecycle; private HomeAdapter mAdapter; private LoadDataScrollController mController; private ProgressDialog pd; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycle_swiperefresh); mRecycle = ((RecyclerView) findViewById(R.id.swipe_target)); mSwipeRefresh = ((SwipeRefreshLayout) findViewById(R.id.swipe_refresh)); mSwipeRefresh.setColorSchemeColors(Color.RED,Color.GREEN,Color.BLUE); /** * 創(chuàng)建控制器,同時(shí)使當(dāng)前activity實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽回調(diào)接口 */ mController = new LoadDataScrollController(this); mAdapter = new HomeAdapter(); //設(shè)置垂直的線性布局管理器,Orientation --> VERTICAL:垂直 HORIZONTAL:水平 LinearLayoutManager layoutManager = new LinearLayoutManager(this); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); //StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL); //添加分割線 mRecycle.addItemDecoration(new DividerItemDecoration(getApplicationContext(), DividerItemDecoration.VERTICAL_LIST)); mRecycle.setLayoutManager(layoutManager); mRecycle.setItemAnimator(new DefaultItemAnimator()); mRecycle.setAdapter(mAdapter); mAdapter.refresh(); /** * 設(shè)置監(jiān)聽 */ mRecycle.addOnScrollListener(mController); mSwipeRefresh.setOnRefreshListener(mController); } @Override public void refresh() { //刷新的接口調(diào) mSwipeRefresh.postDelayed(new Runnable() { @Override public void run() { mAdapter.refresh(); mSwipeRefresh.setRefreshing(false); mController.setLoadDataStatus(false); } },2000); } @Override public void loadMore() { //加載更多的接口回調(diào) pd = new ProgressDialog(this); pd.show(); mSwipeRefresh.postDelayed(new Runnable() { @Override public void run() { mAdapter.add(); //設(shè)置數(shù)據(jù)加載結(jié)束的監(jiān)聽狀態(tài) mController.setLoadDataStatus(false); pd.dismiss(); } },2000); } }
貼個(gè)效果圖
第二種方式:SwipeToLoadLayout實(shí)現(xiàn)上拉加載和下拉刷新
該刷新控件的方式類似于Ultra-pull-to-refresh的使用方式。
如下方式添加該庫:
repositories { maven { url "https://jitpack.io" } } compile 'com.github.Aspsine:SwipeToLoadLayout:1.0.3'
首先我們需要自定義一個(gè)頭視圖和底部視圖,頭部試圖和底部試圖的用法相同,所以我們先定義一個(gè)頭部視圖類:
/** * 基礎(chǔ)的refreshHeadView */ public class RefreshHeaderView extends TextView implements SwipeRefreshTrigger, SwipeTrigger { public RefreshHeaderView(Context context) { super(context); } public RefreshHeaderView(Context context, AttributeSet attrs) { super(context, attrs); } public RefreshHeaderView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public RefreshHeaderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public void onRefresh() { //下拉到一定位置松開之后,調(diào)用此方法 setText("refresh"); Log.i("info","onRefresh"); } @Override public void onPrepare() { //下拉之前調(diào)用此方法 Log.i("info","onPrepare"); } @Override public void onMove(int yScrolled, boolean isComplete, boolean automatic) { if (!isComplete) { //當(dāng)前Y軸偏移量大于控件高度時(shí),標(biāo)識(shí)下拉到界限,顯示“松開已刷新” if (yScrolled >= getHeight()) { } else { //未達(dá)到偏移量 } } Log.i("info","onMove"); } @Override public void onRelease() { //達(dá)到一定滑動(dòng)距離,松開刷新時(shí)調(diào)用 setText("onRelease"); Log.i("info","onRelease"); } @Override public void onComplete() { //加載完成之后調(diào)用此方法 setText("complete"); Log.i("info","onComplete"); } @Override public void onReset() { //重置 setText("onReset"); Log.i("info","onReset"); } }
其需要實(shí)現(xiàn)接口SwipeRefreshTrigger和SwipeTrigger。
而底部需要實(shí)現(xiàn)SwipeTrigger和SwipeLoadMoreTrigger。
布局文件中如下使用
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ECEDF0" > <com.aspsine.swipetoloadlayout.SwipeToLoadLayout android:id="@+id/swipeToLoadLayout" android:layout_width="match_parent" android:layout_height="match_parent"> <com.mahao.alex.systemwidgetdemo.recycleView.swipetoloadlayout.RefreshHeaderView android:id="@+id/swipe_refresh_header" android:layout_width="match_parent" android:layout_height="wrap_content" /> <android.support.v7.widget.RecyclerView android:id="@+id/swipe_target" android:layout_width="match_parent" android:layout_height="wrap_content" android:scrollbars="vertical" /> <com.mahao.alex.systemwidgetdemo.recycleView.swipetoloadlayout.LoaderMoreView android:id="@+id/swipe_load_more_footer" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:padding="20dp" /> </com.aspsine.swipetoloadlayout.SwipeToLoadLayout> </RelativeLayout>
查找控件,設(shè)置監(jiān)聽
swipeToLoadLayout.setOnRefreshListener(this);
swipeToLoadLayout.setOnLoadMoreListener(this);
在我們之前的代碼中,加入了log信息,我們可以看一下log信息。…代表多次onMove()方法多次調(diào)用。
05-10 10:30:34.396 23814-23814/com.mahao.alex.systemwidgetdemo I/info: onPrepare
05-10 10:30:34.536 23814-23814/com.mahao.alex.systemwidgetdemo I/info: onMove
..........................................................................
05-10 10:30:34.886 23814-23814/com.mahao.alex.systemwidgetdemo I/info: onMove
05-10 10:30:34.896 23814-23814/com.mahao.alex.systemwidgetdemo I/info: onRelease
05-10 10:30:34.906 23814-23814/com.mahao.alex.systemwidgetdemo I/info: onMove
..........................................................................
05-10 10:30:35.086 23814-23814/com.mahao.alex.systemwidgetdemo I/info: onMove
05-10 10:30:35.106 23814-23814/com.mahao.alex.systemwidgetdemo I/info: onRefresh
05-10 10:30:37.116 23814-23814/com.mahao.alex.systemwidgetdemo I/info: onComplete
05-10 10:30:37.416 23814-23814/com.mahao.alex.systemwidgetdemo I/info: onMove
..........................................................................
05-10 10:30:37.516 23814-23814/com.mahao.alex.systemwidgetdemo I/info: onMove
05-10 10:30:37.916 23814-23814/com.mahao.alex.systemwidgetdemo I/info: onReset
首先會(huì)調(diào)用onPrepare()方法,onMove()方法會(huì)一直調(diào)用,只要視圖有偏移,就會(huì)調(diào)用。下拉到一定距離之后,松開調(diào)用onRelaease(),回歸到刷新位置時(shí)回調(diào)onRefresh(),加載完成調(diào)用onComplete(),視圖開始縮小,最后隱藏之后調(diào)用onReset()
根據(jù)需求自定義視圖,
定義我們的橢圓,使用自定義控件
/** * CircleView 圓盤控件,可以旋轉(zhuǎn) * Created by Alex_MaHao on 2016/5/10. */ public class CircleView extends View { /** * 控件的半徑 */ private int mRadius; /** * 繪制弧形的畫筆 */ private Paint mArcPaint; /** * 繪制弧形的區(qū)域 */ private RectF mRange; private int[] colors = {Color.RED, Color.BLUE, Color.YELLOW, Color.GREEN}; public CircleView(Context context) { this(context, null, 0); } public CircleView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CircleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mArcPaint = new Paint(); mArcPaint.setAntiAlias(true); mArcPaint.setDither(true); mArcPaint.setStyle(Paint.Style.FILL); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = 0; int height = 0; int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } else { width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 45, getResources().getDisplayMetrics()); } if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 45, getResources().getDisplayMetrics()); } //獲取半徑 mRadius = Math.min(width, height) / 2; /** * 設(shè)置寬高為固定值 */ setMeasuredDimension(mRadius * 2, mRadius * 2); mRange = new RectF(0, 0, mRadius * 2, mRadius * 2); } @Override protected void onDraw(Canvas canvas) { float degree = 360/colors.length/2f; for (int i = 0; i < 8; i++) { mArcPaint.setColor(colors[i%4]); canvas.drawArc(mRange,-90f+degree*i,degree,true,mArcPaint); } } }
繪制頭部刷新試圖
** * 自定義的下拉刷新控件 頭部 * Created by Alex_MaHao on 2016/5/10. */ public class CircleRefreshHeaderView extends RelativeLayout implements SwipeTrigger, SwipeRefreshTrigger { CircleView mCircleView; TextView mDescText; private ObjectAnimator anim; private boolean isRelease; public CircleRefreshHeaderView(Context context) { this(context, null, 0); } public CircleRefreshHeaderView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CircleRefreshHeaderView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(); } /** * 初始化布局 */ private void initView() { int circlewidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, getResources().getDisplayMetrics()); mCircleView = new CircleView(getContext()); LinearLayout.LayoutParams circleParams = new LinearLayout.LayoutParams(circlewidth,circlewidth); mCircleView.setLayoutParams(circleParams); mDescText = new TextView(getContext()); LinearLayout.LayoutParams descParams = new LinearLayout.LayoutParams(circlewidth*3, ViewGroup.LayoutParams.WRAP_CONTENT); descParams.gravity = Gravity.CENTER; descParams.setMargins(circlewidth/2,0,0,0); mDescText.setLayoutParams(descParams); mDescText.setTextSize(12); mDescText.setTextColor(Color.GRAY); mDescText.setText("下拉刷新"); //添加線性的父布局 LinearLayout ll = new LinearLayout(getContext()); RelativeLayout.LayoutParams llParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); llParams.addRule(CENTER_IN_PARENT); ll.setLayoutParams(llParams); ll.setPadding(10,10,10,10); ll.addView(mCircleView); ll.addView(mDescText); addView(ll); } @Override public void onRefresh() { //開始刷新,啟動(dòng)動(dòng)畫 anim = ObjectAnimator.ofFloat(mCircleView, "rotation", mCircleView.getRotation(), mCircleView.getRotation()+360f) .setDuration(500); anim.setRepeatCount(ValueAnimator.INFINITE); anim.setRepeatMode(ValueAnimator.RESTART); anim.start(); mDescText.setText("正在加載數(shù)據(jù)"); } @Override public void onPrepare() { isRelease = false; } @Override public void onMove(int yScroll, boolean isComplete, boolean b1) { if (!isComplete) { if (yScroll < getHeight()) { mDescText.setText("下拉刷新"); } else { mDescText.setText("松開刷新更多"); } //如果是仍在下拉狀態(tài),則圓環(huán)跟隨滑動(dòng)進(jìn)行滾動(dòng) if (!isRelease) mCircleView.setRotation(((float) yScroll) / getHeight() * 360f); } } @Override public void onRelease() { isRelease = true; } @Override public void onComplete() { anim.cancel(); mDescText.setText("加載完成"); } @Override public void onReset() { //重置時(shí),將動(dòng)畫置為初始狀態(tài) mCircleView.setRotation(0f); } }
布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ECEDF0" > <com.aspsine.swipetoloadlayout.SwipeToLoadLayout android:id="@+id/swipeToLoadLayout" android:layout_width="match_parent" android:layout_height="match_parent"> <com.mahao.alex.systemwidgetdemo.recycleView.swipetoloadlayout.CircleRefreshHeaderView android:id="@+id/swipe_refresh_header" android:layout_width="match_parent" android:layout_height="wrap_content" /> <android.support.v7.widget.RecyclerView android:id="@+id/swipe_target" android:layout_width="match_parent" android:layout_height="wrap_content" android:scrollbars="vertical" /> <com.mahao.alex.systemwidgetdemo.recycleView.swipetoloadlayout.LoaderMoreView android:id="@+id/swipe_load_more_footer" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:padding="20dp" /> </com.aspsine.swipetoloadlayout.SwipeToLoadLayout> </RelativeLayout>
public class SwipeToLayoutActivity extends AppCompatActivity implements OnRefreshListener, OnLoadMoreListener { private RecyclerView mRecycleView; SwipeToLoadLayout swipeToLoadLayout; private HomeAdapter adapter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycle_swipetolayout); swipeToLoadLayout = ((SwipeToLoadLayout) findViewById(R.id.swipeToLoadLayout)); mRecycleView = ((RecyclerView) findViewById(R.id.swipe_target)); adapter = new HomeAdapter(); //設(shè)置垂直的線性布局管理器,Orientation --> VERTICAL:垂直 HORIZONTAL:水平 LinearLayoutManager layoutManager = new LinearLayoutManager(this); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); // StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL); //添加分割線 mRecycleView.addItemDecoration(new DividerItemDecoration(getApplicationContext(), DividerItemDecoration.VERTICAL_LIST)); mRecycleView.setLayoutManager(layoutManager); mRecycleView.setItemAnimator(new DefaultItemAnimator()); mRecycleView.setAdapter(adapter); adapter.refresh(); /** * 設(shè)置下拉刷新和上拉加載監(jiān)聽 */ swipeToLoadLayout.setOnRefreshListener(this); swipeToLoadLayout.setOnLoadMoreListener(this); } @Override public void onRefresh() { swipeToLoadLayout.postDelayed(new Runnable() { @Override public void run() { adapter.refresh(); swipeToLoadLayout.setRefreshing(false); } },2000); } @Override public void onLoadMore() { swipeToLoadLayout.postDelayed(new Runnable() { @Override public void run() { adapter.add(); swipeToLoadLayout.setLoadingMore(false); } },2000); } }
OK??隙ㄓ行』锇槭褂迷摽蚣軙r(shí)一直報(bào)錯(cuò),為什么,看框架的源碼,有如下一段
this.mHeaderView = this.findViewById(id.swipe_refresh_header); this.mTargetView = this.findViewById(id.swipe_target); this.mFooterView = this.findViewById(id.swipe_load_more_footer);
可以看出,作者是根據(jù)固定的id值獲取的,所以在我們的布局文件中,必須使用固定的三個(gè)id。
如有需求,可移步我的github獲取源碼,源碼在systemwidgetdemo中。
以上就是本文的全部內(nèi)容,希望對大家學(xué)習(xí)Android軟件編程有所幫助。
- Android中使用RecyclerView實(shí)現(xiàn)下拉刷新和上拉加載
- Android RecyclerView 上拉加載更多及下拉刷新功能的實(shí)現(xiàn)方法
- 詳解Recyclerview item中有EditText使用刷新遇到的坑
- Android使用recyclerview打造真正的下拉刷新上拉加載效果
- android RecyclerView側(cè)滑菜單,滑動(dòng)刪除,長按拖拽,下拉刷新上拉加載
- XRecyclerView實(shí)現(xiàn)下拉刷新、滾動(dòng)到底部加載更多等功能
- Android使用RecyclerView實(shí)現(xiàn)自定義列表、點(diǎn)擊事件以及下拉刷新
- Android RecyclerView的刷新分頁的實(shí)現(xiàn)
- Android RecyclerView下拉刷新和上拉加載更多
- RecyclerView使用payload實(shí)現(xiàn)局部刷新
相關(guān)文章
Android超詳細(xì)講解組件LinearLayout的使用
LinearLayout又稱作線性布局,是一種非常常用的布局。正如它的名字所描述的一樣,這個(gè)布局會(huì)將它所包含的控件在線性方向上依次排列。既然是線性排列,肯定就不僅只有一個(gè)方向,這里一般只有兩個(gè)方向:水平方向和垂直方向2022-03-03Android shell命令行中過濾adb logcat輸出的方法
本文主要介紹Android shell命令行中過濾adb logcat輸出,這里詳細(xì)說明了shell 命令過濾logcat 輸出內(nèi)容,有需要的小伙伴可以參考下2016-08-08android開發(fā)之listView組件用法實(shí)例簡析
這篇文章主要介紹了android開發(fā)之listView組件用法,結(jié)合實(shí)例形式簡單分析了listView組件的相關(guān)屬性與使用技巧,需要的朋友可以參考下2016-01-01Android AIDL和遠(yuǎn)程Service調(diào)用示例代碼
本文主要介紹Android AIDL和遠(yuǎn)程Service,這里詳細(xì)介紹了相關(guān)知識(shí),并附實(shí)例代碼和實(shí)現(xiàn)效果圖,有興趣的朋友參考下2016-08-08Android基于BaseExpandableListAdapter實(shí)現(xiàn)的二級列表仿通話記錄功能詳解
這篇文章主要介紹了Android基于BaseExpandableListAdapter實(shí)現(xiàn)的二級列表仿通話記錄功能,結(jié)合具體實(shí)例形式分析了Android實(shí)現(xiàn)通話記錄功能的布局與功能相關(guān)操作技巧,需要的朋友可以參考下2017-07-07Android RecyclerView 實(shí)現(xiàn)快速滾動(dòng)的示例代碼
本篇文章主要介紹了Android RecyclerView 實(shí)現(xiàn)快速滾動(dòng)的示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-09-09Android控件gridview實(shí)現(xiàn)單行多列橫向滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Android控件gridview實(shí)現(xiàn)單行多列橫向滾動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12淺析Android中常見三種彈框在項(xiàng)目中的應(yīng)用
這篇文章主要介紹了淺析Android中常見三種彈框在項(xiàng)目中的應(yīng)用,需要的朋友可以參考下2017-03-03