PageAdapter的封装,优雅的实现多布局轮播图

前言

不知道你们有没有遇到过这样一个需求,轮播图上有好几种布局,反正我遇到了。刚听见这个需求的时候就想,多布局轮播图,那不是可以用fragment实现么,但又一想,那不得写好几个fragment,多麻烦啊,一点都不优雅,不符合咱的身份。然后回去好好想了想,多布局,RecyclerView的Adpater不是可以根据viewType的不同而加载不同的布局,实现多布局功能吗,那轮播图的PageAdapter是不是也可以呢?说干就干。

效果

实现

PageApdater是在instantiateItem()方法中通过inflate()获取到布局,然后设置到父布局中显示,那么我们可以仿照RecyclerView.Adapter,对每个item设置不同的type,然后在inflate()的时候根据type不同区获取对应的布局,然后添加到父布局中。

先定义接口,子类要实现的方法

    /**
      * 获取item的type
      */
    protected abstract int getItemViewType(int position);

    /**
      * 获取item的布局
      */
    protected abstract int getLayoutId(int position, int itemViewType);

    /**
      * 对布局子view进行操作
      */
    protected abstract void handleView(int position, int itemViewType, View parent);

然后在instantiateItem()方法中

    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        int itemViewType = getItemViewType(position);
        //获取视图布局id
        int layoutId = getLayoutId(position, itemViewType);
        //得到要对应显示的视图对象
        View convertView = LayoutInflater.from(container.getContext()).inflate(layoutId, null);
        //获取到type对应布局后对子view进行操作
        handleView(position, itemViewType, convertView);
        container.addView(convertView);
        return convertView;
    }

然后在子类中,只需要实现父类定义的几个接口

    @Override
    protected int getItemViewType(int position) {
        return list.get(position).getItemType();
    }

    @Override
    protected int getLayoutId(int position, int itemViewType) {
        switch (itemViewType) {
            case ScenicRotationInfo.ADVERT:
                return R.layout.item_shuffling;
            case ScenicRotationInfo.OTHER:
                return R.layout.item_channel;
            default:
                return 0;
        }
    }

    @Override
    protected void handleView(int position, int itemViewType, View parent) {
        ScenicRotationInfo info = list.get(position);
        switch (itemViewType) {
            case ScenicRotationInfo.ADVERT:
                ImageView imageView = parent.findViewById(R.id.imageView_shuffling);
                Glide.with(mContext).load(info.url).into(imageView);
                break;
            case ScenicRotationInfo.OTHER:
                TextView textView = parent.findViewById(R.id.tvChannel);
                textView.setText(info.title);
                break;
        }
    }

怎么样,是不是很简单,是不是很像我们写的RecyclerView的Adpater,不不不,还差一点,那就是对View进行复用,减少布局的加载和findViewById()。毕竟没有性能优化的Adpater是没有灵魂的。

首先要缓存布局

    /**
     * 缓存的视图
     */
    private SparseArray<ArrayList<View>> itemViewCache = new SparseArray<>();

    //修改convertView 获取方式,先从缓存集合中去取,集合内不存在再去通过inflate()进行加载
    ArrayList<View> convertViews = itemViewCache.get(itemViewType);
    if (convertViews == null || convertViews.isEmpty()) {
        //获取视图布局id
        int layoutId = getLayoutId(position, itemViewType);
        //得到我们要返回出去的视图对象可以是一个容器 也可以是一个view
        convertView = LayoutInflater.from(container.getContext()).inflate(layoutId, null);
    } else {
        // 复用视图
        // 取出移除时缓存的视图
        // 通过remove得到,避免集合重复添加长度过长
        convertView = convertViews.remove(convertViews.size() - 1);
    }

    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        View view = (View) object;
        container.removeView(view);
        //缓存视图以重用
        //当该视图移除时,添加到缓存集合中
        int itemViewType = getItemViewType(position);
        ArrayList<View> convertViews = itemViewCache.get(itemViewType);
        if (convertViews == null) {
            convertViews = new ArrayList<>();
            itemViewCache.put(itemViewType, convertViews);
        }
        convertViews.add(view);
    }

然后通过ViewHolder复用View减少findViewById(),修改instantiateItem()方法中代码

    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        View convertView;
        BaseViewHolder viewHolder;
        int itemViewType = getItemViewType(position);
        ArrayList<View> convertViews = itemViewCache.get(itemViewType);
        if (convertViews == null || convertViews.isEmpty()) {
            //获取视图布局id
            int layoutId = getLayoutId(position, itemViewType);
            //得到我们要返回出去的视图对象可以是一个容器 也可以是一个view
            convertView = LayoutInflater.from(container.getContext()).inflate(layoutId, null);
            if (convertView != null) {
                viewHolder = new BaseViewHolder(convertView);
                //将视图和viewHolder绑定
                convertView.setTag(viewHolder);
            } else {
                throw new NullPointerException("inflate layout(id:" + layoutId + ")");
            }
        } else {
            // 复用视图
            // 取出移除时缓存的视图
            // 通过remove得到,避免集合重复添加长度过长
            convertView = convertViews.remove(convertViews.size() - 1);
            viewHolder = (BaseViewHolder) convertView.getTag();
        }
        handleView(viewHolder, position, convertView);
        container.addView(convertView);
        return convertView;
    }

以及接口handleView()。

protected abstract void handleView(BaseViewHolder viewHolder, int position, int itemViewType);

