Android高级UI开发 RecyclerView高级进阶(八)下拉刷新,上拉加载
【摘要】 Listview的下拉刷新,上拉加载我们常用的框架是pullToRefresh。今天我们来介绍RecyclerView如何实现下拉刷新,上拉加载。
源码下载: 源码
1. 下拉刷新
实现思路,无需我们写代码自定义下拉刷新控件,直接使用MaterialDesign5.0为我们提供的SwipeRefreshLayout控件。
布局一般为:
<android.supp...
Listview的下拉刷新,上拉加载我们常用的框架是pullToRefresh。今天我们来介绍RecyclerView如何实现下拉刷新,上拉加载。
源码下载: 源码
1. 下拉刷新
实现思路,无需我们写代码自定义下拉刷新控件,直接使用MaterialDesign5.0为我们提供的SwipeRefreshLayout控件。
布局一般为:
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<com.yanzhenjie.recyclerview.swipe.SwipeMenuRecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.v4.widget.SwipeRefreshLayout>
2. 上拉加载
实现思路, 需要我们自己写代码来实现。在这里我们先贴出MainActivity的代码(使用RecyclerView控件的核心代码)
/*
* Copyright 2017 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.anyikang.volunteer.sos.recyclerview22.activity.load;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import com.anyikang.volunteer.sos.recyclerview22.R;
import com.anyikang.volunteer.sos.recyclerview22.adapter.MainAdapter;
import com.yanzhenjie.recyclerview.swipe.SwipeItemClickListener;
import com.yanzhenjie.recyclerview.swipe.SwipeMenuRecyclerView;
import com.yanzhenjie.recyclerview.swipe.widget.DefaultItemDecoration;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* 默认的加载更多的View。
* </p>
*
*/
public class DefaultActivity extends AppCompatActivity {
private SwipeRefreshLayout mRefreshLayout;
private SwipeMenuRecyclerView mRecyclerView;
private MainAdapter mAdapter;
private List<String> mDataList;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_refresh_loadmore);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
assert actionBar != null;
actionBar.setDisplayHomeAsUpEnabled(true);
mRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.refresh_layout);
mRefreshLayout.setOnRefreshListener(mRefreshListener); // 刷新监听。
mRecyclerView = (SwipeMenuRecyclerView) findViewById(R.id.recycler_view);
// mRecyclerView.setLayoutManager(new GridLayoutManager(this, 2));
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.addItemDecoration(new DefaultItemDecoration(ContextCompat.getColor(this, R.color.divider_color)));
mRecyclerView.setSwipeItemClickListener(mItemClickListener); // RecyclerView Item点击监听。
mRecyclerView.useDefaultLoadMore(); // 使用默认的加载更多的View。
mRecyclerView.setLoadMoreListener(mLoadMoreListener); // 加载更多的监听。
mAdapter = new MainAdapter(this);
mRecyclerView.setAdapter(mAdapter);
// 请求服务器加载数据。
loadData();
}
/**
* 刷新。
*/
private SwipeRefreshLayout.OnRefreshListener mRefreshListener = new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
mRecyclerView.postDelayed(new Runnable() {
@Override
public void run() {
loadData();
}
}, 1000); // 延时模拟请求服务器。
}
};
/**
* 加载更多。
*/
private SwipeMenuRecyclerView.LoadMoreListener mLoadMoreListener = new SwipeMenuRecyclerView.LoadMoreListener() {
@Override
public void onLoadMore() {
mRecyclerView.postDelayed(new Runnable() {
@Override
public void run() {
List<String> strings = createDataList(mAdapter.getItemCount());
mDataList.addAll(strings);
// notifyItemRangeInserted()或者notifyDataSetChanged().
mAdapter.notifyItemRangeInserted(mDataList.size() - strings.size(), strings.size());
// 数据完更多数据,一定要掉用这个方法。
// 第一个参数:表示此次数据是否为空。
// 第二个参数:表示是否还有更多数据。
mRecyclerView.loadMoreFinish(false, true);
// 如果加载失败调用下面的方法,传入errorCode和errorMessage。
// errorCode随便传,你自定义LoadMoreView时可以根据errorCode判断错误类型。
// errorMessage是会显示到loadMoreView上的,用户可以看到。
// mRecyclerView.loadMoreError(0, "请求网络失败");
}
}, 1000);
}
};
/**
* Item点击监听。
*/
private SwipeItemClickListener mItemClickListener = new SwipeItemClickListener() {
@Override
public void onItemClick(View itemView, int position) {
Toast.makeText(DefaultActivity.this, "第" + position + "个", Toast.LENGTH_SHORT).show();
}
};
/**
* 第一次加载数据。
*/
private void loadData() {
mDataList = createDataList(0);
mAdapter.notifyDataSetChanged(mDataList);
mRefreshLayout.setRefreshing(false);
// 第一次加载数据:一定要调用这个方法,否则不会触发加载更多。
// 第一个参数:表示此次数据是否为空,假如你请求到的list为空(== null || list.size == 0),那么这里就要true。
// 第二个参数:表示是否还有更多数据,根据服务器返回给你的page等信息判断是否还有更多,这样可以提供性能,如果不能判断则传true。
mRecyclerView.loadMoreFinish(false, true);
}
protected List<String> createDataList(int start) {
List<String> strings = new ArrayList<>();
for (int i = start; i < start + 100; i++) {
strings.add("第" + i + "个Item");
}
return strings;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
}
return true;
}
}
2.1分析代码
SwipeMenuRecyclerView mRecyclerView
从这个声明可以看出我们需要扩展一个自定义RecyclerView控件。
mRecyclerView.setLoadMoreListener(mLoadMoreListener); // 加载更多的监听
这行代码说明“上拉加载更多”这个事件的触发一定是在自定义RecyclerView里,即SwipeMenuRecyclerView。
而“上拉加载更多”这个事件触发后的 处理或者说响应代码是在mLoadMoreListener这个接口的实例对象里。
综上分析,主要看以下2个部分
(1)SwipeMenuRecyclerView这个自定义RecyclerView的代码,看其“上拉加载”事件是如何触发的。
(2)mLoadMoreListener接口的实现代码,已经在MainActivity里展示过了,mLoadMoreListener实现了onLoadMore函数。也就说在SwipeMenuRecyclerView里“上拉加载”事件触发时一定会回调MainActivity中的mLoadMoreListener的onLoadMore函数。
在这里我们贴出SwipeMenuRecyclerView的代码,分析一下“上拉加载”事件是如何触发的。
SwipeMenuRecyclerView.java:
/*
* Copyright 2016 Yan Zhenjie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.yanzhenjie.recyclerview.swipe;
import android.content.Context;
import android.support.annotation.IntDef;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import com.yanzhenjie.recyclerview.swipe.touch.DefaultItemTouchHelper;
import com.yanzhenjie.recyclerview.swipe.touch.OnItemMoveListener;
import com.yanzhenjie.recyclerview.swipe.touch.OnItemMovementListener;
import com.yanzhenjie.recyclerview.swipe.touch.OnItemStateChangedListener;
import com.yanzhenjie.recyclerview.swipe.widget.DefaultLoadMoreView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
/**
* Created by
*/
public class SwipeMenuRecyclerView extends RecyclerView {
/**
* Left menu.
*/
public static final int LEFT_DIRECTION = 1;
/**
* Right menu.
*/
public static final int RIGHT_DIRECTION = -1;
@IntDef({LEFT_DIRECTION, RIGHT_DIRECTION})
@Retention(RetentionPolicy.SOURCE)
public @interface DirectionMode {
}
/**
* Invalid position.
*/
private static final int INVALID_POSITION = -1;
protected int mScaleTouchSlop;
protected SwipeMenuLayout mOldSwipedLayout;
protected int mOldTouchedPosition = INVALID_POSITION;
private int mDownX;
private int mDownY;
private boolean allowSwipeDelete = false;
private DefaultItemTouchHelper mDefaultItemTouchHelper;
private SwipeMenuCreator mSwipeMenuCreator;
private SwipeMenuItemClickListener mSwipeMenuItemClickListener;
private SwipeItemClickListener mSwipeItemClickListener;
private SwipeItemLongClickListener mSwipeItemLongClickListener;
private SwipeAdapterWrapper mAdapterWrapper;
public SwipeMenuRecyclerView(Context context) {
this(context, null);
}
public SwipeMenuRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SwipeMenuRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScaleTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
private void initializeItemTouchHelper() {
if (mDefaultItemTouchHelper == null) {
mDefaultItemTouchHelper = new DefaultItemTouchHelper();
mDefaultItemTouchHelper.attachToRecyclerView(this);
}
}
/**
* Set OnItemMoveListener.
*
* @param onItemMoveListener {@link OnItemMoveListener}.
*/
public void setOnItemMoveListener(OnItemMoveListener onItemMoveListener) {
initializeItemTouchHelper();
this.mDefaultItemTouchHelper.setOnItemMoveListener(onItemMoveListener);
}
/**
* Set OnItemMovementListener.
*
* @param onItemMovementListener {@link OnItemMovementListener}.
*/
public void setOnItemMovementListener(OnItemMovementListener onItemMovementListener) {
initializeItemTouchHelper();
this.mDefaultItemTouchHelper.setOnItemMovementListener(onItemMovementListener);
}
/**
* Set OnItemStateChangedListener.
*
* @param onItemStateChangedListener {@link OnItemStateChangedListener}.
*/
public void setOnItemStateChangedListener(OnItemStateChangedListener onItemStateChangedListener) {
initializeItemTouchHelper();
this.mDefaultItemTouchHelper.setOnItemStateChangedListener(onItemStateChangedListener);
}
/**
* Set can long press drag.
*
* @param canDrag drag true, otherwise is can't.
*/
public void setLongPressDragEnabled(boolean canDrag) {
initializeItemTouchHelper();
this.mDefaultItemTouchHelper.setLongPressDragEnabled(canDrag);
}
/**
* Get can long press drag.
*
* @return drag true, otherwise is can't.
*/
public boolean isLongPressDragEnabled() {
initializeItemTouchHelper();
return this.mDefaultItemTouchHelper.isLongPressDragEnabled();
}
/**
* Set can swipe delete.
*
* @param canSwipe swipe true, otherwise is can't.
*/
public void setItemViewSwipeEnabled(boolean canSwipe) {
initializeItemTouchHelper();
allowSwipeDelete = canSwipe; // swipe and menu conflict.
this.mDefaultItemTouchHelper.setItemViewSwipeEnabled(canSwipe);
}
/**
* Get can long press swipe.
*
* @return swipe true, otherwise is can't.
*/
public boolean isItemViewSwipeEnabled() {
initializeItemTouchHelper();
return this.mDefaultItemTouchHelper.isItemViewSwipeEnabled();
}
/**
* Start drag a item.
*
* @param viewHolder the ViewHolder to start dragging. It must be a direct child of RecyclerView.
*/
public void startDrag(RecyclerView.ViewHolder viewHolder) {
initializeItemTouchHelper();
this.mDefaultItemTouchHelper.startDrag(viewHolder);
}
/**
* Star swipe a item.
*
* @param viewHolder the ViewHolder to start swiping. It must be a direct child of RecyclerView.
*/
public void startSwipe(RecyclerView.ViewHolder viewHolder) {
initializeItemTouchHelper();
this.mDefaultItemTouchHelper.startSwipe(viewHolder);
}
/**
* Check the Adapter and throw an exception if it already exists.
*/
private void checkAdapterExist(String message) {
if (mAdapterWrapper != null)
throw new IllegalStateException(message);
}
/**
* Set item click listener.
*/
public void setSwipeItemClickListener(SwipeItemClickListener itemClickListener) {
if (itemClickListener == null) return;
checkAdapterExist("Cannot set item click listener, setAdapter has already been called.");
this.mSwipeItemClickListener = new ItemClick(this, itemClickListener);
}
private static class ItemClick implements SwipeItemClickListener {
private SwipeMenuRecyclerView mRecyclerView;
private SwipeItemClickListener mCallback;
public ItemClick(SwipeMenuRecyclerView recyclerView, SwipeItemClickListener callback) {
this.mRecyclerView = recyclerView;
this.mCallback = callback;
}
@Override
public void onItemClick(View itemView, int position) {
position = position - mRecyclerView.getHeaderItemCount();
if (position >= 0)
mCallback.onItemClick(itemView, position);
}
}
/**
* Set item click listener.
*/
public void setSwipeItemLongClickListener(SwipeItemLongClickListener itemLongClickListener) {
if (itemLongClickListener == null) return;
checkAdapterExist("Cannot set item long click listener, setAdapter has already been called.");
this.mSwipeItemLongClickListener = new ItemLongClick(this, itemLongClickListener);
}
private static class ItemLongClick implements SwipeItemLongClickListener {
private SwipeMenuRecyclerView mRecyclerView;
private SwipeItemLongClickListener mCallback;
public ItemLongClick(SwipeMenuRecyclerView recyclerView, SwipeItemLongClickListener callback) {
this.mRecyclerView = recyclerView;
this.mCallback = callback;
}
@Override
public void onItemLongClick(View itemView, int position) {
position = position - mRecyclerView.getHeaderItemCount();
if (position >= 0)
mCallback.onItemLongClick(itemView, position);
}
}
/**
* Set to create menu listener.
*/
public void setSwipeMenuCreator(SwipeMenuCreator menuCreator) {
if (menuCreator == null) return;
checkAdapterExist("Cannot set menu creator, setAdapter has already been called.");
this.mSwipeMenuCreator = menuCreator;
}
/**
* Set to click menu listener.
*/
public void setSwipeMenuItemClickListener(SwipeMenuItemClickListener menuItemClickListener) {
if (menuItemClickListener == null) return;
checkAdapterExist("Cannot set menu item click listener, setAdapter has already been called.");
this.mSwipeMenuItemClickListener = new MenuItemClick(this, menuItemClickListener);
}
private static class MenuItemClick implements SwipeMenuItemClickListener {
private SwipeMenuRecyclerView mRecyclerView;
private SwipeMenuItemClickListener mCallback;
public MenuItemClick(SwipeMenuRecyclerView recyclerView, SwipeMenuItemClickListener callback) {
this.mRecyclerView = recyclerView;
this.mCallback = callback;
}
@Override
public void onItemClick(SwipeMenuBridge menuBridge) {
int position = menuBridge.getAdapterPosition();
position = position - mRecyclerView.getHeaderItemCount();
if (position >= 0) {
menuBridge.mAdapterPosition = position;
mCallback.onItemClick(menuBridge);
}
}
}
@Override
public void setLayoutManager(LayoutManager layoutManager) {
if (layoutManager instanceof GridLayoutManager) {
final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
final GridLayoutManager.SpanSizeLookup spanSizeLookupHolder = gridLayoutManager.getSpanSizeLookup();
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (mAdapterWrapper.isHeaderView(position) || mAdapterWrapper.isFooterView(position)) {
return gridLayoutManager.getSpanCount();
}
if (spanSizeLookupHolder != null)
return spanSizeLookupHolder.getSpanSize(position - getHeaderItemCount());
return 1;
}
});
}
super.setLayoutManager(layoutManager);
}
/**
* Get the original adapter.
*/
public Adapter getOriginAdapter() {
if (mAdapterWrapper == null) return null;
return mAdapterWrapper.getOriginAdapter();
}
@Override
public void setAdapter(Adapter adapter) {
if (mAdapterWrapper != null) {
mAdapterWrapper.getOriginAdapter().unregisterAdapterDataObserver(mAdapterDataObserver);
}
if (adapter == null) {
mAdapterWrapper = null;
} else {
adapter.registerAdapterDataObserver(mAdapterDataObserver);
mAdapterWrapper = new SwipeAdapterWrapper(getContext(), adapter);
mAdapterWrapper.setSwipeItemClickListener(mSwipeItemClickListener);
mAdapterWrapper.setSwipeItemLongClickListener(mSwipeItemLongClickListener);
mAdapterWrapper.setSwipeMenuCreator(mSwipeMenuCreator);
mAdapterWrapper.setSwipeMenuItemClickListener(mSwipeMenuItemClickListener);
if (mHeaderViewList.size() > 0) {
for (View view : mHeaderViewList) {
mAdapterWrapper.addHeaderView(view);
}
}
if (mFooterViewList.size() > 0) {
for (View view : mFooterViewList) {
mAdapterWrapper.addFooterView(view);
}
}
}
super.setAdapter(mAdapterWrapper);
}
private AdapterDataObserver mAdapterDataObserver = new AdapterDataObserver() {
@Override
public void onChanged() {
mAdapterWrapper.notifyDataSetChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
positionStart += getHeaderItemCount();
mAdapterWrapper.notifyItemRangeChanged(positionStart, itemCount);
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
positionStart += getHeaderItemCount();
mAdapterWrapper.notifyItemRangeChanged(positionStart, itemCount, payload);
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
positionStart += getHeaderItemCount();
mAdapterWrapper.notifyItemRangeInserted(positionStart, itemCount);
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
positionStart += getHeaderItemCount();
mAdapterWrapper.notifyItemRangeRemoved(positionStart, itemCount);
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
fromPosition += getHeaderItemCount();
toPosition += getHeaderItemCount();
mAdapterWrapper.notifyItemMoved(fromPosition, toPosition);
}
};
private List<View> mHeaderViewList = new ArrayList<>();
private List<View> mFooterViewList = new ArrayList<>();
/**
* Add view at the headers.
*/
public void addHeaderView(View view) {
mHeaderViewList.add(view);
if (mAdapterWrapper != null) {
mAdapterWrapper.addHeaderViewAndNotify(view);
}
}
/**
* Remove view from header.
*/
public void removeHeaderView(View view) {
mHeaderViewList.remove(view);
if (mAdapterWrapper != null) {
mAdapterWrapper.removeHeaderViewAndNotify(view);
}
}
/**
* Add view at the footer.
*/
public void addFooterView(View view) {
mFooterViewList.add(view);
if (mAdapterWrapper != null) {
mAdapterWrapper.addFooterViewAndNotify(view);
}
}
public void removeFooterView(View view) {
mFooterViewList.remove(view);
if (mAdapterWrapper != null) {
mAdapterWrapper.removeFooterViewAndNotify(view);
}
}
/**
* Get size of headers.
*/
public int getHeaderItemCount() {
if (mAdapterWrapper == null) return 0;
return mAdapterWrapper.getHeaderItemCount();
}
/**
* Get size of footer.
*/
public int getFooterItemCount() {
if (mAdapterWrapper == null) return 0;
return mAdapterWrapper.getFooterItemCount();
}
/**
* Get ViewType of item.
*/
public int getItemViewType(int position) {
if (mAdapterWrapper == null) return 0;
return mAdapterWrapper.getItemViewType(position);
}
/**
* open menu on left.
*
* @param position position.
*/
public void smoothOpenLeftMenu(int position) {
smoothOpenMenu(position, LEFT_DIRECTION, SwipeMenuLayout.DEFAULT_SCROLLER_DURATION);
}
/**
* open menu on left.
*
* @param position position.
* @param duration time millis.
*/
public void smoothOpenLeftMenu(int position, int duration) {
smoothOpenMenu(position, LEFT_DIRECTION, duration);
}
/**
* open menu on right.
*
* @param position position.
*/
public void smoothOpenRightMenu(int position) {
smoothOpenMenu(position, RIGHT_DIRECTION, SwipeMenuLayout.DEFAULT_SCROLLER_DURATION);
}
/**
* open menu on right.
*
* @param position position.
* @param duration time millis.
*/
public void smoothOpenRightMenu(int position, int duration) {
smoothOpenMenu(position, RIGHT_DIRECTION, duration);
}
/**
* open menu.
*
* @param position position.
* @param direction use {@link #LEFT_DIRECTION}, {@link #RIGHT_DIRECTION}.
* @param duration time millis.
*/
public void smoothOpenMenu(int position, @DirectionMode int direction, int duration) {
if (mOldSwipedLayout != null) {
if (mOldSwipedLayout.isMenuOpen()) {
mOldSwipedLayout.smoothCloseMenu();
}
}
position += getHeaderItemCount();
ViewHolder vh = findViewHolderForAdapterPosition(position);
if (vh != null) {
View itemView = getSwipeMenuView(vh.itemView);
if (itemView instanceof SwipeMenuLayout) {
mOldSwipedLayout = (SwipeMenuLayout) itemView;
if (direction == RIGHT_DIRECTION) {
mOldTouchedPosition = position;
mOldSwipedLayout.smoothOpenRightMenu(duration);
} else if (direction == LEFT_DIRECTION) {
mOldTouchedPosition = position;
mOldSwipedLayout.smoothOpenLeftMenu(duration);
}
}
}
}
/**
* Close menu.
*/
public void smoothCloseMenu() {
if (mOldSwipedLayout != null && mOldSwipedLayout.isMenuOpen()) {
mOldSwipedLayout.smoothCloseMenu();
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
boolean isIntercepted = super.onInterceptTouchEvent(e);
if (allowSwipeDelete) // swipe and menu conflict.
return isIntercepted;
else {
if (e.getPointerCount() > 1) return true;
int action = e.getAction();
int x = (int) e.getX();
int y = (int) e.getY();
switch (action) {
case MotionEvent.ACTION_DOWN: {
mDownX = x;
mDownY = y;
isIntercepted = false;
int touchingPosition = getChildAdapterPosition(findChildViewUnder(x, y));
if (touchingPosition != mOldTouchedPosition && mOldSwipedLayout != null && mOldSwipedLayout.isMenuOpen()) {
mOldSwipedLayout.smoothCloseMenu();
isIntercepted = true;
}
if (isIntercepted) {
mOldSwipedLayout = null;
mOldTouchedPosition = INVALID_POSITION;
} else {
ViewHolder vh = findViewHolderForAdapterPosition(touchingPosition);
if (vh != null) {
View itemView = getSwipeMenuView(vh.itemView);
if (itemView instanceof SwipeMenuLayout) {
mOldSwipedLayout = (SwipeMenuLayout) itemView;
mOldTouchedPosition = touchingPosition;
}
}
}
break;
}
// They are sensitive to retain sliding and inertia.
case MotionEvent.ACTION_MOVE: {
isIntercepted = handleUnDown(x, y, isIntercepted);
if (mOldSwipedLayout == null) break;
ViewParent viewParent = getParent();
if (viewParent == null) break;
int disX = mDownX - x;
// 向左滑,显示右侧菜单,或者关闭左侧菜单。
boolean showRightCloseLeft = disX > 0 && (mOldSwipedLayout.hasRightMenu() || mOldSwipedLayout.isLeftCompleteOpen());
// 向右滑,显示左侧菜单,或者关闭右侧菜单。
boolean showLeftCloseRight = disX < 0 && (mOldSwipedLayout.hasLeftMenu() || mOldSwipedLayout.isRightCompleteOpen());
viewParent.requestDisallowInterceptTouchEvent(showRightCloseLeft || showLeftCloseRight);
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
isIntercepted = handleUnDown(x, y, isIntercepted);
break;
}
}
}
return isIntercepted;
}
private boolean handleUnDown(int x, int y, boolean defaultValue) {
int disX = mDownX - x;
int disY = mDownY - y;
// swipe
if (Math.abs(disX) > mScaleTouchSlop && Math.abs(disX) > Math.abs(disY))
return false;
// click
if (Math.abs(disY) < mScaleTouchSlop && Math.abs(disX) < mScaleTouchSlop)
return false;
return defaultValue;
}
@Override
public boolean onTouchEvent(MotionEvent e) {
int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if (mOldSwipedLayout != null && mOldSwipedLayout.isMenuOpen()) {
mOldSwipedLayout.smoothCloseMenu();
}
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return super.onTouchEvent(e);
}
private View getSwipeMenuView(View itemView) {
if (itemView instanceof SwipeMenuLayout) return itemView;
List<View> unvisited = new ArrayList<>();
unvisited.add(itemView);
while (!unvisited.isEmpty()) {
View child = unvisited.remove(0);
if (!(child instanceof ViewGroup)) { // view
continue;
}
if (child instanceof SwipeMenuLayout) return child;
ViewGroup group = (ViewGroup) child;
final int childCount = group.getChildCount();
for (int i = 0; i < childCount; i++) unvisited.add(group.getChildAt(i));
}
return itemView;
}
private int mScrollState = -1;
private boolean isLoadMore = false;
private boolean isAutoLoadMore = true;
private boolean isLoadError = false;
private boolean mDataEmpty = true;
private boolean mHasMore = false;
private LoadMoreView mLoadMoreView;
private LoadMoreListener mLoadMoreListener;
@Override
public void onScrollStateChanged(int state) {
this.mScrollState = state;
}
@Override
public void onScrolled(int dx, int dy) {
LayoutManager layoutManager = getLayoutManager();
if (layoutManager != null && layoutManager instanceof LinearLayoutManager) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
int itemCount = layoutManager.getItemCount();
if (itemCount <= 0) return;
int lastVisiblePosition = linearLayoutManager.findLastVisibleItemPosition();
if (itemCount == lastVisiblePosition + 1 &&
(mScrollState == SCROLL_STATE_DRAGGING || mScrollState == SCROLL_STATE_SETTLING)) {
dispatchLoadMore();
}
} else if (layoutManager != null && layoutManager instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
int itemCount = layoutManager.getItemCount();
if (itemCount <= 0) return;
int[] lastVisiblePositionArray = staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(null);
int lastVisiblePosition = lastVisiblePositionArray[lastVisiblePositionArray.length - 1];
if (itemCount == lastVisiblePosition + 1 &&
(mScrollState == SCROLL_STATE_DRAGGING || mScrollState == SCROLL_STATE_SETTLING)) {
dispatchLoadMore();
}
}
}
private void dispatchLoadMore() {
if (isLoadError) return;
if (!isAutoLoadMore) {
if (mLoadMoreView != null)
mLoadMoreView.onWaitToLoadMore(mLoadMoreListener);
} else {
if (isLoadMore || mDataEmpty || !mHasMore) return;
isLoadMore = true;
if (mLoadMoreView != null)
mLoadMoreView.onLoading();
if (mLoadMoreListener != null)
mLoadMoreListener.onLoadMore();
}
}
/**
* Use the default to load more View.
*/
public void useDefaultLoadMore() {
DefaultLoadMoreView defaultLoadMoreView = new DefaultLoadMoreView(getContext());
addFooterView(defaultLoadMoreView);
setLoadMoreView(defaultLoadMoreView);
}
/**
* Load more view.
*/
public void setLoadMoreView(LoadMoreView loadMoreView) {
mLoadMoreView = loadMoreView;
}
/**
* Load more listener.
*/
public void setLoadMoreListener(LoadMoreListener loadMoreListener) {
mLoadMoreListener = loadMoreListener;
}
/**
* Automatically load more automatically.
* <p>
* Non-auto-loading mode, you can to click on the item to load.
* </p>
*
* @param autoLoadMore you can use false.
* @see LoadMoreView#onWaitToLoadMore(LoadMoreListener)
*/
public void setAutoLoadMore(boolean autoLoadMore) {
isAutoLoadMore = autoLoadMore;
}
/**
* Load more done.
*
* @param dataEmpty data is empty ?
* @param hasMore has more data ?
*/
public final void loadMoreFinish(boolean dataEmpty, boolean hasMore) {
isLoadMore = false;
isLoadError = false;
mDataEmpty = dataEmpty;
mHasMore = hasMore;
if (mLoadMoreView != null) {
mLoadMoreView.onLoadFinish(dataEmpty, hasMore);
}
}
/**
* Called when data is loaded incorrectly.
*
* @param errorCode Error code, will be passed to the LoadView, you can according to it to customize the prompt information.
* @param errorMessage Error message.
*/
public void loadMoreError(int errorCode, String errorMessage) {
isLoadMore = false;
isLoadError = true;
if (mLoadMoreView != null) {
mLoadMoreView.onLoadError(errorCode, errorMessage);
}
}
public interface LoadMoreView {
/**
* Show progress.
*/
void onLoading();
/**
* Load finish, handle result.
*/
void onLoadFinish(boolean dataEmpty, boolean hasMore);
/**
* Non-auto-loading mode, you can to click on the item to load.
*/
void onWaitToLoadMore(LoadMoreListener loadMoreListener);
/**
* Load error.
*/
void onLoadError(int errorCode, String errorMessage);
}
public interface LoadMoreListener {
/**
* More data should be requested.
*/
void onLoadMore();
}
}
我们在上述自定义RecyclerView的源码里搜索onLoadMore,发现是在dispatchLoadMore函数里调用了,好的,然后我们再搜索dispatchLoadMore是在哪里被调用了,发现是在RecyclerView的这个系统函数里调用的:
@Override
public void onScrolled(int dx, int dy) {
LayoutManager layoutManager = getLayoutManager();
if (layoutManager != null && layoutManager instanceof LinearLayoutManager) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
int itemCount = layoutManager.getItemCount();
if (itemCount <= 0) return;
int lastVisiblePosition = linearLayoutManager.findLastVisibleItemPosition();
if (itemCount == lastVisiblePosition + 1 &&
(mScrollState == SCROLL_STATE_DRAGGING || mScrollState == SCROLL_STATE_SETTLING)) {
dispatchLoadMore();
}
} ......
onScrolled这个函数是在RecyclerView控件滚动回调的。它有两个参数dx:水平滚动的距离,dy垂直滚动的距离,当然这里判断是否滚动到列表页底部不是用这两个参数判断的,在这里我们只是顺便提一下。主要是用下面这个if判断来判断页面是否滚动到了底部:
int lastVisiblePosition = linearLayoutManager.findLastVisibleItemPosition();
if (itemCount == lastVisiblePosition + 1 &&
(mScrollState == SCROLL_STATE_DRAGGING || mScrollState == SCROLL_STATE_SETTLING)) {
dispatchLoadMore();
}
当可见列表项的位置为最后一个列表项,且列表滚动状态为 DRAGGING(手指拖曳滚动)时 或 SCROLL_STATE_SETTING(列表惯性滚动)时,调用dispatchLoadMore()函数,从而调用了loadMore上拉加载函数,由于loadMore是在DefaultActivity(显示RecyclerView控件的主页面)里实现的接口函数,因此请求网络数据的代码在以下这个函数里处理的:
/**
* 加载更多。
*/
private SwipeMenuRecyclerView.LoadMoreListener mLoadMoreListener = new SwipeMenuRecyclerView.LoadMoreListener() {
@Override
public void onLoadMore() {
mRecyclerView.postDelayed(new Runnable() {
@Override
public void run() {
List<String> strings = createDataList(mAdapter.getItemCount());
mDataList.addAll(strings);
// notifyItemRangeInserted()或者notifyDataSetChanged().
mAdapter.notifyItemRangeInserted(mDataList.size() - strings.size(), strings.size());
// 数据完更多数据,一定要掉用这个方法。
// 第一个参数:表示此次数据是否为空。
// 第二个参数:表示是否还有更多数据。
mRecyclerView.loadMoreFinish(false, true);
// 如果加载失败调用下面的方法,传入errorCode和errorMessage。
// errorCode随便传,你自定义LoadMoreView时可以根据errorCode判断错误类型。
// errorMessage是会显示到loadMoreView上的,用户可以看到。
// mRecyclerView.loadMoreError(0, "请求网络失败");
}
}, 1000);
}
};
这个函数里我们模拟了列表数据的增加。
OK,至此下拉加载更多的原理和过程也就分析完了。我们来总结以下:
(1)上拉刷新,使用系统SwipeRefreshLayout控件就可以实现,无需自定义代码。
(2)下拉加载更多,需要扩展RecyclerView,并重写onScrolled函数,在滑动页面底部的时候调用dispatchLoadMore()---onLoadMore();
最后点击 源码
文章来源: blog.csdn.net,作者:冉航--小虾米,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/gaoxiaoweiandy/article/details/80963228
【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)