文档章节

Android 使用RecyclerView实现轮播图

IamOkay
 IamOkay
发布于 2019/04/28 18:02
字数 955
阅读 2K
收藏 2

一、需求

之前一篇博客使用ViewPager实现轮播图《Android ViewPager实现循环轮播图》,但是ViewPager有个天生的缺陷是View无法重用,此外ViewPager的滑动过程会频繁requestLayout,尽管可以通过addViewInLayout和removeViewInLayout配合PagerAdapter 的startUpdate和finishUpdate可以减少重绘,但在ListView和RecyclerView中仍然达不到最好的效果。因此,使用一种新的方式十分必要。

 

二、代码实现

RecyclerPagerView 

public class RecyclerPagerView extends RecyclerView implements Handler.Callback {

    private static final long TASK_TIMEOUT = 3000;
    public OnPageChangeListener onPageChangeListener;

    private final Handler mRecyclerHandler;
    private final int MSG_PLAY_NEXT  = 112233;
    private volatile boolean isPlaying = false;
    private boolean lastIsPlayState = false;
    private int realPosition = -1;

    public RecyclerPagerView(Context context) {
        this(context,null);
    }

    public RecyclerPagerView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public RecyclerPagerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mRecyclerHandler = new Handler(Looper.getMainLooper(),this);
    }

    public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
        this.onPageChangeListener = onPageChangeListener;
        if(this.onPageChangeListener!=null){
            addOnScrollListener(this.onPageChangeListener);
            int currentItem = getCurrentItem();
            this.onPageChangeListener.onPageSelection(currentItem);
        }
    }

    public int getCurrentItem(){
        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();
        return linearLayoutManager.findFirstVisibleItemPosition();
    }

    public void setCurrentItem(int position,boolean isAnimate){
        Adapter adapter = getAdapter();
        if(adapter==null || adapter.getItemCount()<=position){
            return;
        }
        if(!isAnimate)
        {
            scrollToPosition(position);
        }else {
            smoothScrollToPosition(position);
        }
    }
    public void setCurrentItem(int position ){
       setCurrentItem(position,true);
    }


    @Override
    public boolean fling(int velocityX, int velocityY) {

        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();

        int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;

        // views on the screen
        int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
        View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition);
        int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
        View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition);

        // distance we need to scroll
        int leftMargin = (screenWidth - lastView.getWidth()) / 2;
        int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth();
        int leftEdge = lastView.getLeft();
        int rightEdge = firstView.getRight();
        int scrollDistanceLeft = leftEdge - leftMargin;
        int scrollDistanceRight = rightMargin - rightEdge;

        int targetPosition;

        if (Math.abs(velocityX) < 1500) {
            // The fling is slow -> stay at the current page if we are less than half through,
            // or go to the next page if more than half through

            if (leftEdge > screenWidth / 2) {
                // go to next page
                smoothScrollBy(-scrollDistanceRight, 0);
                targetPosition = firstVisibleItemPosition;

            } else if (rightEdge < screenWidth / 2) {
                // go to next page
                smoothScrollBy(scrollDistanceLeft, 0);
                targetPosition = firstVisibleItemPosition+1;
            } else {
                // stay at current page
                if (velocityX > 0) {
                    smoothScrollBy(-scrollDistanceRight, 0);
                } else {
                    smoothScrollBy(scrollDistanceLeft, 0);
                }
                targetPosition = firstVisibleItemPosition;
            }
        } else {
            // The fling is fast -> go to next page

            if (velocityX > 0) {
                smoothScrollBy(scrollDistanceLeft, 0);
                targetPosition = firstVisibleItemPosition+1;
            } else {
                smoothScrollBy(-scrollDistanceRight, 0);
                targetPosition = firstVisibleItemPosition;
            }

        }

        Log.e("RecyclerPagerView","nextPage="+targetPosition);
        if(this.onPageChangeListener!=null){
            realPosition = targetPosition;
            this.onPageChangeListener.onPageSelection(targetPosition);
        }
        return true;
    }



    @Override
    public void onScrollStateChanged(final  int state) {
        super.onScrollStateChanged(state);

        if (state == SCROLL_STATE_IDLE) {

            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();

            int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;

            int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
            View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition);
            int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
            View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition);

            // distance we need to scroll
            int leftMargin = (screenWidth - lastView.getWidth()) / 2;
            int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth();
            int leftEdge = lastView.getLeft();
            int rightEdge = firstView.getRight();
            int scrollDistanceLeft = leftEdge - leftMargin;
            int scrollDistanceRight = rightMargin - rightEdge;
            int  targetPosition = -1;
            if (leftEdge > screenWidth / 2) {
                smoothScrollBy(-scrollDistanceRight, 0);
                targetPosition = firstVisibleItemPosition+1;
            } else if (rightEdge < screenWidth / 2) {
                smoothScrollBy(scrollDistanceLeft, 0);
                targetPosition = lastVisibleItemPosition;
            }else{
                targetPosition = firstVisibleItemPosition;
            }
            if(this.onPageChangeListener!=null){
                realPosition = targetPosition;
                this.onPageChangeListener.onPageSelection(targetPosition);
            }
        }

    }

    @Override
    public boolean handleMessage(Message msg) {
        int what = msg.what;
        switch (what){
            case MSG_PLAY_NEXT:
                showNextPage();
                break;
        }

        return false;
    }

    private void showNextPage() {
        if(!isPlaying){
            return;
        }
        if(!canRecyclePlaying()){
            isPlaying = false;
            return;
        }
        Adapter adapter = getAdapter();
        int currentItem = getCurrentItem();
        if(adapter!=null && adapter.getItemCount()>0) {
            if (currentItem == NO_POSITION  ) {
                setCurrentItem(0);
            }else {
                setCurrentItem(currentItem+1);
            }
        }

        mRecyclerHandler.sendEmptyMessageDelayed(MSG_PLAY_NEXT,TASK_TIMEOUT);
    }

    public void startPlay(){
        if(isPlaying){
            stopPlay();
        }
        if (!canRecyclePlaying()){
            isPlaying = false;
            return;
        }

        isPlaying = true;
        mRecyclerHandler.sendEmptyMessageDelayed(MSG_PLAY_NEXT,TASK_TIMEOUT);
    }

    @Override
    public void setAdapter(Adapter adapter) {
        super.setAdapter(adapter);

        if(canRecyclePlaying()){
            if(realPosition==-1){
                realPosition = 1000;
            }
            setCurrentItem(realPosition,false);
        }
    }

    private boolean canRecyclePlaying() {
        Adapter adapter = getAdapter();
        if(adapter==null || adapter.getItemCount()<1) return false;
        return true;
    }

    private void stopPlay() {
        isPlaying = false;
        mRecyclerHandler.removeMessages(MSG_PLAY_NEXT);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if(lastIsPlayState){
            startPlay();
        }

    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        lastIsPlayState = isPlaying;
        stopPlay();
    }

    public static    abstract class OnPageChangeListener extends RecyclerView.OnScrollListener{
            public abstract  void onPageSelection(int position);
    }

}

 

