!!!代码在文末!!!
背景
对于弹幕功能,稍微有点社交内容的app都会用到,其中,实现的方法有很多,本文主要通过
安卓原生recyclerview进行实现
思路
首先,recyclerview提供了api给开发者进行滚动的调用,同时也提供了相关接口,对滚动的
距离进行计算。因此,通过滑动距离监听,以及定时任务进行内容滚动,即可实现。
实现细节
对于上述思路以外,还要注意的是,recyclerview的滑动事件分发问题。本次需求要求的是
recyclerview不能与用户产生触摸交互,因此需要特别处理。
同时,需要注意对象引用问题。
实现过程
(一)定义好外部传入数据的方法
(二)定义一个定时器,用作定时滑动
(三)触摸距离计算,重新滚动逻辑设置
(四)处理触摸事件分发
实现说明
本次弹幕场景,仅仅是设置一次数据,若要进行分页加载,原理其实也是大同小异,稍作修改即可。
实现
(一)定义好一个setData方法,外部传入的是一个bean集合,集合的变量自行定义,这里我
定义的变量如下:
//类型:1内容 2空布局填充
private int type = 1;
//昵称
private String nickName;
//文字
private String content;
因为需求中,需要显示内容前,有一段空白的滑动距离,因此定义一个type==2的情况。
传入代码方法设置后,即可刷新adapter了,注意的是,每次刷新数据,都
需要对定时器进行cancel,否则会导致数据错乱的问题。
核心代码如下:
/**
* 设置数据
*/
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() {
//克隆数据
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();
}
});
}
(二)定义定时器
这里使用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);
}
(三)触摸距离计算
触摸距离,本次实战中,其实用到了就是判断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<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();
主要是计算recyclerview的滑动距离,范围,总距离,然后计算,即可得出滑动百分比。
(四)处理触摸事件
主要设置recyclerview不处理滑动事件即可,核心代码如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
return false;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return false;
}
最后总结一下,若recyclerview在普通页面使用,直接调用设置数据方法即可,若在recyclerview中的子布局中使用,要注意recyclerview的复用问题,建议每次使用前,都把数据清空一次。
that's all--------------------------------------------------------------------