Android高级UI开发 RecyclerView高级进阶(八)下拉刷新,上拉加载

举报
yd_57386892 发表于 2020/12/29 00:13:35 2020/12/29
4.7k+ 0 0
【摘要】 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

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。