Adapter+Holder 适配器

该类的作用主要用于约束和快速接入

public class QuickViewHolder extends RecyclerView.ViewHolder{
    private SparseArray<View> mViews;
    private View mConvertView;

    private QuickViewHolder(View v){
        super(v);
        mConvertView = v;
        mViews = new SparseArray<>();
    }

    public static QuickViewHolder get(ViewGroup parent, int layoutId){
        View convertView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
        return new QuickViewHolder(convertView);
    }

    public <T extends View> T getView(int id){
        View v = mViews.get(id);
        if(v == null){
            v = mConvertView.findViewById(id);
            mViews.put(id, v);
        }
        return (T)v;
    }


}

 

BannerAdapter实现,该类继承QuickAdapter

public class BannerAdapter extends QuickAdapter<String> {

    public BannerAdapter(List<String> datas) {
        super(datas);
    }

    @Override
    public int getLayoutId(int viewType) {
        return R.layout.item_banner_image;
    }

    @Override
    public void convert(QuickViewHolder holder, String url, int position) {


        final Resources resources = holder.itemView.getResources();
        final int drawableId = resources.getIdentifier(url, "drawable", holder.itemView.getContext().getPackageName());
        if(drawableId!=0) {
            ImageView bannerImage = holder.getView(R.id.banner_image_item);
            bannerImage.setImageResource(drawableId);
        }

    }
}

 

