RecyclerView使用攻略(助力篇)

小序

实际上RecyclerView已不是什么新颖的话题了,至少对于用过的前辈们而言是这样的,并且大部分人都会觉得这斯很强大,必须上。而对于刚接触的小伙伴们,难免会遇到各种问题,或是因为陌生,又或是因为项目需求(譬如:侧滑出现删除按钮,拖动与上下拉刷新等等)。虽有云“前人种树,后人乘凉”一说,但依然是需要后人来灌溉的,至少这么说是为了铺垫。

显示效果

RecyclerView基本使用

上面图示仅是RecyclerView数据显示的基本使用,包括Adapter、Divider,相信绝大部分人都是在Hongyang前辈的树下乘过凉,由于其还没有考虑到关于RecyclerView的扩展问题,所以当我们在使用的时候难免会遇到以下两个存在的主流问题:

  1. 列表添加头部(底部)视图,也保留相应的分割线;
  2. 瀑布流列表Item的高度差导致的位置混乱,以及添加分割线出错;

解决问题与效果实现

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.LayoutManager;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View;
import android.widget.LinearLayout;

/**
 * RecyclerView 分割符
 */
public class RecyclerVDivider extends RecyclerView.ItemDecoration {

    private Drawable mDividerDraw;
    private int mLeftSpace, mRightSpace, mTopSpace, mBottomSpace;
    private int mSpaceColor;

    /**
     * @param context src/main/res/values/styles <item name="android:listDivider"/>
     */
    public RecyclerVDivider(Context context) {
        TypedArray a = context.obtainStyledAttributes(new int[]{android.R.attr.listDivider});
        mDividerDraw = a.getDrawable(0);
        a.recycle();
    }

    /**
     * @param drawable src/main/res/drawable
     */
    public RecyclerVDivider(Drawable drawable) {
        mDividerDraw = drawable;
    }

    /**
     * @param leftSpace 左侧间距
     */
    public void setLeftSpace(int leftSpace) {
        mLeftSpace = leftSpace;
    }

    /**
     * @param rightSpace 右侧间距
     */
    public void setRightSpace(int rightSpace) {
        mRightSpace = rightSpace;
    }

    /**
     * @param topSpace 顶部间距
     */
    public void setTopSpace(int topSpace) {
        mTopSpace = topSpace;
    }

    /**
     * @param bottomSpace 底部间距
     */
    public void setBottomSpace(int bottomSpace) {
        mBottomSpace = bottomSpace;
    }

    /**
     * @param spaceColor 分割线间距颜色,避免浮现父容器颜色
     */
    public void setSpaceColor(int spaceColor) {
        mSpaceColor = spaceColor;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (0 != mSpaceColor) c.drawColor(mSpaceColor);
        drawHorizontal(c, parent);
        drawVertical(c, parent);
    }

