项目地址:https://github.com/moscoper/PullToRefresh.git
一般的刷新和加载更多的效果都是竖直方向的(下拉刷新和上拉加载更多),本篇文章就利用 PullToReFresh 和 RecycleView 来实现水平方向的刷新和加载更多的效果。我们暂且叫它 PullToReshHorizontalRecycleView。
我们在 PullToRefresh 的扩展 这篇文章中介绍过如何利用 PullToReFresh 对其他可以滑动的控件添加下拉和上拉的效果。PullToReshHorizontalRecycleView 的实现和其类似。
新建类 PullToReshHorizontalRecycleView
继承 PullToRefreshBase
。并实现 getPullToRefreshScrollDirection
,createRefreshableView
,isReadyForPullStart
和 isReadyForPullEnd
这四个方法。
方法 getPullToRefreshScrollDirection
的返回值决定了滑动的方向。返回 Orientation.HORIZONTAL
时为水平滑动;返回Orientation.VERTICAL
时为竖直滑动。这里我们需要的是水平方向的滑动所以返回Orientation.HORIZONTAL
。
@Override public Orientation getPullToRefreshScrollDirection() {
return Orientation.HORIZONTAL;
}
方法createRefreshableView
的返回值是要添加刷新效果的控件,这里我们需要为横向滑动的 RecycleView 添加刷新效果所以就返回横向滑动的 RecycleView。
@Override protected RecyclerView createRefreshableView(Context context, AttributeSet attrs) {
RecyclerView recyclerView = new RecyclerView(context,attrs);
LinearLayoutManager mannagerTwo = new LinearLayoutManager(context);
mannagerTwo.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(mannagerTwo);
return recyclerView;
}
方法isReadyForPullStart
返回true
时表示可以开始刷新的效果了;返回false
时表示还不满足可以触发刷新的效果。我们这里触发展示刷新效果的条件是 RecycleView 的第一个 item 的左边缘在屏幕左边缘的右侧。所以isReadyForPullStart
的具体实现可以如下:
@Override protected boolean isReadyForPullStart() {
return isFirstItemVisible();
}
private boolean isFirstItemVisible() {
final RecyclerView.Adapter adapter = mRefreshableView.getAdapter();
if (null == adapter || adapter.getItemCount() ==0) {
if (DEBUG) {
Log.d(LOG_TAG, "isFirstItemVisible. Empty View.");
}
return true;
} else {
/**
* This check should really just be:
* mRefreshableView.getFirstVisiblePosition() == 0, but PtRListView
* internally use a HeaderView which messes the positions up. For
* now we'll just add one to account for it and rely on the inner
* condition which checks getTop().
*/
if (getFirstVisiblePosition() <= 1) {
final View firstVisibleChild = mRefreshableView.getChildAt(0);
if (firstVisibleChild != null) {
return firstVisibleChild.getLeft() >= mRefreshableView.getLeft();
}
}
}
return false;
}
方法isReadyForPullEnd
返回true
时表示满足触发加载更多效果的条件,否则表示不满足。这里满足触发加载更多效果的条件是 RecycleView 最后一个 item 的右边缘在屏幕右边缘的左边。所以isReadyForPullEnd
的实现如下:
@Override protected boolean isReadyForPullEnd() {
return isLastItemVisible();
}
private boolean isLastItemVisible() {
final RecyclerView.Adapter adapter = mRefreshableView.getAdapter();
if (null == adapter || adapter.getItemCount()==0) {
if (DEBUG) {
Log.d(LOG_TAG, "isLastItemVisible. Empty View.");
}
return true;
} else {
final int lastItemPosition = adapter.getItemCount() - 1;
final int lastVisiblePosition = getLastVisiblePosition();
if (DEBUG) {
Log.d(LOG_TAG, "isLastItemVisible. Last Item Position: "
+ lastItemPosition
+ " Last Visible Pos: "
+ lastVisiblePosition);
}
/**
* This check should really just be: lastVisiblePosition ==
* lastItemPosition, but PtRListView internally uses a FooterView
* which messes the positions up. For me we'll just subtract one to
* account for it and rely on the inner condition which checks
* getBottom().
*/
if (lastVisiblePosition >= lastItemPosition - 1) {
final int childIndex = lastVisiblePosition - getFirstVisiblePosition();
final View lastVisibleChild = mRefreshableView.getChildAt(childIndex);
if (lastVisibleChild != null) {
return lastVisibleChild.getRight() <= mRefreshableView.getRight();
}
}
}
return false;
}
public class PullRefreshRecyclerView extends PullToRefreshBase {
public PullRefreshRecyclerView(Context context, Mode mode) {
super(context, mode);
}
public PullRefreshRecyclerView(Context context) {
super(context);
}
public PullRefreshRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PullRefreshRecyclerView(Context context, Mode mode, AnimationStyle animStyle) {
super(context, mode, animStyle);
}
@Override public Orientation getPullToRefreshScrollDirection() {
return Orientation.HORIZONTAL;
}
@Override protected RecyclerView createRefreshableView(Context context, AttributeSet attrs) {
RecyclerView recyclerView = new RecyclerView(context,attrs);
LinearLayoutManager mannagerTwo = new LinearLayoutManager(context);
mannagerTwo.setOrientation(LinearLayoutManager.HORIZONTAL);
//recyclerView.addItemDecoration(
// new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL_LIST));
recyclerView.setLayoutManager(mannagerTwo);
return recyclerView;
}
public void addItemDecoration(RecyclerView.ItemDecoration itemDecoration){
mRefreshableView.addItemDecoration(itemDecoration);
}
public void setAdapter(RecyclerView.Adapter adapter){
mRefreshableView.setAdapter(adapter);
}
@Override protected boolean isReadyForPullEnd() {
return isLastItemVisible();
}
@Override protected boolean isReadyForPullStart() {
return isFirstItemVisible();
}
private boolean isLastItemVisible() {
final RecyclerView.Adapter adapter = mRefreshableView.getAdapter();
if (null == adapter || adapter.getItemCount()==0) {
if (DEBUG) {
Log.d(LOG_TAG, "isLastItemVisible. Empty View.");
}
return true;
} else {
final int lastItemPosition = adapter.getItemCount() - 1;
final int lastVisiblePosition = getLastVisiblePosition();
if (DEBUG) {
Log.d(LOG_TAG, "isLastItemVisible. Last Item Position: "
+ lastItemPosition
+ " Last Visible Pos: "
+ lastVisiblePosition);
}
/**
* This check should really just be: lastVisiblePosition ==
* lastItemPosition, but PtRListView internally uses a FooterView
* which messes the positions up. For me we'll just subtract one to
* account for it and rely on the inner condition which checks
* getBottom().
*/
if (lastVisiblePosition >= lastItemPosition - 1) {
final int childIndex = lastVisiblePosition - getFirstVisiblePosition();
final View lastVisibleChild = mRefreshableView.getChildAt(childIndex);
if (lastVisibleChild != null) {
return lastVisibleChild.getRight() <= mRefreshableView.getRight();
}
}
}
return false;
}
private int getFirstVisiblePosition(){
int position = 0;
RecyclerView.LayoutManager manager = mRefreshableView.getLayoutManager();
if (manager instanceof LinearLayoutManager){
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) manager;
return linearLayoutManager.findFirstVisibleItemPosition();
}
return position;
}
private int getLastVisiblePosition(){
int position = 0;
RecyclerView.LayoutManager manager = mRefreshableView.getLayoutManager();
if (manager instanceof LinearLayoutManager){
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) manager;
return linearLayoutManager.findLastVisibleItemPosition();
}
return position;
}
private boolean isFirstItemVisible() {
final RecyclerView.Adapter adapter = mRefreshableView.getAdapter();
if (null == adapter || adapter.getItemCount() ==0) {
if (DEBUG) {
Log.d(LOG_TAG, "isFirstItemVisible. Empty View.");
}
return true;
} else {
/**
* This check should really just be:
* mRefreshableView.getFirstVisiblePosition() == 0, but PtRListView
* internally use a HeaderView which messes the positions up. For
* now we'll just add one to account for it and rely on the inner
* condition which checks getTop().
*/
if (getFirstVisiblePosition() <= 1) {
final View firstVisibleChild = mRefreshableView.getChildAt(0);
if (firstVisibleChild != null) {
return firstVisibleChild.getLeft() >= mRefreshableView.getLeft();
}
}
}
return false;
}
}
PullToReshHorizontalRecycleView 的用法和原有的 PullToReFresh 的控件是一样的,这里不再赘述。