纯粹按照自己的想法仿照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();
}
容器设置足够大就好
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
顶部和底部的弹幕主要就是显示几秒后再消失就行了比较简单
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