Android

RecyclerView使用(三)——下拉刷新,上拉加载

2019-07-23  本文已影响0人  Sraindy

一、准备工作

1、item布局
2、footerView底部布局
3、通过Adapter
4、在acivity中使用
5、显示如下:


ezgif.com-resize.gif

二、代码实现——通用部分

1、item布局 item_recycler.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:gravity="center"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tvName"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center_vertical"
            android:textSize="16sp" />

        <TextView
            android:id="@+id/tvValue"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="10dp"
            android:gravity="center_vertical"
            android:textSize="16sp" />
    </LinearLayout>
</LinearLayout>


2、footerview布局 layout_recycler_footer.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="70dp"
    android:orientation="vertical"
    android:padding="10dp">

    <!-- 加载时显示的布局 -->
    <LinearLayout
        android:id="@+id/ll_footer_loading"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="horizontal"
        android:visibility="gone">

        <ProgressBar
            android:id="@+id/pb_loading"
            android:layout_width="34dp"
            android:layout_height="34dp"
            android:indeterminateDrawable="@drawable/progressbar_recycler_footer" />

        <TextView
            android:id="@+id/tv_loading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="正在加载..."
            android:textColor="@color/color_recycler_footer_txt"
            android:textSize="16sp" />
    </LinearLayout>

    <!--错误时显示的布局-->
    <LinearLayout
        android:id="@+id/ll_footer_error"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clickable="true"
        android:gravity="center"
        android:visibility="gone"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="网络错误,点击重新加载"
            android:textColor="@color/color_recycler_footer_txt"
            android:textSize="16sp" />
    </LinearLayout>

    <!--全部加载完毕显示的布局-->
    <LinearLayout
        android:id="@+id/ll_footer_all_loaded"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:gravity="center"
        android:orientation="horizontal"
        android:visibility="gone">

        <View
            android:layout_width="0dp"
            android:layout_height="1dp"
            android:layout_weight="1"
            android:alpha="0.5"
            android:background="@color/color_recycler_footer_txt"
            android:gravity="start" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="15dp"
            android:text="全部加载完毕"
            android:textColor="@color/color_recycler_footer_txt"
            android:textSize="16sp" />

        <View
            android:layout_width="0dp"
            android:layout_height="1dp"
            android:layout_weight="1"
            android:alpha="0.5"
            android:background="@color/color_recycler_footer_txt"
            android:gravity="end" />

    </LinearLayout>
</LinearLayout>

3、progressbar的外观 drawable/progressbar_recycler_footer.xml

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="720">
    <shape
        android:innerRadius="10dp"
        android:shape="ring"
        android:thickness="3dp"
        android:useLevel="false">
        <gradient
            android:centerColor="@color/color_lib_main_center"
            android:endColor="@color/color_lib_main_end"
            android:startColor="@color/color_lib_main_start"
            android:type="sweep" />
    </shape>
</rotate>

4、values/colors.xml

 <!-- jar库中主题颜色 中的中间色 用于渐变 -->
    <color name="color_lib_main_start">#fff</color>
    <color name="color_lib_main_center">#5d8afc</color>
    <color name="color_lib_main_end">#5d8afc</color>

    <!-- recycler footer 字体颜色 -->
    <color name="color_recycler_footer_txt">#5d8afc</color>

5、BaseViewHolder

/**
 * 基础ViewHolder
 */
public abstract class BaseViewHolder extends RecyclerView.ViewHolder {

    public BaseViewHolder(View itemView) {
        super(itemView);
    }
}

6、FooterViewHolder


/**
 * 底部ViewHolder
 */
public class FooterViewHolder extends RecyclerView.ViewHolder {
    public LinearLayout mFooterLoading = null;
    public LinearLayout mFooterError = null;
    public LinearLayout mAllLoaded = null;

    public FooterViewHolder(View itemView) {
        super(itemView);
        mFooterLoading = itemView.findViewById(R.id.ll_footer_loading);
        mFooterError = itemView.findViewById(R.id.ll_footer_error);
        mAllLoaded = itemView.findViewById(R.id.ll_footer_all_loaded);
    }
    
}


7、 ABaseRecyclerAdapter

/**
 * 带有底部的 基础RecyclerAdapter
 */
public abstract class ABaseRecyclerAdapter<T> extends RecyclerView.Adapter {
    private Context mContext = null;
    private List<T> mDataList = null; //数据源

    /**
     * item的布局
     */
    private int itemLayoutId = 0;

    /*** 普通的item */
    private final int TYPE_ITEM = 0;
    /*** 底部 */
    private final int TYPE_FOOTER = 1;


    private FooterViewHolder mFooterViewHolder = null;
    private View mFooterView = null;


    /**
     * 加载状态
     */
    private int loadState = 1;


    private OnReloadClickListener mReloadClick = null;