    private int getSpanCount(RecyclerView parent) {
        int spanCount = -1;
        LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
        } else if (layoutManager instanceof StaggeredGridLayoutManager) {
            spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
        }
        return spanCount;
    }

    public void drawHorizontal(Canvas c, RecyclerView parent) {
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int left = child.getLeft() - params.leftMargin + mLeftSpace;
            final int right = child.getRight() + params.rightMargin
                    + mDividerDraw.getIntrinsicWidth() - mRightSpace;
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDividerDraw.getIntrinsicHeight();
            mDividerDraw.setBounds(left, top, right, bottom);
            mDividerDraw.draw(c);
        }
    }

    public void drawVertical(Canvas c, RecyclerView parent) {
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getTop() - params.topMargin + mTopSpace;
            final int bottom = child.getBottom() + params.bottomMargin - mBottomSpace;
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDividerDraw.getIntrinsicWidth();
            mDividerDraw.setBounds(left, top, right, bottom);
            mDividerDraw.draw(c);
        }
    }

    private boolean isVertical(LayoutManager layoutManager) {
        if (layoutManager instanceof GridLayoutManager) {
            return ((GridLayoutManager) layoutManager).getOrientation() == LinearLayout.VERTICAL;
        } else if (layoutManager instanceof StaggeredGridLayoutManager) {
            return ((StaggeredGridLayoutManager) layoutManager).getOrientation() == LinearLayout.VERTICAL;
        } else if (layoutManager instanceof LinearLayoutManager) {
            return ((LinearLayoutManager) layoutManager).getOrientation() == LinearLayout.VERTICAL;
        }
        return true;
    }

    /**
     * @param adapter RecyclerView.Adapter
     * @return 当前遍历Item所处列数
     */
    private int getSpanIndex(RecyclerView.Adapter adapter) {
        if (adapter instanceof BaseRVAdapter) {
            return ((BaseRVAdapter) adapter).mSpanIndex + 1;
        }
        return 0;
    }

    /**
     * @param adapter   RecyclerView.Adapter
     * @param spanCount 最大可显示列数
     * @return 所需绘制底部分割线的最大行数(除去底部视图以及最后一排数据)
     */
    private int getMaxRaw(RecyclerView.Adapter adapter, int spanCount) {
        int childCount = adapter.getItemCount();
        int maxRawSize = childCount - childCount % spanCount;

        if (adapter instanceof BaseRVAdapter) {
            BaseRVAdapter baseRVAdapter = (BaseRVAdapter) adapter;

            if (null != baseRVAdapter.mFooterViews && null != baseRVAdapter.mHeaderViews) {
                childCount = baseRVAdapter.mList.size();
                maxRawSize = childCount - childCount % spanCount;
                maxRawSize += baseRVAdapter.mHeaderViews.size();
            }
        }
        return maxRawSize;
    }

    /**
     * @param adapter RecyclerView.Adapter
     * @return true_表示当前为数据Item,false_标识当前为头部/底部View
     */
    private boolean getItemState(RecyclerView.Adapter adapter) {
        if (adapter instanceof BaseRVAdapter) {
            return ((BaseRVAdapter) adapter).isMainItem;
        }
        return true;
    }

    @Override
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        LayoutManager layoutManager = parent.getLayoutManager();
        int spanCount = getSpanCount(parent);
        boolean isLastRaw = isVertical(layoutManager) ?
                itemPosition + 1 >= getMaxRaw(parent.getAdapter(), spanCount) :
                getSpanIndex(parent.getAdapter()) == spanCount;

        boolean isLastColum = isVertical(layoutManager) ?
                spanCount == getSpanIndex(parent.getAdapter()) :
                itemPosition + 1 >= getMaxRaw(parent.getAdapter(), spanCount);

        if (getItemState(parent.getAdapter())) {
            outRect.set(0, 0,
                    isLastColum ? 0 : mDividerDraw.getIntrinsicWidth(),// 如果是最后一列,则不需要绘制右边
                    isLastRaw ? 0 : mDividerDraw.getIntrinsicHeight());// 如果是最后一行,则不需要绘制底部
        }
    }
}

可见分割线的绘制判断改动比较大,且与自定义RecyclerView.Adapter的实现类BaseRVAdapter.class耦合在一起了。但为了解决以上存在的两个问题,这也是没有办法中的办法(表示蓝瘦到香菇)。以上简单的判断处理相信大家是能看懂,主要还是对这些判断的数据来源比较感冒些。

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

/**
 * RecyclerView 适配器
 */
public abstract class BaseRVAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> {

    public static final int TYPE_NORMAL = 0;
    public static final int TYPE_HEADER = 1;
    public static final int TYPE_FOOTER = -1;

    public Context mContext;
    public List<T> mList = new ArrayList<>();
    public List<View> mHeaderViews = new ArrayList<>();
    public List<View> mFooterViews = new ArrayList<>();
    public boolean isCloseItemAnim;

    public BaseRVAdapter(Context context, @NonNull List<T> list) {
        this.mList = list;
        this.mContext = context;
    }

    public T getData(int position) {
        return mList.get(position);
    }

    public void updateAdapter(@NonNull List<T> list) {
        mList.clear();
        mList.addAll(list);
        notifyDataSetChanged();
        // notifyItemRangeChanged(startIndex(0), mList.size());
    }

    public void updateData(int position, T object) {
        mList.set(position, object);

        if (isCloseItemAnim) {
            notifyDataSetChanged();
        } else {
            int updateIndex = startIndex(position);
            notifyItemChanged(updateIndex);
            notifyItemRangeChanged(updateIndex, mList.size());
        }
    }

    public void cleanData() {
        if (!isCloseItemAnim) {
            mList.clear();
            notifyDataSetChanged();
        } else {
            int cleanIndex = mHeaderViews.size();
            notifyItemRangeRemoved(cleanIndex, mList.size());
            mList.clear();
            notifyItemRangeChanged(cleanIndex, mList.size());
        }
    }

    public void removeData(int position) {
        if (mList.size() <= position) return;

        if (isCloseItemAnim) {
            mList.remove(position);
            notifyDataSetChanged();
        } else {
            notifyItemRemoved(startIndex(position));
            mList.remove(position);
            notifyItemRangeChanged(startIndex(position), mList.size());
        }
    }

