關(guān)于安卓自定義彈幕組件實(shí)現(xiàn)(recyclerview)(一)

?。?!代碼在文末!??!

背景

對(duì)于彈幕功能,稍微有點(diǎn)社交內(nèi)容的app都會(huì)用到,其中,實(shí)現(xiàn)的方法有很多,本文主要通過
安卓原生recyclerview進(jìn)行實(shí)現(xiàn)

思路

首先,recyclerview提供了api給開發(fā)者進(jìn)行滾動(dòng)的調(diào)用,同時(shí)也提供了相關(guān)接口,對(duì)滾動(dòng)的
距離進(jìn)行計(jì)算。因此,通過滑動(dòng)距離監(jiān)聽,以及定時(shí)任務(wù)進(jìn)行內(nèi)容滾動(dòng),即可實(shí)現(xiàn)。

實(shí)現(xiàn)細(xì)節(jié)

對(duì)于上述思路以外,還要注意的是,recyclerview的滑動(dòng)事件分發(fā)問題。本次需求要求的是
recyclerview不能與用戶產(chǎn)生觸摸交互,因此需要特別處理。
同時(shí),需要注意對(duì)象引用問題。

實(shí)現(xiàn)過程

(一)定義好外部傳入數(shù)據(jù)的方法
(二)定義一個(gè)定時(shí)器,用作定時(shí)滑動(dòng)
(三)觸摸距離計(jì)算,重新滾動(dòng)邏輯設(shè)置
(四)處理觸摸事件分發(fā)

實(shí)現(xiàn)說明

本次彈幕場(chǎng)景,僅僅是設(shè)置一次數(shù)據(jù),若要進(jìn)行分頁加載,原理其實(shí)也是大同小異,稍作修改即可。

實(shí)現(xiàn)

(一)定義好一個(gè)setData方法,外部傳入的是一個(gè)bean集合,集合的變量自行定義,這里我
定義的變量如下:

    //類型:1內(nèi)容 2空布局填充
    private int type = 1;

    //昵稱
    private String nickName;

    //文字
    private String content;

因?yàn)樾枨笾?,需要顯示內(nèi)容前,有一段空白的滑動(dòng)距離,因此定義一個(gè)type==2的情況。

傳入代碼方法設(shè)置后,即可刷新adapter了,注意的是,每次刷新數(shù)據(jù),都
需要對(duì)定時(shí)器進(jìn)行cancel,否則會(huì)導(dǎo)致數(shù)據(jù)錯(cuò)亂的問題。
核心代碼如下:

    /**
     * 設(shè)置數(shù)據(jù)
     */
    public void setData(List<BulletInfo> bulletList) {
        if (handler == null) {
            return;
        }
        if (System.currentTimeMillis() - mSetDataInterval < 1000) {
            return;
        }
        mSetDataInterval = System.currentTimeMillis();
        clearHandler();
        handler.post(new Runnable() {
            @Override
            public void run() {
                //克隆數(shù)據(jù)
                List<BulletInfo> 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();
            }
        });
    }

(二)定義定時(shí)器
這里使用Timer和TimerTask實(shí)現(xiàn)。通過查看源碼發(fā)現(xiàn),recyclerview的滑動(dòng)方法,是支持子線程調(diào)用的,所以直接使用子線程執(zhí)行的timer進(jìn)行輪詢。
核心代碼如下:

    private void startHandler() {
        if (mTimer == null) {
            mTimer = new Timer();
        }
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    smoothScrollBy(0, 6);
                    //檢查是否滑動(dòng)到了底部
                    checkScrollToBottom();
                } catch (Exception e) {

                }
            }
        }, 10, 80);
    }

(三)觸摸距離計(jì)算
觸摸距離,本次實(shí)戰(zhàn)中,其實(shí)用到了就是判斷recyclerview是否滑動(dòng)到底部的判斷而已,沒什么大不了的。主要的也是調(diào)用官方的api,直接放出核心代碼:

    private void checkScrollToBottom() {
        //監(jiān)聽回調(diào)--------------------------------------------------
        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) {
                //已經(jīng)到底了
                if (mInfoAdapter != null && mInfoAdapter.getData() != null &&
                        mInfoAdapter.getData().size() != 0) {
                    //重新設(shè)置數(shù)據(jù)
                    List<BulletInfo> 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();
        }
    }

可以看到,主要方法有:

            int verticalExtent = computeVerticalScrollExtent();
            int verticalOffset = computeVerticalScrollOffset();
            int verticalTotal = computeVerticalScrollRange();

主要是計(jì)算recyclerview的滑動(dòng)距離,范圍,總距離,然后計(jì)算,即可得出滑動(dòng)百分比。

(四)處理觸摸事件
主要設(shè)置recyclerview不處理滑動(dòng)事件即可,核心代碼如下:


    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        return false;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return false;
    }

最后總結(jié)一下,若recyclerview在普通頁面使用,直接調(diào)用設(shè)置數(shù)據(jù)方法即可,若在recyclerview中的子布局中使用,要注意recyclerview的復(fù)用問題,建議每次使用前,都把數(shù)據(jù)清空一次。

代碼地址--庫libbullet

that's all--------------------------------------------------------------------

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容