利用RecyclerView实现Banner轮播图

随着入行时间变长,越来越懒得使用开源裤子,对第三方的认知也越来越清晰,有团队支撑的裤子还好,个人开发的如果遇到BUG,有些问题是很难自己修复的;
这不,在使用人气最高的Banner框架时,就遇到了一个无法处理的BUG,我觉得是小问题,就是显示越界,但是我只能大眼瞪小眼的没法搞,相比于修复这个BUG,索性自己实现一个轮播图;
利用RecyclerView作为主体,通过自定义Adapter完成,对相关逻辑进行封装;布局中直接使用RecyclerView,并且绑定自定义Adapter,只需要重写两个方法既可轻松实现
自定义Adapter继承BaseBannerAdapter,并重写createLayout()、bindViewData()


/**
 * Created by dzh on 05.21.021.
 * 轮播图适配器
 */
public class BannerAdapter extends BaseBannerAdapter {
    private Context context;

    public BannerAdapter(Context context) {
        this.context = context;
        //添加点击事件(这个语法是 java 1.8,如果你的项目不是用java1.8写的,自行new一个监听器出来即可)
        setOnItemClickListener((obj, position) ->{
            BannerBean bannerBean = (BannerBean) obj;
            ToastUtils.showShort("点击第:" + position + "   " + bannerBean.getPosition());
            System.out.println("s" + position);
        });
    }

    /**
     * 绑定Item视图,View可以任意自定义
     * @return
     */
    @Override
    protected int createLayout() {
        return R.layout.item_banner;
    }

    /**
     * 绑定数据,将第二个参数强转换为自己的javabean
     * @param holder
     * @param data
     * @param position
     */
    @Override
    protected void bindViewData(ViewHolder holder, Object data, int position) {
        BannerBean bannerBean = (BannerBean) data;
        //通过 holder.findView()可以获取你自定义的对应控件
        ImageView bannerImg = holder.findView(R.id.bannerImg);
        bannerImg.setImageResource(bannerBean.getPosition());
    }

}

好了,上面就是最精简的轮播图适配器,其余工作全都交给父类去完成;
在Activity/Fragment中绑定RecyclerView即可

        //模拟数据
        int[] imgs = {R.mipmap.banner1,R.mipmap.banner2,R.mipmap.banner3};
        bannerAdapter = new BannerAdapter(this);
        //因为我将RecyclerView的一些设置封装在了Adapter中
        //所以这里不再是RecyclerView.setAdapter()了,而是由Adapter绑定RecyclerView,请注意
        bannerAdapter.bindingRecyclerView(mainBanner);
        bannerBeans = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            bannerBeans.add(new BannerBean(imgs[i]));
        }
        //把数据交给Adapter
        bannerAdapter.addData(bannerBeans);

下面是完整的BaseBannerAdapter


/**
 * Created by dzh on 05.21.021.
 * 无限轮播适配器
 */
public abstract class BaseBannerAdapter extends RecyclerView.Adapter {
    private static final String TAG = "BaseBannerAdapter";
    private RecyclerView recyclerView;
    private int position;
    private List data;
    private int duration = 5000;//切换时间,默认5秒
    private OnItemClickListener onItemClickListener;
    private Handler handler;
    private Runnable runnable;
    private boolean isPause;//暂停
    private OnPageChangeListener onPageChangeListener;


    public interface OnPageChangeListener {
        void onPage(int sum, int page);
    }

