• 关于安卓自定义弹幕控件的实现(recyclerview)(一)


    !!!代码在文末!!!
    背景

    对于弹幕功能,稍微有点社交内容的app都会用到,其中,实现的方法有很多,本文主要通过
    安卓原生recyclerview进行实现

    思路

    首先,recyclerview提供了api给开发者进行滚动的调用,同时也提供了相关接口,对滚动的
    距离进行计算。因此,通过滑动距离监听,以及定时任务进行内容滚动,即可实现。

    实现细节

    对于上述思路以外,还要注意的是,recyclerview的滑动事件分发问题。本次需求要求的是
    recyclerview不能与用户产生触摸交互,因此需要特别处理。
    同时,需要注意对象引用问题。

    实现过程

    (一)定义好外部传入数据的方法
    (二)定义一个定时器,用作定时滑动
    (三)触摸距离计算,重新滚动逻辑设置
    (四)处理触摸事件分发

    实现说明

    本次弹幕场景,仅仅是设置一次数据,若要进行分页加载,原理其实也是大同小异,稍作修改即可。

    实现

    (一)定义好一个setData方法,外部传入的是一个bean集合,集合的变量自行定义,这里我
    定义的变量如下:

        //类型:1内容 2空布局填充
        private int type = 1;
    
        //昵称
        private String nickName;
    
        //文字
        private String content;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    因为需求中,需要显示内容前,有一段空白的滑动距离,因此定义一个type==2的情况。

    传入代码方法设置后,即可刷新adapter了,注意的是,每次刷新数据,都
    需要对定时器进行cancel,否则会导致数据错乱的问题。
    核心代码如下:

        /**
         * 设置数据
         */
        public void setData(List bulletList) {
            if (handler == null) {
                return;
            }
            if (System.currentTimeMillis() - mSetDataInterval < 1000) {
                return;
            }
            mSetDataInterval = System.currentTimeMillis();
            clearHandler();
            handler.post(new Runnable() {
                @Override
                public void run() {
                    //克隆数据
                    List trainList = new ArrayList<>();
                    if (bulletList == null || bulletList.size() == 0) {
                        trainList = new ArrayList<>();
                    } else {
                        for (BulletInfo bulletInfo : bulletList) {
                            BulletInfo cache = new BulletInfo();
                            cache.setType(bulletInfo.getType());
                            cache.setContent(bulletInfo.getContent());
                            cache.setNickName(bulletInfo.getNickName());
                            trainList.add(cache);
                        }
                    }
                    mBulletCacheList.clear();
                    mBulletCacheList.addAll(trainList);
                    if (mInfoAdapter != null) {
                        mInfoAdapter.clear();
                        mInfoAdapter.setData(trainList);
                    }
                    scrollToPosition(0);
                    startHandler();
                }
            });
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    (二)定义定时器
    这里使用Timer和TimerTask实现。通过查看源码发现,recyclerview的滑动方法,是支持子线程调用的,所以直接使用子线程执行的timer进行轮询。
    核心代码如下:

        private void startHandler() {
            if (mTimer == null) {
                mTimer = new Timer();
            }
            mTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    try {
                        smoothScrollBy(0, 6);
                        //检查是否滑动到了底部
                        checkScrollToBottom();
                    } catch (Exception e) {
    
                    }
                }
            }, 10, 80);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    (三)触摸距离计算
    触摸距离,本次实战中,其实用到了就是判断recyclerview是否滑动到底部的判断而已,没什么大不了的。主要的也是调用官方的api,直接放出核心代码:

        private void checkScrollToBottom() {
            //监听回调--------------------------------------------------
            try {
                //int offset, int progress, int duration, float percent
                //verticalOffset, verticalOffset + verticalExtent, verticalTotal,
                // (((verticalOffset + verticalExtent) * 1.0f) / (verticalTotal * 1.0f))
                int verticalExtent = computeVerticalScrollExtent();
                int verticalOffset = computeVerticalScrollOffset();
                int verticalTotal = computeVerticalScrollRange();
                float percent = (((verticalOffset + verticalExtent) * 1.0f) / (verticalTotal * 1.0f));
                if (percent == 1) {
                    //已经到底了
                    if (mInfoAdapter != null && mInfoAdapter.getData() != null &&
                            mInfoAdapter.getData().size() != 0) {
                        //重新设置数据
                        List trainResult = new ArrayList<>();
                        for (BulletInfo bulletInfo : mBulletCacheList) {
                            BulletInfo cache = new BulletInfo();
                            cache.setType(bulletInfo.getType());
                            cache.setContent(bulletInfo.getContent());
                            cache.setNickName(bulletInfo.getNickName());
                            trainResult.add(cache);
                        }
                        setData(trainResult);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    可以看到,主要方法有:

                int verticalExtent = computeVerticalScrollExtent();
                int verticalOffset = computeVerticalScrollOffset();
                int verticalTotal = computeVerticalScrollRange();
    
    • 1
    • 2
    • 3

    主要是计算recyclerview的滑动距离,范围,总距离,然后计算,即可得出滑动百分比。

    (四)处理触摸事件
    主要设置recyclerview不处理滑动事件即可,核心代码如下:

    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent e) {
            return false;
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            return false;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    最后总结一下,若recyclerview在普通页面使用,直接调用设置数据方法即可,若在recyclerview中的子布局中使用,要注意recyclerview的复用问题,建议每次使用前,都把数据清空一次。

    代码地址–库libbullet

    that’s all--------------------------------------------------------------------

  • 相关阅读:
    每日一题|2022-11-8|1684. 统计一致字符串的数目|哈希表|Golang
    Vue3自定义指令(directive)
    第33章_瑞萨MCU零基础入门系列教程之DHT11温湿度获取实验
    C#中隐藏窗体并执行窗体逻辑的方法
    五种多目标优化算法(MOFA、NSWOA、MOJS、MOAHA、MOPSO)性能对比(提供MATLAB代码)
    目前最好用的NAS系统是什么?
    AR手势识别交互,让应用更加“得心应手”
    用crash tool观察ARM64 Linux地址转换
    如何计算股票程序化交易系统的收益率?
    Linux系统性能监控分析工具perf
  • 原文地址:https://blog.csdn.net/motosheep/article/details/126898446