一、简介
自定义一个外包式沉浸式下拉更多加载,兼容ListView\RecyclerView\ScrollViewt等多种场景需求。外包RecyclerView效果图如下:
演示.gif
二、创建pull_foot.xml和pull_head.xml
pull_head.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:padding="30dp">
<ImageView
android:id="@+id/pull_to_load_image"
android:layout_width="15dp"
android:layout_height="15dp"
android:background="@drawable/ic_pulltorefresh_arrow" />
<ProgressBar
android:id="@+id/pull_to_load_progress"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone" />
<TextView
android:id="@+id/pull_to_load_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:text="下拉刷新" />
<TextView
android:id="@+id/pull_to_refresh_updated_at"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:text="" />
</LinearLayout>
</LinearLayout>
pull_foot.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal"
android:padding="15dp">
<ImageView
android:id="@+id/pull_foot_image"
android:layout_width="20dp"
android:layout_height="20dp"
android:background="@drawable/ic_pulltorefresh_arrow"
/>
<ProgressBar
android:id="@+id/pull_fot_progress"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
/>
<TextView
android:id="@+id/pull_foot_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:textColor="@color/teal_200"
android:text="加载更多"
/>
</LinearLayout>
三、创建自定义PullToRefreshView继续LinearLayout
1、其中核心代码如下:
/**
* init AdapterView like ListView,GridView and so on;or init ScrollView
*/
private void initContentAdapterView() {
int count = getChildCount();
if (count < 3) {
throw new IllegalArgumentException("this layout must contain 3 child views,and AdapterView or ScrollView must in the second position!");
}
View view = null;
for (int i = 0; i < count - 1; ++i) {
view = getChildAt(i);
if (view instanceof AdapterView<?>) {
mAdapterView = (AdapterView<?>) view;
}
if (view instanceof ScrollView) {
// finish later
mScrollView = (ScrollView) view;
}
if (view instanceof RecyclerView) {
mRecyclerView = (RecyclerView) view;
}
}
if (mAdapterView == null && mScrollView == null && mRecyclerView == null) {
throw new IllegalArgumentException("must contain a AdapterView or ScrollView and RecyclerView in this layout!");
}
}
通过获取子布局View判断用户所外包是ScrollView 还是RecyclerView 等,然后根据得到的特定view达到滑动显示效果,具体代码实现如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
int y = (int) e.getRawY();
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
// 首先拦截down事件,记录y坐标
mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
// deltaY > 0 是向下运动,< 0是向上运动
int deltaY = y - mLastMotionY;
if (isRefreshViewScroll(deltaY)) {
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
}
return false;
}
/*
* 如果在onInterceptTouchEvent()方法中没有拦截(即onInterceptTouchEvent()方法中 return
* false)则由PullToRefreshView 的子View来处理;否则由下面的方法来处理(即由PullToRefreshView自己来处理)
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mLock) {
return true;
}
int y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// onInterceptTouchEvent已经记录
// mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
int deltaY = y - mLastMotionY;
if (mPullState == PULL_DOWN_STATE) {
// PullToRefreshView执行下拉
Log.i(TAG, " pull down!parent view move!");
headerPrepareToRefresh(deltaY);
// setHeaderPadding(-mHeaderViewHeight);
} else if (mPullState == PULL_UP_STATE) {
// PullToRefreshView执行上拉
Log.i(TAG, "pull up!parent view move!");
footerPrepareToRefresh(deltaY);
}
mLastMotionY = y;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
int topMargin = getHeaderTopMargin();
if (mPullState == PULL_DOWN_STATE) {
if (topMargin >= 0) {
// 开始刷新
headerRefreshing();
} else {
// 还没有执行刷新,重新隐藏
setHeaderTopMargin(-mHeaderViewHeight);
}
} else if (mPullState == PULL_UP_STATE) {
if (Math.abs(topMargin) >= mHeaderViewHeight + mFooterViewHeight) {
// 开始执行footer 刷新
footerRefreshing();
} else {
// 还没有执行刷新,重新隐藏
setHeaderTopMargin(-mHeaderViewHeight);
}
}
break;
}
return super.onTouchEvent(event);
}
/**
* 是否应该到了父View,即PullToRefreshView滑动
*
* @param deltaY , deltaY > 0 是向下运动,< 0是向上运动
* @return
*/
private boolean isRefreshViewScroll(int deltaY) {
if (mHeaderState == REFRESHING || mFooterState == REFRESHING) {
return false;
}
// 对于ListView和GridView
if (mAdapterView != null) {
// 子view(ListView or GridView)滑动到最顶端
if (deltaY > 0) {
// 判断是否禁用下拉刷新操作
if (!enablePullTorefresh) {
return false;
}
View child = mAdapterView.getChildAt(0);
if (child == null) {
// 如果mAdapterView中没有数据,不拦截
return false;
}
if (mAdapterView.getFirstVisiblePosition() == 0 && child.getTop() == 0) {
mPullState = PULL_DOWN_STATE;
return true;
}
int top = child.getTop();
int padding = mAdapterView.getPaddingTop();
if (mAdapterView.getFirstVisiblePosition() == 0 && Math.abs(top - padding) <= 11) {// 这里之前用3可以判断,但现在不行,还没找到原因
mPullState = PULL_DOWN_STATE;
return true;
}
} else if (deltaY < 0) {
// 判断是否禁用上拉加载更多操作
if (!enablePullLoadMoreDataStatus) {
return false;
}
View lastChild = mAdapterView.getChildAt(mAdapterView.getChildCount() - 1);
if (lastChild == null) {
// 如果mAdapterView中没有数据,不拦截
return false;
}
// 最后一个子view的Bottom小于父View的高度说明mAdapterView的数据没有填满父view,
// 等于父View的高度说明mAdapterView已经滑动到最后
if (lastChild.getBottom() <= getHeight() && mAdapterView.getLastVisiblePosition() == mAdapterView.getCount() - 1) {
mPullState = PULL_UP_STATE;
return true;
}
}
}
// 对于ScrollView
if (mScrollView != null) {
// 子scroll view滑动到最顶端
View child = mScrollView.getChildAt(0);
if (deltaY > 0 && mScrollView.getScrollY() == 0) {
mPullState = PULL_DOWN_STATE;//下拉状态
return true;
} else if (deltaY < 0 && child.getMeasuredHeight() <= getHeight() + mScrollView.getScrollY()) {
mPullState = PULL_UP_STATE;//上拉状态
return true;
}
}
// RecyclerView
if (mRecyclerView != null) {
// mRecyclerView子view
View child = mRecyclerView.getChildAt(0);
if (child == null) {
// 如果 mRecyclerView 中没有数据,不拦截
return false;
}
int firstItemPosition = 0;
int lastItemPosition = 0;
LinearLayoutManager linearLayoutManager = null;
GridLayoutManager gridLayoutManager = null;
if (layoutManager == null) {
throw new IllegalArgumentException("must contain a LayoutManager in this RecyclerView!");
}
if (layoutManager instanceof LinearLayoutManager) {
linearLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
firstItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
lastItemPosition = linearLayoutManager.findLastVisibleItemPosition();
}
if (layoutManager instanceof GridLayoutManager) {
gridLayoutManager = (GridLayoutManager) mRecyclerView.getLayoutManager();
firstItemPosition = gridLayoutManager.findFirstVisibleItemPosition();
lastItemPosition = gridLayoutManager.findLastVisibleItemPosition();
}
if (deltaY > 0) {//下拉状态
if (firstItemPosition != 0) {
// 还不是最顶一行,把上一行显示出来
// mRecyclerView.smoothScrollToPosition(firstItemPosition);
return false;
} else {
mPullState = PULL_DOWN_STATE;//下拉状态
return true;
}
} else if (deltaY < 0) {//上拉状态
if (linearLayoutManager != null) {
if (lastItemPosition != linearLayoutManager.getItemCount() - 1) {
// 还不是最后一行,把下一行显示出来
// mRecyclerView.smoothScrollToPosition(lastItemPosition + 1);
return false;
} else {
mPullState = PULL_UP_STATE;//上拉状态
return true;
}
}
if (gridLayoutManager != null) {
if (lastItemPosition != gridLayoutManager.getItemCount() - 1) {
// 还不是最后一行,把下一行显示出来
mRecyclerView.smoothScrollToPosition(lastItemPosition + 1);
return false;
} else {
mPullState = PULL_UP_STATE;//上拉状态
return true;
}
}
}
}
return false;
}
通过拦截滑动事件判断滑动方向,判断是否加载显示头布局和脚布局。
2、完整代码如下:
package com.zyd.recyclerviewdemo.view;
import android.widget.LinearLayout;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.zyd.recyclerviewdemo.R;
import java.util.Date;
public class PullToRefreshView extends LinearLayout {
private static final String TAG = "PullToRefreshView";
// refresh states
private static final int PULL_TO_REFRESH = 2;
private static final int RELEASE_TO_REFRESH = 3;
private static final int REFRESHING = 4;
// pull state
private static final int PULL_UP_STATE = 0;
private static final int PULL_DOWN_STATE = 1;
private boolean enablePullTorefresh = true;
private boolean enablePullLoadMoreDataStatus = true;
/**
* last y
*/
private int mLastMotionY;
/**
* lock
*/
private boolean mLock;
/**
* header view
*/
private View mHeaderView;
/**
* footer view
*/
private View mFooterView;
/**
* list or grid
*/
private AdapterView<?> mAdapterView;
/**
* scrollview
*/
private ScrollView mScrollView;
/**
* RecyclerView
*/
private RecyclerView mRecyclerView;
/**
* RecyclerView.LayoutManager
*/
private RecyclerView.LayoutManager layoutManager;
/**
* header view height
*/
private int mHeaderViewHeight;
/**
* footer view height
*/
private int mFooterViewHeight;
/**
* header view image
*/
private ImageView mHeaderImageView;
/**
* footer view image
*/
private ImageView mFooterImageView;
/**
* header tip text
*/
private TextView mHeaderTextView;
/**
* footer tip text
*/
private TextView mFooterTextView;
/**
* header refresh time
*/
private TextView mHeaderUpdateTextView;
/**
* footer refresh time
*/
// private TextView mFooterUpdateTextView;
/**
* header progress bar
*/
private ProgressBar mHeaderProgressBar;
/**
* footer progress bar
*/
private ProgressBar mFooterProgressBar;
/**
* layout inflater
*/
private LayoutInflater mInflater;
/**
* header view current state
*/
private int mHeaderState;
/**
* footer view current state
*/
private int mFooterState;
/**
* pull state,pull up or pull down;PULL_UP_STATE or PULL_DOWN_STATE
*/
private int mPullState;
/**
* 变为向下的箭头,改变箭头方向
*/
private RotateAnimation mFlipAnimation;
/**
* 变为逆向的箭头,旋转
*/
private RotateAnimation mReverseFlipAnimation;
/**
* footer refresh listener
*/
private PullToRefreshView.OnFooterRefreshListener mOnFooterRefreshListener;
/**
* footer refresh listener
*/
private PullToRefreshView.OnHeaderRefreshListener mOnHeaderRefreshListener;
/**
* last update time
*/
// private String mLastUpdateTime;
public PullToRefreshView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PullToRefreshView(Context context) {
super(context);
init();
}
/**
* init
*
* @param
* @description
*/
private void init() {
// Load all of the animations we need in code rather than through XML
mFlipAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mFlipAnimation.setInterpolator(new LinearInterpolator());
mFlipAnimation.setDuration(250);
mFlipAnimation.setFillAfter(true);
mReverseFlipAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
mReverseFlipAnimation.setDuration(250);
mReverseFlipAnimation.setFillAfter(true);
mInflater = LayoutInflater.from(getContext());
// header view 在此添加,保证是第一个添加到linearlayout的最上端
addHeaderView();
}
private void addHeaderView() {
// header view
mHeaderView = mInflater.inflate(R.layout.pull_head, this, false);
mHeaderImageView = (ImageView) mHeaderView.findViewById(R.id.pull_to_load_image);
mHeaderTextView = (TextView) mHeaderView.findViewById(R.id.pull_to_load_text);
mHeaderUpdateTextView = (TextView) mHeaderView.findViewById(R.id.pull_to_refresh_updated_at);
mHeaderProgressBar = (ProgressBar) mHeaderView.findViewById(R.id.pull_to_load_progress);
// header layout
measureView(mHeaderView);
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mHeaderViewHeight);
// 设置topMargin的值为负的header View高度,即将其隐藏在最上方
params.topMargin = -(mHeaderViewHeight);
// mHeaderView.setLayoutParams(params1);
addView(mHeaderView, params);
}
private void addFooterView() {
// footer view
mFooterView = mInflater.inflate(R.layout.pull_foot, this, false);
mFooterImageView = (ImageView) mFooterView.findViewById(R.id.pull_foot_image);
mFooterTextView = (TextView) mFooterView.findViewById(R.id.pull_foot_text);
mFooterProgressBar = (ProgressBar) mFooterView.findViewById(R.id.pull_fot_progress);
// footer layout
measureView(mFooterView);
mFooterViewHeight = mFooterView.getMeasuredHeight();
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mFooterViewHeight);
// int top = getHeight();
// params.topMargin
// =getHeight();//在这里getHeight()==0,但在onInterceptTouchEvent()方法里getHeight()已经有值了,不再是0;
// getHeight()什么时候会赋值,稍候再研究一下
// 由于是线性布局可以直接添加,只要AdapterView的高度是MATCH_PARENT,那么footer view就会被添加到最后,并隐藏
addView(mFooterView, params);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// footer view 在此添加保证添加到linearlayout中的最后
addFooterView();
initContentAdapterView();
}
/**
* init AdapterView like ListView,GridView and so on;or init ScrollView
*/
private void initContentAdapterView() {
int count = getChildCount();
if (count < 3) {
throw new IllegalArgumentException("this layout must contain 3 child views,and AdapterView or ScrollView must in the second position!");
}
View view = null;
for (int i = 0; i < count - 1; ++i) {
view = getChildAt(i);
if (view instanceof AdapterView<?>) {
mAdapterView = (AdapterView<?>) view;
}
if (view instanceof ScrollView) {
// finish later
mScrollView = (ScrollView) view;
}
if (view instanceof RecyclerView) {
mRecyclerView = (RecyclerView) view;
}
}
if (mAdapterView == null && mScrollView == null && mRecyclerView == null) {
throw new IllegalArgumentException("must contain a AdapterView or ScrollView and RecyclerView in this layout!");
}
}
private void measureView(View child) {
ViewGroup.LayoutParams p = child.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthSpec, childHeightSpec);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
int y = (int) e.getRawY();
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
// 首先拦截down事件,记录y坐标
mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
// deltaY > 0 是向下运动,< 0是向上运动
int deltaY = y - mLastMotionY;
if (isRefreshViewScroll(deltaY)) {
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
}
return false;
}
/*
* 如果在onInterceptTouchEvent()方法中没有拦截(即onInterceptTouchEvent()方法中 return
* false)则由PullToRefreshView 的子View来处理;否则由下面的方法来处理(即由PullToRefreshView自己来处理)
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mLock) {
return true;
}
int y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// onInterceptTouchEvent已经记录
// mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
int deltaY = y - mLastMotionY;
if (mPullState == PULL_DOWN_STATE) {
// PullToRefreshView执行下拉
Log.i(TAG, " pull down!parent view move!");
headerPrepareToRefresh(deltaY);
// setHeaderPadding(-mHeaderViewHeight);
} else if (mPullState == PULL_UP_STATE) {
// PullToRefreshView执行上拉
Log.i(TAG, "pull up!parent view move!");
footerPrepareToRefresh(deltaY);
}
mLastMotionY = y;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
int topMargin = getHeaderTopMargin();
if (mPullState == PULL_DOWN_STATE) {
if (topMargin >= 0) {
// 开始刷新
headerRefreshing();
} else {
// 还没有执行刷新,重新隐藏
setHeaderTopMargin(-mHeaderViewHeight);
}
} else if (mPullState == PULL_UP_STATE) {
if (Math.abs(topMargin) >= mHeaderViewHeight + mFooterViewHeight) {
// 开始执行footer 刷新
footerRefreshing();
} else {
// 还没有执行刷新,重新隐藏
setHeaderTopMargin(-mHeaderViewHeight);
}
}
break;
}
return super.onTouchEvent(event);
}
/**
* 是否应该到了父View,即PullToRefreshView滑动
*
* @param deltaY , deltaY > 0 是向下运动,< 0是向上运动
* @return
*/
private boolean isRefreshViewScroll(int deltaY) {
if (mHeaderState == REFRESHING || mFooterState == REFRESHING) {
return false;
}
// 对于ListView和GridView
if (mAdapterView != null) {
// 子view(ListView or GridView)滑动到最顶端
if (deltaY > 0) {
// 判断是否禁用下拉刷新操作
if (!enablePullTorefresh) {
return false;
}
View child = mAdapterView.getChildAt(0);
if (child == null) {
// 如果mAdapterView中没有数据,不拦截
return false;
}
if (mAdapterView.getFirstVisiblePosition() == 0 && child.getTop() == 0) {
mPullState = PULL_DOWN_STATE;
return true;
}
int top = child.getTop();
int padding = mAdapterView.getPaddingTop();
if (mAdapterView.getFirstVisiblePosition() == 0 && Math.abs(top - padding) <= 11) {// 这里之前用3可以判断,但现在不行,还没找到原因
mPullState = PULL_DOWN_STATE;
return true;
}
} else if (deltaY < 0) {
// 判断是否禁用上拉加载更多操作
if (!enablePullLoadMoreDataStatus) {
return false;
}
View lastChild = mAdapterView.getChildAt(mAdapterView.getChildCount() - 1);
if (lastChild == null) {
// 如果mAdapterView中没有数据,不拦截
return false;
}
// 最后一个子view的Bottom小于父View的高度说明mAdapterView的数据没有填满父view,
// 等于父View的高度说明mAdapterView已经滑动到最后
if (lastChild.getBottom() <= getHeight() && mAdapterView.getLastVisiblePosition() == mAdapterView.getCount() - 1) {
mPullState = PULL_UP_STATE;
return true;
}
}
}
// 对于ScrollView
if (mScrollView != null) {
// 子scroll view滑动到最顶端
View child = mScrollView.getChildAt(0);
if (deltaY > 0 && mScrollView.getScrollY() == 0) {
mPullState = PULL_DOWN_STATE;//下拉状态
return true;
} else if (deltaY < 0 && child.getMeasuredHeight() <= getHeight() + mScrollView.getScrollY()) {
mPullState = PULL_UP_STATE;//上拉状态
return true;
}
}
// RecyclerView
if (mRecyclerView != null) {
// mRecyclerView子view
View child = mRecyclerView.getChildAt(0);
if (child == null) {
// 如果 mRecyclerView 中没有数据,不拦截
return false;
}
int firstItemPosition = 0;
int lastItemPosition = 0;
LinearLayoutManager linearLayoutManager = null;
GridLayoutManager gridLayoutManager = null;
if (layoutManager == null) {
throw new IllegalArgumentException("must contain a LayoutManager in this RecyclerView!");
}
if (layoutManager instanceof LinearLayoutManager) {
linearLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
firstItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
lastItemPosition = linearLayoutManager.findLastVisibleItemPosition();
}
if (layoutManager instanceof GridLayoutManager) {
gridLayoutManager = (GridLayoutManager) mRecyclerView.getLayoutManager();
firstItemPosition = gridLayoutManager.findFirstVisibleItemPosition();
lastItemPosition = gridLayoutManager.findLastVisibleItemPosition();
}
if (deltaY > 0) {//下拉状态
if (firstItemPosition != 0) {
// 还不是最顶一行,把上一行显示出来
// mRecyclerView.smoothScrollToPosition(firstItemPosition);
return false;
} else {
mPullState = PULL_DOWN_STATE;//下拉状态
return true;
}
} else if (deltaY < 0) {//上拉状态
if (linearLayoutManager != null) {
if (lastItemPosition != linearLayoutManager.getItemCount() - 1) {
// 还不是最后一行,把下一行显示出来
// mRecyclerView.smoothScrollToPosition(lastItemPosition + 1);
return false;
} else {
mPullState = PULL_UP_STATE;//上拉状态
return true;
}
}
if (gridLayoutManager != null) {
if (lastItemPosition != gridLayoutManager.getItemCount() - 1) {
// 还不是最后一行,把下一行显示出来
mRecyclerView.smoothScrollToPosition(lastItemPosition + 1);
return false;
} else {
mPullState = PULL_UP_STATE;//上拉状态
return true;
}
}
}
}
return false;
}
/**
* header 准备刷新,手指移动过程,还没有释放
*
* @param deltaY ,手指滑动的距离
*/
private void headerPrepareToRefresh(int deltaY) {
int newTopMargin = changingHeaderViewTopMargin(deltaY);
// 当header view的topMargin>=0时,说明已经完全显示出来了,修改header view 的提示状态
if (newTopMargin >= 0 && mHeaderState != RELEASE_TO_REFRESH) {
mHeaderTextView.setText("松手可刷新");
mHeaderUpdateTextView.setVisibility(View.VISIBLE);
mHeaderImageView.clearAnimation();
mHeaderImageView.startAnimation(mFlipAnimation);
mHeaderState = RELEASE_TO_REFRESH;
} else if (newTopMargin < 0 && newTopMargin > -mHeaderViewHeight) {// 拖动时没有释放
mHeaderImageView.clearAnimation();
mHeaderImageView.startAnimation(mFlipAnimation);
// mHeaderImageView.
mHeaderTextView.setText("下拉刷新");
mHeaderState = PULL_TO_REFRESH;
}
}
/**
* footer 准备刷新,手指移动过程,还没有释放 移动footer view高度同样和移动header view
* 高度是一样,都是通过修改header view的topmargin的值来达到
*
* @param deltaY ,手指滑动的距离
*/
private void footerPrepareToRefresh(int deltaY) {
int newTopMargin = changingHeaderViewTopMargin(deltaY);
// 如果header view topMargin 的绝对值大于或等于header + footer 的高度
// 说明footer view 完全显示出来了,修改footer view 的提示状态
Log.i(TAG, "移动的距离" + deltaY + "=======" + (mHeaderViewHeight + mFooterViewHeight) + "-----------------------" + mFooterState);
if (Math.abs(newTopMargin) >= (mHeaderViewHeight + mFooterViewHeight) && mFooterState != RELEASE_TO_REFRESH) {
Log.i(TAG, "松手可加载更多");
mFooterTextView.setText("松手可加载更多");
mFooterImageView.clearAnimation();
mFooterImageView.startAnimation(mFlipAnimation);
mFooterState = RELEASE_TO_REFRESH;
} else if (Math.abs(newTopMargin) < (mHeaderViewHeight + mFooterViewHeight)) {
Log.i(TAG, "上拉可加载更多");
mFooterImageView.clearAnimation();
mFooterImageView.startAnimation(mFlipAnimation);
mFooterTextView.setText("上拉可加载更多");
mFooterState = PULL_TO_REFRESH;
}
}
/**
* 修改Header view top margin的值
*
* @param deltaY
* @description
*/
private int changingHeaderViewTopMargin(int deltaY) {
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
float newTopMargin = params.topMargin + deltaY * 0.3f;
// 这里对上拉做一下限制,因为当前上拉后然后不释放手指直接下拉,会把下拉刷新给触发了,感谢网友yufengzungzhe的指出
// 表示如果是在上拉后一段距离,然后直接下拉
if (deltaY > 0 && mPullState == PULL_UP_STATE && Math.abs(params.topMargin) <= mHeaderViewHeight) {
return params.topMargin;
}
// 同样地,对下拉做一下限制,避免出现跟上拉操作时一样的bug
if (deltaY < 0 && mPullState == PULL_DOWN_STATE && Math.abs(params.topMargin) >= mHeaderViewHeight) {
return params.topMargin;
}
params.topMargin = (int) newTopMargin;
mHeaderView.setLayoutParams(params);
invalidate();
return params.topMargin;
}
/**
* header refreshing
*/
public void headerRefreshing() {
mHeaderState = REFRESHING;
setHeaderTopMargin(0);
mHeaderImageView.setVisibility(View.GONE);
mHeaderImageView.clearAnimation();
mHeaderProgressBar.setVisibility(View.VISIBLE);
mHeaderTextView.setText("正在刷新");
if (mOnHeaderRefreshListener != null) {
mOnHeaderRefreshListener.onHeaderRefresh(this);
}
}
/**
* footer refreshing
*/
private void footerRefreshing() {
mFooterState = REFRESHING;
int top = mHeaderViewHeight + mFooterViewHeight;
setHeaderTopMargin(-top);
mFooterImageView.setVisibility(View.GONE);
mFooterImageView.clearAnimation();
mFooterProgressBar.setVisibility(View.VISIBLE);
mFooterTextView.setText("正在加载。。");
if (mOnFooterRefreshListener != null) {
mOnFooterRefreshListener.onFooterRefresh(this);
}
}
/**
* 设置header view 的topMargin的值
*
* @param topMargin ,为0时,说明header view 刚好完全显示出来; 为-mHeaderViewHeight时,说明完全隐藏了
* @description
*/
private void setHeaderTopMargin(int topMargin) {
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
params.topMargin = topMargin;
mHeaderView.setLayoutParams(params);
invalidate();
}
/**
* header view 完成更新后恢复初始状态
*/
public void onHeaderRefreshComplete() {
setHeaderTopMargin(-mHeaderViewHeight);
mHeaderImageView.setVisibility(View.VISIBLE);
mHeaderTextView.setText("下拉刷新");
mHeaderProgressBar.setVisibility(View.GONE);
mHeaderState = PULL_TO_REFRESH;
setLastUpdated("最近更新:" + new Date().toLocaleString());
}
/**
* Resets the list to a normal state after a refresh.
*
* @param lastUpdated Last updated at.
*/
public void onHeaderRefreshComplete(CharSequence lastUpdated) {
setLastUpdated(lastUpdated);
onHeaderRefreshComplete();
}
/**
* footer view 完成更新后恢复初始状态
*/
public void onFooterRefreshComplete() {
setHeaderTopMargin(-mHeaderViewHeight);
mFooterImageView.setVisibility(View.VISIBLE);
mFooterTextView.setText("加载完成,点击可加载更多");
mFooterProgressBar.setVisibility(View.GONE);
// mHeaderUpdateTextView.setText("");
mFooterState = PULL_TO_REFRESH;
}
/**
* footer view 完成更新后恢复初始状态
*/
public void onFooterRefreshComplete(int size) {
if (size > 0) {
mFooterView.setVisibility(View.VISIBLE);
} else {
mFooterView.setVisibility(View.GONE);
}
setHeaderTopMargin(-mHeaderViewHeight);
mFooterImageView.setVisibility(View.VISIBLE);
mFooterImageView.setImageResource(R.drawable.ic_pulltorefresh_arrow);
mFooterTextView.setText("上拉可加载更多");
mFooterProgressBar.setVisibility(View.GONE);
// mHeaderUpdateTextView.setText("");
mFooterState = PULL_TO_REFRESH;
}
/**
* Set a text to represent when the list was last updated.
*
* @param lastUpdated Last updated at.
*/
public void setLastUpdated(CharSequence lastUpdated) {
if (lastUpdated != null) {
mHeaderUpdateTextView.setVisibility(View.VISIBLE);
mHeaderUpdateTextView.setText(lastUpdated);
} else {
mHeaderUpdateTextView.setVisibility(View.GONE);
}
}
/**
* 获取当前header view 的topMargin
*
* @description
*/
private int getHeaderTopMargin() {
LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
return params.topMargin;
}
// /**
// * lock
// *
// */
// private void lock() {
// mLock = true;
// }
//
// /**
// * unlock
// *
// */
// private void unlock() {
// mLock = false;
// }
/**
* set headerRefreshListener
*
* @param headerRefreshListener
* @description
*/
public void setOnHeaderRefreshListener(PullToRefreshView.OnHeaderRefreshListener headerRefreshListener) {
mOnHeaderRefreshListener = headerRefreshListener;
}
public void setOnFooterRefreshListener(PullToRefreshView.OnFooterRefreshListener footerRefreshListener) {
mOnFooterRefreshListener = footerRefreshListener;
}
/**
* Interface definition for a callback to be invoked when list/grid footer
* view should be refreshed.
*/
public interface OnFooterRefreshListener {
public void onFooterRefresh(PullToRefreshView view);
}
/**
* Interface definition for a callback to be invoked when list/grid header
* view should be refreshed.
*/
public interface OnHeaderRefreshListener {
public void onHeaderRefresh(PullToRefreshView view);
}
public boolean isEnablePullTorefresh() {
return enablePullTorefresh;
}
public void setEnablePullTorefresh(boolean enablePullTorefresh) {
handler.sendEmptyMessage(0);
System.out.println("setEnablePullTorefresh------------------------下拉刷新结束");
this.enablePullTorefresh = enablePullTorefresh;
}
public boolean isEnablePullLoadMoreDataStatus() {
return enablePullLoadMoreDataStatus;
}
public void setEnablePullLoadMoreDataStatus(boolean enablePullLoadMoreDataStatus) {
handler.sendEmptyMessage(1);
this.enablePullLoadMoreDataStatus = enablePullLoadMoreDataStatus;
}
public RecyclerView.LayoutManager getLayoutManager() {
return layoutManager;
}
public void setLayoutManager(RecyclerView.LayoutManager layoutManager) {
this.layoutManager = layoutManager;
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
setHeaderTopMargin(-mHeaderViewHeight);
mHeaderState = PULL_TO_REFRESH;
mHeaderProgressBar.setVisibility(View.GONE);
} else if (msg.what == 1) {
mFooterProgressBar.setVisibility(View.GONE);
onFooterRefreshComplete(1);
}
}
};
}
四、创建SimpleRecyclerViewAdapter
public class SimpleRecyclerViewAdapter extends RecyclerView.Adapter {
private LayoutInflater inflater;
private List<String> listItem;
// private MyItemClickListener myItemClickListener;
public SimpleRecyclerViewAdapter(Context context, List<String> listItem) {
inflater = LayoutInflater.from(context);
this.listItem = listItem;
}//构造函数,传入数据
//定义Viewholder
class Viewholder extends RecyclerView.ViewHolder {
private TextView Text;
public Viewholder(View root) {
super(root);
Text = (TextView) root.findViewById(R.id.tv_item);
// root.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// if (myItemClickListener != null)
// myItemClickListener .onItemClick(v,getPosition());
// }
//
// }//监听到点击就回调MainActivity的onItemClick函数
// );
}
public TextView getText() {
return Text;
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new Viewholder(inflater.inflate(R.layout.adapter_load_more, null));
}//在这里把ViewHolder绑定Item的布局
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
Viewholder vh = (Viewholder) holder;
vh.Text.setText((String) listItem.get(position));
}//在这里绑定数据到ViewHolder里面
@Override
public int getItemCount() {
return listItem.size();
}//返回Item数目
//
// public void setOnItemClickListener(MyItemClickListener listener) {
// myItemClickListener = listener;
// }//绑定MainActivity传进来的点击监听器
}
五、创建Activity加载数据
1、activity_recycler_pull_refresh.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/purple_500"
app:popupTheme="@style/ToolbarPopupTheme"
app:theme="@style/ThemeOverlay.AppCompat.Dark"
app:titleTextColor="@android:color/white" />
<com.zyd.recyclerviewdemo.view.PullToRefreshView
android:id="@+id/pull_to_refresh"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--使用红色分隔条-->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</com.zyd.recyclerviewdemo.view.PullToRefreshView>
</LinearLayout>
2、RecyclerPullToRefreshActivity
public class RecyclerPullToRefreshActivity extends AppCompatActivity implements PullToRefreshView.OnFooterRefreshListener,PullToRefreshView.OnHeaderRefreshListener {
private Toolbar toolbar;
private List<String> dataList = new ArrayList<>();
private RecyclerView recyclerView;
private SimpleRecyclerViewAdapter adapter;
private PullToRefreshView pull_to_refresh;
private Handler handler=new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler_pull_refresh);
init();
}
private void init() {
toolbar = findViewById(R.id.toolbar);
// 使用Toolbar替换ActionBar
setSupportActionBar(toolbar);
recyclerView = findViewById(R.id.recyclerView);
pull_to_refresh= findViewById(R.id.pull_to_refresh);
// 模拟获取数据
getData();
adapter = new SimpleRecyclerViewAdapter(this,dataList);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
pull_to_refresh.setLayoutManager(linearLayoutManager);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
pull_to_refresh.setOnFooterRefreshListener(this);
pull_to_refresh.setOnHeaderRefreshListener(this);
}
private void getData() {
String letter = "Item";
for (int i = 0; i < 50; i++) {
dataList.add(letter + i);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.layout_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.liner:
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
pull_to_refresh.setLayoutManager(linearLayoutManager);
break;
case R.id.grid:
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(gridLayoutManager);
pull_to_refresh.setLayoutManager(gridLayoutManager);
break;
}
dataList.clear();
getData();
return super.onOptionsItemSelected(item);
}
@Override
public void onFooterRefresh(PullToRefreshView view) {
Log.w("SwipeRecyclerActivity","加载完成");
handler.postDelayed(new Runnable() {
@Override
public void run() {
getData();
adapter.notifyDataSetChanged();
pull_to_refresh.onFooterRefreshComplete();
}
},2000);
}
@Override
public void onHeaderRefresh(PullToRefreshView view) {
Log.w("SwipeRecyclerActivity","刷新完成");
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (dataList.size()>0){
dataList.clear();
getData();
}
pull_to_refresh.onHeaderRefreshComplete();
}
}, 2000);
}
}
完整代码及相关素材:https://toscode.gitee.com/zyd_gitee/recycler-view-refresh-demo.git
网友评论