美文网首页Android知识Android进阶之路Android技术知识
如何优雅的实现一个可复用的 PagerAdapter

如何优雅的实现一个可复用的 PagerAdapter

作者: 为何是Hex的昵称 | 来源:发表于2017-06-15 13:40 被阅读399次

这几天在项目中需要用到轮播图,由于不想使用别人开源的,于是直接使用了 ViewPager 来实现了,ViewPager 实现轮播图这里就不赘述了

开始的时候是直接继承的 PagerAdapter

public class BannerAdapter extends PagerAdapter {
    private Context mContext;
    private List<BannerBean> mDataList;

    public BannerAdapter(Context context) {
        this.mContext = context;
    }

    public void setDataList(List<BannerBean> dataList) {
        this.mDataList = dataList;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        final BannerBean bean = mDataList.get(position % mDataList.size());
        View rootView = View.inflate(mContext, R.layout.xxx, null);
        container.addView(rootView);
        return rootView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
    }

    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return (view == object);
    }
}

很简单,在 getCount() 函数里,返回 Integer.MAX_VALUE ,其他函数正常写即可
但是这样会存在一个问题,在 ViewPager 不停的滚动的时候,就会不停的调用 instantiateItem() 函数,这就会导致不停的 inflate 新的 View,这样是十分不好的
首先想到的就是 itemView 复用,当然,实现复用也是很简单的,看下面

// 缓存 list
private LinkedList<View> mViewCache = new LinkedList<>();

@Override
public Object instantiateItem(ViewGroup container, int position) {
    View convertView = null;
    if (mViewCache.size() == 0) {
        convertView = mLayoutInflater.inflate(R.layout.xxx, null, false);
    } else {
        convertView = mViewCache.removeFirst();
    }
    container.addView(convertView);
    return convertView;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    View contentView = (View) object;
    container.removeView(contentView);
    mViewCache.add(contentView);
}

OK 功能实现了,等等,说好的优雅 呢?你 ™逗我?

找屎.jpg
为了更优雅的实现,我可是加(huó)班(gāi)半个多小时,连写带自测才搞定

Talk is cheap. Show me the code

/**
 * 仿照 recyclerview.adapter 实现的具有 item view 复用功能的 PagerAdapter
 */
public abstract class ReusePagerAdapter<VH extends ReusePagerAdapter.Holder> extends PagerAdapter {

    private SparseArray<LinkedList<VH>> holders = new SparseArray<>(1);

    /**
     * 获取 item count
     *
     * @return count
     */
    public abstract int getItemCount();

    /**
     * 获取 view type
     *
     * @param position position
     * @return type
     */
    public int getItemViewType(int position) {
        return 0;
    }

    /**
     * 创建 holder
     *
     * @param parent   parent
     * @param viewType type
     * @return holder
     */
    public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);

    /**
     * 绑定 holder
     *
     * @param holder   holder
     * @param position position
     */
    public abstract void onBindViewHolder(VH holder, int position);

    @Override
    public int getCount() {
        return getItemCount();
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        // 获取 position 对应的 type
        int itemViewType = getItemViewType(position);
        // 根据 type 找到缓存的 list
        LinkedList<VH> holderList = holders.get(itemViewType);
        VH holder;
        if (holderList == null) {
            // 如果 list 为空,表示没有缓存
            // 调用 onCreateViewHolder 创建一个 holder
            holder = onCreateViewHolder(container, itemViewType);
            holder.itemView.setTag(R.id.holder_id, holder);
        } else {
            holder = holderList.pollLast();
            if (holder == null) {
                // 如果 list size = 0,表示没有缓存
                // 调用 onCreateViewHolder 创建一个 holder
                holder = onCreateViewHolder(container, itemViewType);
                holder.itemView.setTag(R.id.holder_id, holder);
            }
        }
        holder.position = position;
        holder.viewType = itemViewType;
        // 调用 onBindViewHolder 对 itemView 填充数据
        onBindViewHolder(holder, position);
        container.addView(holder.itemView);
        return holder.itemView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        View view = (View) object;
        container.removeView(view);
        VH holder = (VH) view.getTag(R.id.holder_id);
        int itemViewType = holder.viewType;
        LinkedList<VH> holderList = holders.get(itemViewType);
        if (holderList == null) {
            holderList = new LinkedList<>();
            holders.append(itemViewType, holderList);
        }
        // 缓存 holder
        holderList.push(holder);
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    public static abstract class Holder {
        public View itemView;
        public int viewType;
        public int position;

        public Holder(View view) {
            if (view == null) {
                throw new IllegalArgumentException("itemView may not be null");
            }
            itemView = view;
        }
    }
}

holder 要保存在 itemView 的 tag 中,为了防止冲突,需要指定一个 id

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="holder_id" type="id" />
</resources>

子类在继承 ReusePagerAdapter 的时候,可以直接像写 RecyclerView 的 Adapter 那样写就可以了

相关文章

网友评论

    本文标题:如何优雅的实现一个可复用的 PagerAdapter

    本文链接:https://www.haomeiwen.com/subject/jswyqxtx.html