Android RecyclerView實現(xiàn)下拉刷新和上拉加載
RecyclerView已經(jīng)出來很久了,許許多多的項目都開始從ListView轉(zhuǎn)戰(zhàn)RecyclerView,那么,上拉加載和下拉刷新是一件很有必要的事情。
在ListView上,我們可以通過自己添加addHeadView和addFootView去添加頭布局和底部局實現(xiàn)自定義的上拉和下拉,或者使用一些第三方庫來簡單的集成,例如Android-pulltorefresh或者android-Ultra-Pull-to-Refresh,后者的自定義更強,但需要自己實現(xiàn)上拉加載。
而在下面我們將用兩種方式來實現(xiàn)上拉加載和下拉刷新
第一種方式:SwipeRefreshLayout+滑動底部自動加載
第二種方式:使用第三方庫SwipeToLoadLayout實現(xiàn)上拉加載和下拉刷新。
第一種方式:SwipeRefreshLayout+滑動底部自動加載
SwipeRefreshLayout實現(xiàn)很簡單,重點是滑動到底部自動加載應該如何實現(xiàn),其實其實現(xiàn)的方式類似于ListView的實現(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>
布局文件就兩個控件,SwipeRefreshLayout中嵌套RecyclerView。
在代碼中初始化RecyclerView以及實現(xiàn)adapter等,這不是重點,不再貼代碼。
在RecyclerView中有方法addOnScrollListener,該方法類似于ListView的setOnScrollListener方法,OnScrollListener中有兩個方法的回調(diào)
*onScrolled(RecyclerView recyclerView, int dx, int dy) :滾動的回調(diào),dx和dy表示手指滑動水平和垂直的偏移量。
*onScrollStateChanged(RecyclerView recyclerView, int newState):滑動狀態(tài)的回調(diào)。
那么,我們的著重點就在這個兩個方法上了。
對于向上加載更多,我們需要有如下判斷
--是否是向上滑動
--是否滑動到底部
--當前是否正在加載數(shù)據(jù)
--當前狀態(tài)是否是滑動停止的狀態(tài)
實現(xiàn)比較復雜,定義一個類LoadDataScrollController,繼承類RecyclerView.OnScrollListener,
因為onScrollStateChanged實在狀態(tài)改變時的回調(diào),無法時時的獲取顯示的條目以及位置,所以我們在onScrolled中獲取相應位置,
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
/**
* 獲取布局參數(shù)
*/
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
//如果為null,第一次運行,確定布局類型
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ù),不同的方法獲取到當前顯示的最后一個條目數(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;
}
}
首先獲取布局管理器,并判斷是那種類型的,因為有三種類型,定義枚舉來保存布局類型的參數(shù)
/**
*
* RecycleView的布局管理器的類型
* Created by Alex_MaHao on 2016/5/10.
*/
public enum LayoutManagerType {
LINEAR_LAYOUT,
GRID_LAYOUT,
STAGGERED_GRID_LAYOUT
}
然后根據(jù)布局慣例其的類型獲取其當前顯示的最大條目,對于瀑布流來說,他如果是垂直的兩列瀑布的話,我們需要獲取兩列中分別最大條目數(shù),進行比較,選出最大條目數(shù)。
/**
* 當是瀑布流時,獲取到的是每一個瀑布最下方顯示的條目,通過條目進行對比
*/
private int findMax(int[] lastPositions) {
int max = lastPositions[0];
for (int value : lastPositions) {
if (value > max) {
max = value;
}
}
return max;
}
拿到當前最大的條目數(shù)之后,在onScrollStateChange中進行判斷狀態(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();
// 四個條件,分別是是否有數(shù)據(jù),狀態(tài)是否是滑動停止狀態(tài),顯示的最大條目是否大于整個數(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,同時利用接口回調(diào)加載數(shù)據(jù),等數(shù)據(jù)加載完成,通過setLoadDataStatus方法設(shè)置為false
public void setLoadDataStatus(boolean isLoadData){
this.isLoadData = isLoadData;
}
如果這樣就結(jié)束了,感覺很麻煩,對于刷新和加載更多,我們需要在調(diào)用的地方分別設(shè)置監(jiān)聽,那么我們可以讓LoadDataScrollController實現(xiàn)SwipeRefreshLayout的刷新監(jiān)聽方法,在利用我們定義的統(tǒng)一的上拉刷新和加載數(shù)據(jù)接口進行處理
/**
* 實現(xiàn)上拉加載的監(jiān)聽:加載條件:滑動到最后,且是停止狀態(tài),則開始加載數(shù)據(jù)
* Created by Alex_MaHao on 2016/5/10.
*/
public class LoadDataScrollController extends RecyclerView.OnScrollListener implements SwipeRefreshLayout.OnRefreshListener {
/**
* 當前布局管理器的類型
*/
private LayoutManagerType mLayoutManagerType;
/**
* 當前RecycleView顯示的最大條目
*/
private int mLastVisibleItemPosition;
/**
* 每列的最后一個條目
*/
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,第一次運行,確定布局類型
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ù),不同的方法獲取到當前顯示的最后一個條目數(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();
// 四個條件,分別是是否有數(shù)據(jù),狀態(tài)是否是滑動停止狀態(tài),顯示的最大條目是否大于整個數(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();
}
}
}
/**
* 當是瀑布流時,獲取到的是每一個瀑布最下方顯示的條目,通過條目進行對比
*/
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和代碼判斷
* 實現(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)建控制器,同時使當前activity實現(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);
}
}
貼個效果圖

