纯粹依照自己的想法仿照b站的弹幕写的一个demo,不知道正确的姿势怎么样的。

demo下载地址

首先。一条弹幕就是一个textview

public abstract class Danmu extends TextView{
private Context context;
private int position;//弹幕的位置,在屏幕哪一行 public Danmu(Context context) {
super(context);
this.context=context;
setSingleLine();
} public int getPosition() {
return position;
} public void setPosition(int position) {
this.position = position;
} public abstract void send(); }

将弹幕放在一个相对布局容器中

 <RelativeLayout
android:id="@+id/danmuContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3" />

当字数非常多时,会放不下全部文字,所以手动设置了容器的宽度

容器设置足够大就好

ViewGroup.LayoutParams lp=container.getLayoutParams();
lp.width=DensityUtils.sp2px(this,15)*100;
container.setLayoutParams(lp);

弹幕分为好几种这里做了普通的从右到左的。逆向的。还有在顶部和底部的

普通弹幕由两个TranslateAnimation完毕,第一个是当弹幕移动后空出足够多空间时通知其它弹幕能够跟在它后面,第二个动画完毕接下来的移出屏幕

public class NormalDanmu extends Danmu {
private Animation animation0,animation1;
private int fx0,tx0,fx1,tx1;
private int duration0,duration1;
private OnAnimationEndListener onAnimationEndListener; public interface OnAnimationEndListener
{
public void clearPosition();//第一个动画结束,将当前行设置为能够发送弹幕
public void animationEnd();//弹幕全然移出屏幕
} public NormalDanmu(Context context,int fx,int tx)
{
super(context);
this.fx0=fx;
this.tx0=Math.abs(fx)-Math.abs(tx)-100;//第一个动画结束位置,当尾部空出100像素时就能够通知其它弹幕跟上了
this.fx1=tx0;
this.tx1=tx; duration0=2000*(Math.abs(tx0-fx0))/DensityUtils.getScreenW(context);
duration1=2000*(Math.abs(tx1-fx1))/DensityUtils.getScreenW(context); initAnimation();
} private void initAnimation()
{
animation0=new TranslateAnimation(fx0,tx0,0,0);
animation1=new TranslateAnimation(fx1,tx1,0,0);
animation0.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) { } @Override
public void onAnimationEnd(Animation animation) { clearAnimation();
startAnimation(animation1);
if (onAnimationEndListener!=null)
{
onAnimationEndListener.clearPosition();
}
} @Override
public void onAnimationRepeat(Animation animation) { }
}); animation0.setFillAfter(true);
animation0.setDuration(duration0);
animation0.setInterpolator(new AccelerateInterpolator()); animation1.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) { } @Override
public void onAnimationEnd(Animation animation) { if(onAnimationEndListener!=null)
{
onAnimationEndListener.animationEnd();
} } @Override
public void onAnimationRepeat(Animation animation) { }
}); animation1.setFillAfter(true);
animation1.setDuration(duration1);
animation1.setInterpolator(new DecelerateInterpolator());
} public void setOnAnimationEndListener(OnAnimationEndListener onAnimationEndListener)
{
this.onAnimationEndListener=onAnimationEndListener;
} @Override
public void send() {
startAnimation(animation0);
}
}

然后发送弹幕 final NormalDanmu danmu=new NormalDanmu(this,sWidth,(int) -paint.measureText(str));

swidth表示屏幕宽度。paint.measureText(str)是textview宽度,表示从最右端移动到左边全然移出屏幕

lp.addRule(RelativeLayout.ALIGN_PARENT_TOP);

lp.topMargin=i*danmuHeight;

danmuHeight是一个textview的高度,这里设置放在容器的第i行