    public void addDataLs(final int position, @NonNull List<T> list) {
        mList.addAll(position, list);

        if (isCloseItemAnim) {
            notifyDataSetChanged();
        } else {
            int addIndex = startIndex(position);
            notifyItemRangeInserted(addIndex, list.size());
            notifyItemRangeChanged(addIndex, mList.size());
        }
    }

    public void addDataLs(@NonNull List<T> list) {
        addDataLs(startIndex(mList.size()), list);
    }

    public void addData(int position, T object) {
        int startIndex = startIndex(position);
        mList.add(position, object);

        if (isCloseItemAnim) {
            notifyDataSetChanged();
        } else {
            notifyItemInserted(startIndex);
            notifyItemRangeChanged(startIndex, mList.size());
        }
    }

    public void addData(T object) {
        addData(startIndex(mList.size()), object);
    }

    public void addHeaderViews(@NonNull List<View> headerViews) {
        mHeaderViews.addAll(headerViews);
        notifyDataSetChanged();
    }

    public void addHeaderView(@NonNull View headerView) {
        mHeaderViews.add(headerView);
        notifyDataSetChanged();
    }

    public void addFooterViews(@NonNull List<View> footerViews) {
        mFooterViews.addAll(footerViews);
        notifyDataSetChanged();
    }

    public void addFooterView(@NonNull View footerView) {
        mFooterViews.add(footerView);
        notifyDataSetChanged();
    }

    private int startIndex(int doneIndex) {
        return doneIndex + mHeaderViews.size();
    }

    /**
     * {@link #TYPE_HEADER} 头部列表
     * {@link #TYPE_NORMAL} 正常列表
     * {@link #TYPE_FOOTER} 底部列表
     */
    @Override
    public int getItemViewType(int position) {
        if (!mHeaderViews.isEmpty() && position < mHeaderViews.size()) {
            return TYPE_HEADER + position;
        } else if (!mFooterViews.isEmpty() && position >= mHeaderViews.size() + mList.size()) {
            int beforeSize = mHeaderViews.size() + mList.size();
            return TYPE_FOOTER - (position - beforeSize);
        } else {
            return TYPE_NORMAL;
        }
    }

    @Override
    public int getItemCount() {
        if ((null == mList || mList.isEmpty())) {
            return null == mHeaderViews ? 0 : mHeaderViews.size()
                    + (null == mFooterViews ? 0 : mFooterViews.size());
        } else {
            return mHeaderViews.size() + mList.size() + mFooterViews.size();
        }
    }

    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (!mHeaderViews.isEmpty() && viewType > TYPE_NORMAL) {
            return new BaseViewHolder(mHeaderViews.get(viewType - 1));
        } else if (!mFooterViews.isEmpty() && viewType <= TYPE_FOOTER) {
            return new BaseViewHolder(mFooterViews.get(-viewType - 1));
        } else {
            View view = LayoutInflater.from(mContext).inflate(getLayoutId(viewType),
                    parent, false);
            return new BaseViewHolder(view);
        }
    }

    public abstract int getLayoutId(int viewType);

    @Override
    public void onBindViewHolder(final BaseViewHolder holder, int position) {
        if (getItemViewType(position) != TYPE_NORMAL) return;
        onBind(holder, position - mHeaderViews.size());

        if (null != mOnItemClickListener) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    mOnItemClickListener.itemSelect(
                            holder.getAdapterPosition() - mHeaderViews.size());
                }
            });
        }
    }

    public abstract void onBind(BaseViewHolder holder, int position);

    @Override
    public void onViewRecycled(final BaseViewHolder holder) {
        super.onViewRecycled(holder);
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();

        if (manager instanceof GridLayoutManager) {
            final GridLayoutManager gridManager = ((GridLayoutManager) manager);
            gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    return getItemViewType(position) != TYPE_NORMAL ? gridManager.getSpanCount() : 1;
                }
            });
        }
    }

    /**
     * @see RecyclerVDivider
     * <ul>判断是否添加分割符的标识
     * <li>true_添加,false_不添加</li></ul>
     * Title:另外使用分割符的情况下可删除该变量
     */
    public boolean isMainItem;

    /**
     * @see RecyclerVDivider
     * 获取GridLayoutManager/StaggeredGridLayoutManager当前Item项所处的列数(行数)
     * 提示:另外使用分割符的情况下可删除该变量
     */
    public int mSpanIndex;

    @Override
    public void onViewAttachedToWindow(BaseViewHolder holder) {
        super.onViewAttachedToWindow(holder);
        ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();

        if (null == lp) return;
        isMainItem = getItemViewType(holder.getLayoutPosition()) == TYPE_NORMAL;

        if (lp instanceof GridLayoutManager.LayoutParams) {
            GridLayoutManager.LayoutParams p = (GridLayoutManager.LayoutParams) lp;
            mSpanIndex = p.getSpanIndex();
        } else if (lp instanceof StaggeredGridLayoutManager.LayoutParams) {
            StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;
            mSpanIndex = p.getSpanIndex();
            p.setFullSpan(!isMainItem);
        }
    }

    private OnItemClickListener mOnItemClickListener;

    public void addItemClickListener(OnItemClickListener listener) {
        mOnItemClickListener = listener;
    }

    public interface OnItemClickListener {
        void itemSelect(int position);
    }
}