三、使用

        final RecyclerPagerView rpv  = findViewById(R.id.recycler_pager);
        tipTextView                  = findViewById(R.id.tip_text);
        LinearLayoutManager lm = new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false);
        rpv.setLayoutManager(lm);

        List<String> imagelist = new ArrayList<>();
        imagelist.add("banner_t1");
        imagelist.add("banner_t2");
        imagelist.add("banner_t3");
        imagelist.add("banner_t4");

        rpv.setAdapter(new BannerAdapter(imagelist));
        rpv.setOnPageChangeListener(new PagerChangeListener(tipTextView,imagelist.size()));
        rpv.startPlay();

监听器


    public static class  PagerChangeListener extends RecyclerPagerView.OnPageChangeListener {

        private TextView tipTextView;
        private int size;

        public PagerChangeListener(TextView tipTextView,int size) {
            this.tipTextView = tipTextView;
            this.size = size;
        }

        @Override
        public void onPageSelection(int position) {
            tipTextView.setText((position%size+1)+"/"+size);
        }
    }

 

 

© 著作权归作者所有

IamOkay

IamOkay

粉丝 205
博文 483
码字总数 403230
作品 0
海淀
程序员
私信 提问
加载中

评论(2)

唧唧复及及
大神,有源码吗? 767302856@qq.com 谢谢
IamOkay
IamOkay 博主
这博客中的就是源码
ViewPager2:官方Viewpager升级版来临

这两天浏览安卓开发者官网的时候,发现google悄然推出了一个新的控件:ViewPager2,一看名称就知道这是一个和我们常用的ViewPager功能相似的控件,算是ViewPager的升级版吧。目前还只是推出了...

大头呆
2019/02/21
0
0
android手机卫士、3D指南针、动画精选、仿bilibli客户端、身份证银行卡识别等源码

Android精选源码 android身份证、银行卡号扫描源码(http://www.apkbus.com/thread-599859-1-1.html) android仿bilibili客户端(http://www.apkbus.com/thread-599860-1-1.html) android一款3......

逆鳞龙
2018/06/04
45
0
长图片自动循环滚动效果 (仿小红书)

先看小红书的效果 说说思路 滚动效果用RecyclerView实现。RecyclerView有个smoothScrollToPosition方法,可以滚动到指定位置(有滚动效果,不是直接到指定位置),不了解的看这里RecycleView...

丶ZHI念
2018/08/04
0
0
用RecyclerView打造一个轮播图

通常Android的轮播图(俗名:Banner)都是用ViewPager实现的,但是我在实际项目运用中碰到了一些小问题,于是决定另寻思路,采用RecyclerView这个更优雅更强大的控件来实现轮播的功能,顺便复习...

大头呆
2017/11/20
0
0
教你如何用 RecyclerView 做一个好用的轮播图

引子 一般情况下,我们手机 App 上轮播图一般都是几张图来回循环,最多也就10几张,一般都是在10张以内的轮播。所以我们一般可能都是自己写,还有可能用到了别人写的第三方库。由此可能由于图...

涩郎
2017/03/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

序列化、反序列化和transient关键字的作用

引言 序列化:将一个对象转换成一串二进制表示的字节数组,通过保存或转移这些字节数据来达到持久化的目的。 反序列化:将字节数组重新构造成对象。 将 Java 对象序列化为二进制文件的 Java...

潦草的犀牛
33分钟前
57
0
聊聊artemis的maxDeliveryAttempts

序 本文主要研究一下artemis的maxDeliveryAttempts maxDeliveryAttempts activemq-artemis-2.11.0/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSe......

go4it
51分钟前
37
0
heroku - 如何查看所有日志

我在heroku上有一个小应用程序。 每当我想看日志时,我都会去命令行做 heroku logs 这只能让我看到大约100行。 有没有办法在heroku上查看我们的应用程序的完整日志? #1楼 对于雪松堆栈,请...

javail
58分钟前
37
0
Spring AOP 创建增强类

AOP联盟为增强定义了org.aopalliance.aop.Advice接口,Spring支持5种类型的增强: 1)前置增强:org.springframework.aop.BeforeAdvice 代表前置增强,因为Spring 只支持方法级的增强,所有M...

onedotdot
今天
51
0
将.apply()与'new'运算符配合使用。 这可能吗?

在JavaScript中,我想创建一个对象实例(通过new运算符),但是将任意数量的参数传递给构造函数。 这可能吗? 我想做的是这样的(但是下面的代码不起作用): function Something(){ //...

技术盛宴
今天
74
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部