纯粹依照自己的想法仿照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. 【Codeforces Round #423 (Div. 2) C】String Reconstruction

    [Link]:http://codeforces.com/contest/828/problem/C [Description] 让你猜一个字符串原来是什么; 你知道这个字符串的n个子串; 且知道第i ...

  2. 【Round #36 (Div. 2 only) C】Socks Pairs

    [题目链接]:https://csacademy.com/contest/round-36/task/socks-pairs/ [题意] 给你n种颜色的袜子,每种颜色颜色的袜子有ai只; 假设你在取袜 ...

  3. rsyslog学习

    http://blog.csdn.net/zhaoyangjian724/article/details/52116809 http://blog.csdn.net/zhangxihangzhuan/ ...

  4. 对spring默认的单列模式的理解

    我想大部分朋友对spring的单例模式都比較了解. 什么叫单例模式呢,顾名思义就是无论有多少个请求,都仅仅生成一个实例. 比方在spring中a,b请求都在调用同样的bean ,他们都是调用的同一个b ...

  5. iOS App 上架流程

                                                             iPhone App 上架流程 1.  申请 App ID (1)  连到 Devel ...

  6. Bootstrapbutton组

    button组同意多个button被堆叠在同一行上.当你想要把button对齐在一起时,这就显得很实用. 基本button组 给div加上class .btn-group <!DOCTYPE h ...

  7. acme.sh建立SAN证书 和泛域名证书

    文件来源 https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert domain=$domain time=`date +%Y%m%d% ...

  8. spring配置 quartz-config.xml

    <!-- 配置调度程序quartz ,其中配置JobDetail有两种方式--> <!-- 使用MethodInvokingJobDetailFactoryBean,任务类可以不实现 ...

  9. ES6特性-带标签的模板字符串(tagged template)

    tagged template: 加在模板字符串前面加一个标签(函数). let dessert = = '甜品' drink = '茶' let breakfast = kitchen`今天的早餐是 ...

  10. node.js状态码

    100——客户必须继续发出请求101——客户要求服务器根据请求转换HTTP协议版本200——交易成功201——提示知道新文件的URL202——接受和处理.但处理未完成203——返回信息不确定或不完整2 ...