?。?!代碼在文末!??!
背景
對(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ù)清空一次。
that's all--------------------------------------------------------------------