    /**
     * 重新加载
     *
     * @param mReloadClick
     */
    public void setmReloadClick(OnReloadClickListener mReloadClick) {
        this.mReloadClick = mReloadClick;
    }

    /**
     * 底部上拉加载的view有四种状态:
     * <p>
     * 初始时:隐藏(避免recyclerView的item不满一页时,上拉加载的view一直显示)
     * <p>
     * 第一次滑动后:显示(显示信息为正在加载)
     * <p>
     * 网络错误时:显示(显示信息为网络错误,点击重新加载)
     * <p>
     * 全部都已加载完时:显示(显示信息为已全部加载完毕)
     */
    public static final int STATE_FIRST = 1; //初始状态
    public static final int STATE_SHOW = 2;  //展示正在加载
    public static final int STATE_ERROR_NET = 3; //网络错误
    public static final int STATE_ALL_LOADED = 4; //已经全部加载完毕

    public ABaseRecyclerAdapter(Context mContext, List<T> mDataList, int itemLayoutId) {
        this.mContext = mContext;
        this.mDataList = mDataList;
        this.itemLayoutId = itemLayoutId;
    }

    /**
     * 获取总数
     * 因为底部需要提示内容,所以在所有对象数量上再加1
     *
     * @return
     */
    @Override
    public int getItemCount() {
        if (null == mDataList || mDataList.size() == 0) {
            return 0;
        } else {
            return mDataList.size() + 1;
        }
    }

    /**
     * 获取每个Item中的viewType
     * recycleView调用流程:getItemViewType先于onCreateViewHolder。
     *
     * @param position
     * @return
     */
    @Override
    public int getItemViewType(int position) {
        if (position + 1 == getItemCount()) {
            return TYPE_FOOTER;
        } else {
            return TYPE_ITEM;
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        RecyclerView.ViewHolder viewHolderRes = null;

        if (viewType == TYPE_FOOTER) { //底部footerView
            if (null == mFooterViewHolder) {
                mFooterView = getItemViewByLayoutId(parent, R.layout.layout_recycler_footer);
                mFooterViewHolder = new FooterViewHolder(mFooterView);
                viewHolderRes = mFooterViewHolder;
            } else {
                viewHolderRes = mFooterViewHolder;
            }
        } else if (viewType == TYPE_ITEM) { //普通Item
            View view = getItemViewByLayoutId(parent, itemLayoutId);
            viewHolderRes = getBaseViewHolder(view);
        }
        return viewHolderRes;
    }

    /**
     * 根据layoutId获取view
     *
     * @param parent
     * @param layoutId
     * @return
     */
    public View getItemViewByLayoutId(ViewGroup parent, int layoutId) {
        return LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
    }


    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof BaseViewHolder) {
            convertViewHolder((BaseViewHolder) holder, mDataList.get(position), position);
        } else if (holder instanceof FooterViewHolder) {
            FooterViewHolder tHolder = (FooterViewHolder) holder;
            switch (loadState) {
                case STATE_FIRST: { //初始状态
                    mFooterView.setVisibility(View.GONE);
                    tHolder.mFooterLoading.setVisibility(View.GONE);
                    tHolder.mFooterError.setVisibility(View.GONE);
                    tHolder.mAllLoaded.setVisibility(View.GONE);
                    break;
                }
                case STATE_SHOW: { //展示正在加载
                    mFooterView.setVisibility(View.VISIBLE);
                    tHolder.mFooterLoading.setVisibility(View.VISIBLE);
                    tHolder.mFooterError.setVisibility(View.GONE);
                    tHolder.mAllLoaded.setVisibility(View.GONE);
                    break;
                }
                case STATE_ERROR_NET: { //网络出错
                    mFooterView.setVisibility(View.VISIBLE);
                    tHolder.mFooterLoading.setVisibility(View.GONE);
                    tHolder.mFooterError.setVisibility(View.VISIBLE);
                    tHolder.mAllLoaded.setVisibility(View.GONE);
                    tHolder.mFooterError.setOnClickListener(v -> {
                        if (null != mReloadClick) {
                            mReloadClick.onClick();
                        }
                    });
                    break;
                }
                case STATE_ALL_LOADED: { //已经全部加载
                    mFooterView.setVisibility(View.VISIBLE);
                    tHolder.mFooterLoading.setVisibility(View.GONE);
                    tHolder.mFooterError.setVisibility(View.GONE);
                    tHolder.mAllLoaded.setVisibility(View.VISIBLE);
                    break;
                }
                default:
                    break;
            }
        }
    }

    protected abstract void convertViewHolder(BaseViewHolder holder, T data, int position);

    public abstract BaseViewHolder getBaseViewHolder(View itemView);

    /**
     * 更新加载状态
     * STATE_FIRST = 1; //初始状态
     * STATE_SHOW = 2;  //展示正在加载
     * STATE_ERROR_NET = 3; //网络错误
     * STATE_ALL_LOADED = 4; //已经全部加载完毕
     *
     * @param loadState
     */
    public void setLoadState(int loadState) {
        this.loadState = loadState;
        notifyDataSetChanged();
    }

    /**
     * 加载失败,重新加载
     */
    interface OnReloadClickListener {
        void onClick();
    }
}

