纯粹依照自己的想法仿照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. Java基础学习总结(12)——一哈希编码HashCode

    一.哈希编码 现在是站在JAVA虚拟机的角度来看内存里面的布局,站在JAVA虚拟机的角度,在内存里面有好多好多个对象,这里用椭圆来代表一个个对象.一个程序运行起来的时候,可能会有很多个对象在内存里面分 ...

  2. 洛谷 P2104 二进制

    P2104 二进制 题目描述 小Z最近学会了二进制数,他觉得太小的二进制数太没意思,于是他想对一个巨大二进制数做以下 4 种基础运算: 运算 1:将整个二进制数加 1 运算 2:将整个二进制数减 1 ...

  3. 洛谷 P1747 好奇怪的游戏

    P1747 好奇怪的游戏 题目背景 <爱与愁的故事第三弹·shopping>娱乐章. 调调口味来道水题. 题目描述 爱与愁大神坐在公交车上无聊,于是玩起了手机.一款奇怪的游戏进入了爱与愁大 ...

  4. NYOJ 541 最强的战斗力

    最强DE 战斗力 时间限制:1000 ms  |  内存限制:65535 KB 难度: 描写叙述 春秋战国时期,赵国地大物博,资源很丰富.人民安居乐业.但很多国家对它虎视眈眈.准备联合起来对赵国发起一 ...

  5. 习题(3-3) 计算(a+b)*c的值

    题目 - 习题(3-3) 计算(a+b)*c的值   来源 计算概论B 2010 描写叙述 计算表达式(a+b)*c的值,当中a, b, c均为整数,且a,b,c的值介于-10000和10000之间( ...

  6. 离散化求RECT1

    本文转载至点击打开链接 #include<stdio.h> struct node{ int x1,y1,x2,y2,c; }; struct node s[1010]; int px[2 ...

  7. Effective C++ Item 30 inline里里外外

    本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie Item 44 46 1.将大多数 inlining 限制在小型.被频繁调用的函数身上.这可 ...

  8. 15:Challenge 11(主席树裸题)

    总时间限制:  10000ms 单个测试点时间限制:  1000ms 内存限制:  262144kB 描述 给一个长为N的数列,有M次操作,每次操作是以下两种之一: (1)修改数列中的一个数 (2)求 ...

  9. Sql Server通用分页存储过程

    Sql Server2005通用分页存储过程 CREATE PROCEDURE [dbo].[Common_GetPagedList] ( @TableName nvarchar(100), --表名 ...

  10. vue.js路由vue-router(一)——简单路由基础

    前言 vue.js除了拥有组件开发体系之外,还有自己的路由vue-router.在没有使用路由之前,我们页面的跳转要么是后台进行管控,要么是用a标签写链接.使用vue-router后,我们可以自己定义 ...