private void setDanmu()
{
String ss="按是按时按是android.os.BinderProx按是";
int ll=ss.length()*DensityUtils.sp2px(this,15);
int ran= new Random().nextInt(ss.length());
String str=ss.substring(ran);
final NormalDanmu danmu=new NormalDanmu(this,sWidth,(int) -paint.measureText(str));
danmu.setTextSize(15);
danmu.setText(str);
danmu.setOnAnimationEndListener(new NormalDanmu.OnAnimationEndListener() {
@Override
public void clearPosition() { sendPosition.put(danmu.getPosition(), false);
} @Override
public void animationEnd() { container.removeView(danmu);
} }); for(int i=0;i<count;i++)
{
if(sendPosition.get(i)==false)
{
danmu.setPosition(i);
RelativeLayout.LayoutParams lp=new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, danmuHeight);
lp.addRule(RelativeLayout.ALIGN_PARENT_TOP);
lp.topMargin=i*danmuHeight;
danmu.setGravity(Gravity.CENTER);
container.addView(danmu, lp); danmu.send(); sendPosition.put(i,true);
break;
} }
}



逆向弹幕就是和普通弹幕移动方向不同其它全然一样



顶部和底部的弹幕主要就是显示几秒后再消失即可了比較简单

public class TopDanmu extends Danmu {
private OnDisappearListener onDisappearListener;
private int duration;
private Handler handler=new Handler()
{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what==1)
{
if(onDisappearListener!=null)
{
onDisappearListener.disappear();
}
}
}
}; public TopDanmu(Context context,int duration) {
super(context);
this.duration=duration;
} public interface OnDisappearListener
{
public void disappear();
}
@Override
public void send() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(duration);
handler.sendEmptyMessage(1); } catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
} public void setOnDisappearListener(OnDisappearListener onDisappearListener )
{
this.onDisappearListener=onDisappearListener;
}
}

发送顶部弹幕

顶部弹幕要水平居中,这里的容器设置的宽度超过的屏幕大小。所以要手动计算弹幕的水平位置

int margin= (int) ((sWidth-paint.measureText(danmu.getText().toString()))/2);



private void setTopDanmu()
{
String ss="按是按时按是android.os.BinderProx按是"; int ran= new Random().nextInt(ss.length());
String str=ss.substring(ran);
int ll=str.length()*DensityUtils.sp2px(this, 15);
final TopDanmu danmu=new TopDanmu(this,2000);
danmu.setTextSize(15);
danmu.setText(str);
danmu.setTextColor(Color.GREEN);
danmu.setOnDisappearListener(new TopDanmu.OnDisappearListener() {
@Override
public void disappear() {
container.removeView(danmu);
topSendPosition.put(danmu.getPosition(), false);
}
}); for(int i=0;i<count;i++)
{
if(topSendPosition.get(i)==false)
{
danmu.setPosition(i);
RelativeLayout.LayoutParams lp=new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, danmuHeight);
lp.addRule(RelativeLayout.ALIGN_PARENT_TOP);
int margin= (int) ((sWidth-paint.measureText(danmu.getText().toString()))/2);
lp.topMargin=i*danmuHeight;
lp.leftMargin=margin;
danmu.setGravity(Gravity.CENTER);
container.addView(danmu, lp); danmu.send(); topSendPosition.put(i,true);
break;
} }
}