代码实现——具体使用

1、ItemRecyclerModel

public class ItemRecyclerModel {

    private String mName = "";
    private String mValue = "";

    public String getmName() {
        return mName;
    }

    public void setmName(String mName) {
        this.mName = mName;
    }

    public String getmValue() {
        return mValue;
    }

    public void setmValue(String mValue) {
        this.mValue = mValue;
    }

    @Override
    public String toString() {
        return "ItemRecyclerModel{" +
                "mName='" + mName + '\'' +
                ", mValue='" + mValue + '\'' +
                '}';
    }
}


2、ItemRecyclerViewAdapter

public class ItemRecyclerViewAdapter extends ABaseRecyclerAdapter<ItemRecyclerModel> {

    public ItemRecyclerViewAdapter(Context mContext, List<ItemRecyclerModel> mDataList, int itemLayoutId) {
        super(mContext, mDataList, itemLayoutId);
    }

    @Override
    protected void convertViewHolder(BaseViewHolder holder, ItemRecyclerModel data, int position) {
        ItemRecyclerViewHolder tHolder = (ItemRecyclerViewHolder) holder;
        if (null != tHolder) {
            tHolder.mTvName.setText(data.getmName());
            tHolder.mTvValue.setText(data.getmValue());
        }
    }

    @Override
    public BaseViewHolder getBaseViewHolder(View itemView) {
        return new ItemRecyclerViewHolder(itemView);
    }
}


3、主布局 activity_recycler_test.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_add_one"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="加一" />

        <Button
            android:id="@+id/btn_subtract_one"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:text="减一" />
    </LinearLayout>

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/srl"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        </android.support.v7.widget.RecyclerView>
    </android.support.v4.widget.SwipeRefreshLayout>

</LinearLayout>

4、RecyclerTestActivity

public class RecyclerTestActivity extends AppCompatActivity {

    private Button btnAdd;
    private Button btnSubtract;

    private SwipeRefreshLayout mSwipeRefreshLayout;
    private RecyclerView mRecyclerView;

    private List<ItemRecyclerModel> mDataList = new ArrayList<>();
    private ItemRecyclerViewAdapter mItemAdapter;

    //避免item不满一页时,下拉刷新触发上拉加载事件
    private boolean isUp = false;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler_test);
        initData();
        initWidget();
    }

    private void initData() {
        ItemRecyclerModel model = new ItemRecyclerModel();
        model.setmName("第一个数");
        model.setmValue("1");
        mDataList.add(model);

    }

    private void initWidget() {
        btnAdd = findViewById(R.id.btn_add_one);
        btnSubtract = findViewById(R.id.btn_subtract_one);
        btnAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addItem();
                mItemAdapter.setLoadState(STATE_FIRST);
            }
        });
        btnSubtract.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mItemAdapter.setLoadState(STATE_ALL_LOADED);
            }
        });

        mSwipeRefreshLayout = findViewById(R.id.srl);
        mRecyclerView = findViewById(R.id.rv);

        mItemAdapter = new ItemRecyclerViewAdapter(this, mDataList, R.layout.item_recycler);
        LinearLayoutManager llManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(llManager);

        mRecyclerView.setAdapter(mItemAdapter);
        mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            private int lastVisibleItemPosition = 0;

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);

                if ((newState == SCROLL_STATE_IDLE) && (lastVisibleItemPosition + 1 == mItemAdapter.getItemCount()) && isUp) {
                    Log.i("zss", "到达底部,可再次加载更多数据");
                    mItemAdapter.setLoadState(STATE_SHOW);
                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                lastVisibleItemPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition();

            }
        });

        // 监听RecyclerView的滑动事件,判断是否是手指向上滑动,避免item不满一页时,下拉刷新触发上拉加载事件。
        mRecyclerView.setOnTouchListener(new View.OnTouchListener() {
            float oldy;

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        oldy = event.getY();
                        break;
                    case MotionEvent.ACTION_UP:
                        if (oldy - event.getY() > ViewConfiguration.get(RecyclerTestActivity.this).getScaledTouchSlop()) {
                            isUp = true;
                        } else {
                            isUp = false;
                        }
                }

                return false;
            }
        });
    }

    private void addItem() {
        ItemRecyclerModel model = new ItemRecyclerModel();
        model.setmName("第 " + (mDataList.size() + 1) + " 个数");
        model.setmValue((mDataList.size() + 1) + "");
        mDataList.add(model);
    }

}

上一篇下一篇

猜你喜欢

热点阅读