本来还想着直接贴重点相关的代码块就行了,毕竟这些实现都大同小异,再说就这些也上不了排场。但是我左思右想觉得不成,这个都得贴上,这样若是出现其他问题“怪我咯~”。大伙重点看onViewAttachedToWindow()就可以了,因为关于分割线绘制与否的数据判定基本上就是根据这里劫取的。

自定义RecyclerView

除此之外因RecyclerView在使用前操作过多嫌麻烦,于是自定义了一个RecyclerView的实现类,并且已在RecyclerVDivider.class当中做了相应的关联和处理,若不嫌弃可一并拿去。

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.support.v7.widget.DefaultItemAnimator;
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 com.mrv.R;
import com.mrv.utils.UnitConverter;

/**
 * 重定义RecyclerView使用
 */
public class MyRecyclerView extends RecyclerView {

    public static final int TYPE_LIST = 0;
    public static final int TYPE_GRID = 1;
    public static final int TYPE_STAGGERED_GRID = 2;

    /**
     * <ul>列表类型
     * <li>同 ListView :{@link #TYPE_LIST}(默认)</li>
     * <li>同 GridView :{@link #TYPE_GRID}</li>
     * <li>同 StaggeredGridView :{@link #TYPE_STAGGERED_GRID}</li></ul>
     */
    private int mType;

    public static final int VERTICAL = 1;
    public static final int HORIZONTAL = 0;

    /**
     * <ul>列表类型
     * <li>垂直方向 :{@link #VERTICAL}(默认)</li>
     * <li>水平方向 :{@link #HORIZONTAL}</li></ul>
     */
    private int mOrientation;

    /**
     * ?固定大小,默认固定
     */
    private boolean isFixSize = true;

    private static final int DEFAULT_ROW_NUM = 2;

    /**
     * GRID与STAGGERED_GRID显示列数,默认{@link #DEFAULT_ROW_NUM}列
     */
    private int mSpanCount;

    /**
     * @see RecyclerVDivider
     * 分割线,默认从styles中获取
     */
    private Drawable mDividerDraw;

    /**
     * 分割线左侧边距,默认无边距
     */
    private int mDividerLeftSpace;

    /**
     * 分割线右侧边距,默认无边距
     */
    private int mDividerRightSpace;

    /**
     * 分割线顶部边距,默认无边距
     */
    private int mDividerTopSpace;

    /**
     * 分割线底部边距,默认无边距
     */
    private int mDividerBottomSpace;

    /**
     * 分割线间距颜色(即背景色),默认无
     */
    private int mDividerSpaceColor;

    public MyRecyclerView(Context context) {
        super(context);
        initView();
    }