android 弹幕评论效果的更多相关文章

  1. Android弹幕编程设计实现的解决方案(一)

     Android弹幕编程设计实现的解决方案(一) 在现在的一些视频类网站.视频类直播网站,比如A站和B站,当视频在播放的时候,会在屏幕上出现一些滚动的字幕,这些字幕是UGC,通常是用户的评论,称之 ...

  2. 【Android源代码下载】收集整理android界面UI效果源码

    在Android开发中,Android界面UI效果设计一直都是很多童鞋关注的问题,今天给大家分享下大神收集整理的多个android界面UI效果,都是源码,都是干货,贡献给各位网友! 话不多说,直接上效 ...

  3. Android ViewPager 动画效果

    找到个不错的开源项目:https://github.com/jfeinstein10/JazzyViewPager Android ViewPager 动画效果   

  4. android 弹幕效果demo

    记得之前有位朋友在我的公众号里问过我,像直播的那种弹幕功能该如何实现?如今直播行业确实是非常火爆啊,大大小小的公司都要涉足一下直播的领域,用斗鱼的话来讲,现在就是千播之战.而弹幕则无疑是直播功能当中最 ...

  5. Android弹幕功能实现,模仿斗鱼直播的弹幕效果

    转载出处:http://blog.csdn.net/sinyu890807/article/details/51933728 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即 ...

  6. Android 弹幕效果开发案例

    概述 现在有个很流行的效果就是弹幕效果,满屏幕的文字从右到左飘来飘去.看的眼花缭乱,看起来还蛮cool的 现在就是来实现这一的一个效果,大部分的都是从右向左移动漂移,本文的效果中也支持从左向右的漂移移 ...

  7. Android ListView各种效果实现总结,持续更新...

    一.ListView圆角:重写ListView的onInterceptTouchEvent方法,通过pointToPosition(x,y)方法判断当前点击位置所对应的项,有三种情况:分别是第一项.最 ...

  8. Android 遮罩层效果

    (用别人的代码进行分析) 不知道在开发中有没有经常使用到这种效果,所谓的遮罩层就是给一张图片不是我们想要的形状,这个时候我们就可以使用遮罩效果把这个图片变成我们想要的形状,一般使用最多就是圆形的效果, ...

  9. Android登录等待效果

    上一篇为大家分享了关于AsyncTask的使用,本篇结合AsyncTask为大家介绍一个我们经常看到的一个效果,就是当我们点击登录后,会弹出一个请等待的小窗体,这个效果是如何实现的呢?本篇我就带大家简 ...

随机推荐

  1. 转载-- Qt Creator编译时make: arm-linux-g++: command not found 错误!

    前提是已经配置好交叉编译器,但是qt creator找不到. 解决方法: 修改 /usr/local/Trolltech/QtEmbedded-4.7.0-arm/mkspecs/qws/linux- ...

  2. HTML学习----------DAY2第四节

    HTML 文档是由 HTML 元素定义的. HTML 元素 HTML 元素指的是从开始标签(start tag)到结束标签(end tag)的所有代码. 注释:开始标签常被称为开放标签(opening ...

  3. ArcGIS api for javascript——加载图标

    描述 这个示例展示了如何能用一个动画图片显示地图正在加载.在这个示例中,图片是一个小的动画GIF.当地图第一次加载或用户缩放和平移地图时显示图片.当所有图层加载完成图片消失. 这个示例是通过event ...

  4. KNN分类器

    KNN学习(K-Nearest Neighbor algorithm,K最邻近方法 )是一种统计分类器,对数据的特征变量的筛选尤其有效. 基本原理 KNN的基本思想是:输入没有标签(标注数据的类别), ...

  5. [Recompose] Refactor React Render Props to Streaming Props with RxJS and Recompose

    This lesson takes the concept of render props and migrates it over to streaming props by keeping the ...

  6. 基于BP神经网络的简单字符识别算法自小结(C语言版)

    本文均属自己阅读源代码的点滴总结.转账请注明出处谢谢. 欢迎和大家交流.qq:1037701636 email:gzzaigcn2009@163.com 写在前面的闲话: 自我感觉自己应该不是一个非常 ...

  7. BZOJ 1193 搜索+贪心

    预处理出100*100以内的最优解 贪心走日 判断是0*4还是2*4 搞定 //By SiriusRen #include <queue> #include <cstdio> ...

  8. OpenCV中数据转换

    在OpenCV中Mat.CvMat和IplImage类型都可以代表和显示图像.IplImage由CvMat派生,而CvMat由CvArr派生即CvArr -> CvMat -> IplIm ...

  9. try/catch的用法

    1.try/catch用法基础介绍 try { //程序中抛出异常 throw value; } catch(valuetype v) { //例外处理程序段 } 语法小结:throw抛出值,catc ...

  10. Oracle 删除重复数据的几种方法

    去重 第一种:distinct create table tmp_t3 as select distinct * from t3; drop table t3; alter table tmp_t2 ...