第二種方式:SwipeToLoadLayout實現(xiàn)上拉加載和下拉刷新
該刷新控件的方式類似于Ultra-pull-to-refresh的使用方式。
如下方式添加該庫:
repositories {
maven { url "https://jitpack.io" }
}
compile 'com.github.Aspsine:SwipeToLoadLayout:1.0.3'
首先我們需要自定義一個頭視圖和底部視圖,頭部試圖和底部試圖的用法相同,所以我們先定義一個頭部視圖類:
/**
* 基礎(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) {
//當前Y軸偏移量大于控件高度時,標識下拉到界限,顯示“松開已刷新”
if (yScrolled >= getHeight()) {
} else {
//未達到偏移量
}
}
Log.i("info","onMove");
}
@Override
public void onRelease() {
//達到一定滑動距離,松開刷新時調(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");
}
}
其需要實現(xiàn)接口SwipeRefreshTrigger和SwipeTrigger。
而底部需要實現(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信息?!矶啻蝟nMove()方法多次調(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
首先會調(diào)用onPrepare()方法,onMove()方法會一直調(diào)用,只要視圖有偏移,就會調(diào)用。下拉到一定距離之后,松開調(diào)用onRelaease(),回歸到刷新位置時回調(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() {
//開始刷新,啟動動畫
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)跟隨滑動進行滾動
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() {
//重置時,將動畫置為初始狀態(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一直報錯,為什么,看框架的源碼,有如下一段
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值獲取的,所以在我們的布局文件中,必須使用固定的三個id。
如有需求,可移步我的github獲取源碼,源碼在systemwidgetdemo中。
以上就是本文的全部內(nèi)容,希望對大家學習Android軟件編程有所幫助。
- Android中使用RecyclerView實現(xiàn)下拉刷新和上拉加載
- Android RecyclerView 上拉加載更多及下拉刷新功能的實現(xiàn)方法
- 詳解Recyclerview item中有EditText使用刷新遇到的坑
- Android使用recyclerview打造真正的下拉刷新上拉加載效果
- android RecyclerView側(cè)滑菜單,滑動刪除,長按拖拽,下拉刷新上拉加載
- XRecyclerView實現(xiàn)下拉刷新、滾動到底部加載更多等功能
- Android使用RecyclerView實現(xiàn)自定義列表、點擊事件以及下拉刷新
- Android RecyclerView的刷新分頁的實現(xiàn)
- Android RecyclerView下拉刷新和上拉加載更多
- RecyclerView使用payload實現(xiàn)局部刷新
相關(guān)文章
Android shell命令行中過濾adb logcat輸出的方法
本文主要介紹Android shell命令行中過濾adb logcat輸出,這里詳細說明了shell 命令過濾logcat 輸出內(nèi)容,有需要的小伙伴可以參考下2016-08-08
android開發(fā)之listView組件用法實例簡析
這篇文章主要介紹了android開發(fā)之listView組件用法,結(jié)合實例形式簡單分析了listView組件的相關(guān)屬性與使用技巧,需要的朋友可以參考下2016-01-01
Android AIDL和遠程Service調(diào)用示例代碼
本文主要介紹Android AIDL和遠程Service,這里詳細介紹了相關(guān)知識,并附實例代碼和實現(xiàn)效果圖,有興趣的朋友參考下2016-08-08
Android基于BaseExpandableListAdapter實現(xiàn)的二級列表仿通話記錄功能詳解
這篇文章主要介紹了Android基于BaseExpandableListAdapter實現(xiàn)的二級列表仿通話記錄功能,結(jié)合具體實例形式分析了Android實現(xiàn)通話記錄功能的布局與功能相關(guān)操作技巧,需要的朋友可以參考下2017-07-07
Android RecyclerView 實現(xiàn)快速滾動的示例代碼
本篇文章主要介紹了Android RecyclerView 實現(xiàn)快速滾動的示例代碼,具有一定的參考價值,有興趣的可以了解一下2017-09-09
Android控件gridview實現(xiàn)單行多列橫向滾動效果
這篇文章主要為大家詳細介紹了Android控件gridview實現(xiàn)單行多列橫向滾動效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-12-12

