項目地址:https://github.com/moscoper/PullToRefresh.git
前言
一般的刷新和加載更多的效果都是豎直方向的(下拉刷新和上拉加載更多),本篇文章就利用 PullToReFresh 和 RecycleView 來實現水平方向的刷新和加載更多的效果。我們暫且叫它 PullToReshHorizontalRecycleView。
PullToReshHorizontalRecycleView
我們在 PullToRefresh 的擴展 這篇文章中介紹過如何利用 PullToReFresh 對其他可以滑動的控件添加下拉和上拉的效果。PullToReshHorizontalRecycleView 的實現和其類似。
繼承 PullToRefreshBase
新建類 PullToReshHorizontalRecycleView
繼承 PullToRefreshBase<RecyclerView>
。並實現 getPullToRefreshScrollDirection
,createRefreshableView
,isReadyForPullStart
和 isReadyForPullEnd
這四個方法。
getPullToRefreshScrollDirection
方法 getPullToRefreshScrollDirection
的返回值決定了滑動的方向。返回 Orientation.HORIZONTAL
時爲水平滑動;返回Orientation.VERTICAL
時爲豎直滑動。這裏我們需要的是水平方向的滑動所以返回Orientation.HORIZONTAL
。
@Override public Orientation getPullToRefreshScrollDirection() {
return Orientation.HORIZONTAL;
}
createRefreshableView
方法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
方法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
方法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<RecyclerView> {
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 的控件是一樣的,這裏不再贅述。