    public interface OnItemClickListener {
        void onItemClick(Object obj, int position);
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(createLayout(), parent, false);
        ViewHolder holder = new ViewHolder(view);
        if (onItemClickListener != null) {
            int position = computePage(holder.getLayoutPosition());
            holder.itemView.setOnClickListener(v -> onItemClickListener.onItemClick(data != null ? data.get(position) : null, position));
        }
        return holder;
    }


    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        if (data == null) return;
        bindViewData(holder, data.get(computePage(position)), computePage(position));
    }

    /**
     * 核心方法,返回int最大值
     */
    @Override
    public int getItemCount() {
        return getTrueItemCount();
    }

    private int getTrueItemCount() {
        if (data == null || data.size() == 0) {
            return 0;
        } else if (data.size() == 1) {
            return 1;
        }
        return Integer.MAX_VALUE;
    }

    /**
     * ViewHolder
     */
    public class ViewHolder extends RecyclerView.ViewHolder {
        private View view;

        ViewHolder(@NonNull View itemView) {
            super(itemView);
            this.view = itemView;
        }

        public  V findView(@IdRes int id) {
            return view.findViewById(id);
        }
    }

    /**
     * 计算相应页码
     *
     * @param page 实际页码
     * @return 相应页码下标
     */
    private int computePage(int page) {
        int size = data.size();
        return page % size;
    }

    /**
     * 抽象方法,用于子类创建View
     */
    protected abstract int createLayout();

    /**
     * 抽象方法,用于子类绑定数据
     */
    protected abstract void bindViewData(ViewHolder holder, Object data, int position);

    /**
     * 绑定RecyclerView
     */
    public void bindingRecyclerView(RecyclerView recyclerView) {
        this.recyclerView = recyclerView;
        recyclerView.setAdapter(this);
        recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext(), LinearLayoutManager.HORIZONTAL, false) {
            @Override
            public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
                LinearSmoothScroller smoothScroller =
                        new LinearSmoothScroller(recyclerView.getContext()) {
                            //调整RecyclerView切换时的滚动速度
                            @Override
                            protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
                                return 150f / displayMetrics.densityDpi;
                            }
                        };
                smoothScroller.setTargetPosition(position);
                startSmoothScroll(smoothScroller);
            }
        });
        PagerSnapHelper helper = new PagerSnapHelper() {
            @Override
            public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) {
                position = super.findTargetSnapPosition(layoutManager, velocityX, velocityY);
                if (onPageChangeListener != null) {
                    int page = position % data.size() + 1;
                    onPageChangeListener.onPage(data.size(), page);
                }
                return position;
            }
        };
        helper.attachToRecyclerView(recyclerView);
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                if (newState == 1) {
                    if (handler != null)
                        handler.removeCallbacks(runnable);
                    handler = null;
                } else {
                    if (handler == null)
                        startTimer();
                }
            }
        });

        recyclerView.getViewTreeObserver().addOnWindowAttachListener(new ViewTreeObserver.OnWindowAttachListener() {
            @Override
            public void onWindowAttached() {
            }

            @Override
            public void onWindowDetached() {
                if (handler != null) {
                    if (runnable != null) {
                        handler.removeCallbacks(runnable);
                    }
                    handler = null;
                }
            }
        });
    }


    /**
     * 绑定数据
     */
    public void setData(List data) {
        this.data = data;
        Log.d("Banner", "dataSize: " + data.size());
        notifyDataSetChanged();
        toCenterPage();
        if (handler == null) {
            startTimer();
        }
        if (onPageChangeListener != null) {
            onPageChangeListener.onPage(data.size(), 1);
        }
    }

    /**
     * 跳转到中心页,并从第一页开始
     */
    private void toCenterPage() {
        if (getTrueItemCount() < 2) {
            return;
        }
        int centerPage = Integer.MAX_VALUE / 2;
        if (centerPage % data.size() > 0) {
            centerPage = centerPage - centerPage % data.size();
        }
        if (recyclerView != null) {
            recyclerView.scrollToPosition(position = centerPage);
        }
    }

    /**
     * 开始轮播 每5秒执行一次
     */
    private void startTimer() {
        if (getTrueItemCount() < 2) return;
        if (handler == null)
            handler = new Handler();
        if (runnable == null)
            runnable = () -> {
                Log.d("banner", "切换: ");
                if (recyclerView != null) {
                    pageDown();
                    handler.postDelayed(runnable, duration);
                }
            };
        handler.postDelayed(runnable, duration);
    }


    public void setDuration(int duration) {
        this.duration = duration;
    }

    /**
     * 当前页码 +1,切换到下一页
     */
    private void pageDown() {
        if (recyclerView != null) {
            recyclerView.smoothScrollToPosition(position = position + 1);
        }
        if (onPageChangeListener != null) {
            int page = position % data.size() + 1;
            onPageChangeListener.onPage(data.size(), page);
        }
    }

    /**
     * 设置点击事件
     */
    protected void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
        this.onPageChangeListener = onPageChangeListener;
    }

    /**
     * 恢复轮播
     */
    public void onResume() {
        if (isPause) {
            startTimer();
        }
        isPause = false;
    }

    public void onPause() {
        isPause = true;
        if (handler != null)
            handler.removeCallbacks(runnable);
        handler = null;
    }

}

BaseBannerAdapter的代码就这么多,以实现自动轮播,和手势触摸时停止自动轮播,离开时自动恢复轮播功能,下面是点击事件和轮播监听器用于实现指示器。


       bannerAdapter.setOnPageChangeListener((sum, page) -> {
            gdBannerIndicator.setText(page + "/" + sum);
        });

你可能感兴趣的:(利用RecyclerView实现Banner轮播图)