    public MyRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.recycle_view);
        isFixSize = typedArray.getBoolean(R.styleable.recycle_view_fixSize, true);
        mSpanCount = typedArray.getInteger(R.styleable.recycle_view_spanCount, DEFAULT_ROW_NUM);
        mType = typedArray.getInt(R.styleable.recycle_view_type, TYPE_LIST);
        mOrientation = typedArray.getInt(R.styleable.recycle_view_orientation, VERTICAL);
        mDividerDraw = typedArray.getDrawable(R.styleable.recycle_view_divider);
        mDividerLeftSpace = typedArray.getInt(R.styleable.recycle_view_dividerLeftSpace, 0);
        mDividerRightSpace = typedArray.getInt(R.styleable.recycle_view_dividerRightSpace, 0);
        mDividerTopSpace = typedArray.getInt(R.styleable.recycle_view_dividerTopSpace, 0);
        mDividerBottomSpace = typedArray.getInt(R.styleable.recycle_view_dividerBottomSpace, 0);
        mDividerSpaceColor = typedArray.getColor(R.styleable.recycle_view_dividerSpaceColor, 0);
        typedArray.recycle();
        initView();
    }

    public MyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    private void initView() {
        // 布局样式
        switch (mType) {
            case TYPE_LIST:
                LinearLayoutManager linearLayoutManager = new LinearLayoutManager(
                        getContext(), mOrientation, false);
                this.setLayoutManager(linearLayoutManager);
                break;
            case TYPE_GRID:
                GridLayoutManager gridLayoutManager = new GridLayoutManager(
                        getContext(), mSpanCount, mOrientation, false);
                this.setLayoutManager(gridLayoutManager);
                break;
            case TYPE_STAGGERED_GRID:
                StaggeredGridLayoutManager staggeredGridLayoutManager =
                        new StaggeredGridLayoutManager(mSpanCount, mOrientation);
                this.setLayoutManager(staggeredGridLayoutManager);
                break;
        }

        // 分割线
        if (null != mDividerDraw) {
            RecyclerVDivider divider = new RecyclerVDivider(mDividerDraw);
            divider.setLeftSpace(UnitConverter.dip2px(getContext(), mDividerLeftSpace));
            divider.setRightSpace(UnitConverter.dip2px(getContext(), mDividerRightSpace));
            divider.setTopSpace(UnitConverter.dip2px(getContext(), mDividerTopSpace));
            divider.setBottomSpace(UnitConverter.dip2px(getContext(), mDividerBottomSpace));
            if (0 != mDividerSpaceColor) divider.setSpaceColor(mDividerSpaceColor);

            this.addItemDecoration(divider);
        }

        // 大小固定
        this.setHasFixedSize(isFixSize);
        // Item出入动画
        this.setItemAnimator(new DefaultItemAnimator());
    }

    public int getSpanCount() {
        return mSpanCount;
    }

    public int getOrientation() {
        return mOrientation;
    }

    public int getType() {
        return mType;
    }
}

下面是关于自定义属性的内容与对应实现功能,其中需要注意的是如果启动了分割符的边距设置,这需要相应的设置一下分割符的颜色,从而避免产生的边距露出Item的父容器背景颜色

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="recycle_view">
        <attr name="fixSize" format="boolean" />
        <attr name="spanCount" format="integer" />
        <attr name="divider" format="reference"/>
        <attr name="dividerLeftSpace" format="integer" />
        <attr name="dividerRightSpace" format="integer" />
        <attr name="dividerTopSpace" format="integer" />
        <attr name="dividerBottomSpace" format="integer" />
        <attr name="dividerSpaceColor" format="color" />
        <attr name="type">
            <flag name="list" value="0" />
            <flag name="grid" value="1" />
            <flag name="staggeredGrid" value="2" />
        </attr>
        <attr name="orientation">
            <flag name="vertical" value="1" />
            <flag name="horizontal" value="0" />
        </attr>
    </declare-styleable>
</resources>
NameFormatFunction
fixSizeboolean设置true提升性能,默认true
spanCountinteger网格以及流布局显示最大列数,默认2
dividerreference分割符资源Id,默认没有分割符
dividerLeftSpaceinteger分隔符左边距,默认0,单位dp
dividerRightSpaceinteger分隔符左边距,默认0,单位dp
dividerTopSpaceinteger分隔符顶部边距,默认0,单位dp
dividerBottomSpaceinteger分隔符底部边距,默认0,单位dp
dividerSpaceColorcolor分隔符底部颜色,默认无
dividerSpaceColorcolor分隔符底部颜色,默认无
typeflaglist:列表;grid:网格;staggeredGrid:瀑布流
orientationflagvertical:垂直方向延伸; horizontal:水平方向延伸

结尾助力

以上为本篇重点简述内容,剩下的讲讲何为”助力篇“的实际意义。一方面,个人觉得为RecyclerView添加分割符的形式有些毛躁,因为判断太多直接影响性能。另一方面是自己能力尚浅,没有办法将其优化好,包括逻辑处理以及动画效果的延伸。

还可以的RecyclerView

如果你觉得上面的gif演示已经比较nice了,那么再请你看看下面的另一个gif演示:

糟透了的RecyclerView

看完上面两者的比较以后不难发现存在的问题了,当然如果用回notifyDataSetChanged();是可以尽量避免这些问题的。而这里不仅是为了将存在的问题表露出来,更多的是为了能让有实力的小伙伴们帮忙解决问题,或者提供一些已有的解决方案。以下为浮现的问题:

  1. Item进出动画卡顿
  2. 分割线配合动画使用绘制无刷新(可能页面显示无更新,又或者只是绘制逻辑上出现的错误)
  3. 有些情况下使用动画会有闪屏的现象,体验较差。如上面gif从底部scrollToPosition()到顶部时候出现闪屏

Fork助力项目地址:https://github.com/gzejia/URecyclerView

分隔符使用参考:Android RecyclerView 使用完全解析 体验艺术般的控件

添加头部/底部视图参考:RecyclerView添加Header的正确方式

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值