@Override
    protected void handleView(BaseViewHolder viewHolder, int position, int itemViewType) {
        ScenicRotationInfo info = list.get(position);
        switch (itemViewType) {
            case ScenicRotationInfo.ADVERT:
                ImageView imageView = viewHolder.getView(R.id.imageView_shuffling);
                Glide.with(mContext).load(info.url).into(imageView);
                break;
            case ScenicRotationInfo.OTHER:
                viewHolder.setText(R.id.tvChannel, info.title);
                break;
        }
    }

我这里的BaseViewHolder是项目中引用了 BaseRecyclerViewAdapterHelper 这个库,里面提供了这个类,很棒的一个Powerful and flexible RecyclerAdapte。如果你不想引用的话,自己写一个简单的ViewHolder也可以。

public class BaseViewHolder extends ViewHolder {
    
    /**
     * Views indexed with their IDs
     */
    private SparseArray<View> views;

    /**
     * use itemView instead
     */
    public View convertView;

    public BaseViewHolder(@NonNull View itemView) {
        super(itemView);
        this.views = new SparseArray<>();
        convertView = itemView;
    }

    public <T extends View> T getView(@IdRes int viewId) {
        View view = views.get(viewId);
        if (view == null) {
            view = itemView.findViewById(viewId);
            views.put(viewId, view);
        }
        return (T) view;
    }
}

最后优化一下代码,这样比较优雅。

/**
 * Created by yangqc on 2020/5/18
 * 多布局的PageAdapter
 *
 * @Author yangqc
 */
public abstract class BaseMultiItemPageAdapter extends PagerAdapter {

    protected Context mContext;
    protected LayoutInflater inflater;

    /**
     * 获取item的type
     */
    protected abstract int getItemViewType(int position);

    /**
     * 获取item的布局
     */
    protected abstract int getLayoutId(int position, int itemViewType);

    /**
     * 对布局子view进行操作
     */
    protected abstract void handleView(BaseViewHolder viewHolder, int position, int itemViewType);

    /**
     * 缓存的视图
     */
    private SparseArray<ArrayList<View>> itemViewCache = new SparseArray<>();

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

    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        if (mContext == null)
            mContext = container.getContext();
        if (inflater == null)
            inflater = LayoutInflater.from(mContext);
        View convertView;
        BaseViewHolder viewHolder;
        int itemViewType = getItemViewType(position);
        ArrayList<View> convertViews = itemViewCache.get(itemViewType);
        if (convertViews == null || convertViews.isEmpty()) {
            //获取视图布局id
            int layoutId = getLayoutId(position, itemViewType);
            //得到我们要返回出去的视图对象可以是一个容器 也可以是一个view
            convertView = inflater.inflate(layoutId, null);
            if (convertView != null) {
                viewHolder = new BaseViewHolder(convertView);
                //将视图和viewHolder绑定
                convertView.setTag(viewHolder);
            } else {
                throw new NullPointerException("inflate layout(id:" + layoutId + ")");
            }
        } else {
            // 复用视图
            // 取出移除时缓存的视图
            // 通过remove得到,避免集合重复添加长度过长
            convertView = convertViews.remove(convertViews.size() - 1);
            viewHolder = (BaseViewHolder) convertView.getTag();
        }
        handleView(viewHolder, position, itemViewType);
        container.addView(convertView);
        return convertView;
    }

    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        View view = (View) object;
        container.removeView(view);
        //缓存视图以重用
        //当该视图移除时,添加到缓存集合中
        int itemViewType = getItemViewType(position);
        ArrayList<View> convertViews = itemViewCache.get(itemViewType);
        if (convertViews == null) {
            convertViews = new ArrayList<>();
            itemViewCache.put(itemViewType, convertViews);
        }
        convertViews.add(view);
    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现Android轮播图常用的方式是使用ViewPager和PageAdapter。下面简单介绍一下实现方法: 1. 在布局文件中添加ViewPager: ``` <androidx.viewpager.widget.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="wrap_content"/> ``` 2. 创建一个适配器用于提供ViewPager的显示内容: ``` public class BannerAdapter extends PagerAdapter { private List<ImageView> mList; public BannerAdapter(List<ImageView> list) { mList = list; } @Override public int getCount() { return mList.size(); } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Object instantiateItem(ViewGroup container, int position) { ImageView imageView = mList.get(position); container.addView(imageView); return imageView; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(mList.get(position)); } } ``` 3. 在Activity或Fragment中设置ViewPager的适配器: ``` ViewPager viewPager = findViewById(R.id.view_pager); List<ImageView> list = new ArrayList<>(); // 添加图片到list中 BannerAdapter adapter = new BannerAdapter(list); viewPager.setAdapter(adapter); ``` 4. 如果需要自动轮播,可以在Activity或Fragment中使用Handler定时更新ViewPager的显示页面: ``` Handler handler = new Handler(); Runnable runnable = new Runnable() { @Override public void run() { int currentItem = viewPager.getCurrentItem(); int nextItem = (currentItem + 1) % list.size(); viewPager.setCurrentItem(nextItem); handler.postDelayed(this, 3000); // 每隔3秒更新一次 } }; handler.postDelayed(runnable, 3000); ``` 这样就可以实现简单的轮播图效果了。如果需要更多的功能,可以参考一些轮播图开源库,如Android-ViewPagerIndicator、BannerViewPager等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值