Android高级UI开发 RecyclerView高级进阶(八)下拉刷新,上拉加载
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
- 点赞
- 收藏
- 关注作者
评论(0)