天下之事常成于困约,而败于奢靡。这篇文章主要讲述android 弹幕评论效果相关的知识,希望能为你提供帮助。
纯粹依照自己的想法仿照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 弹幕评论效果】
推荐阅读
- 我的Android进阶之旅------&gt;Android实现用Android手机控制PC端的关机和重新启动的功能Androidclient功能展示
- android XMLPullParser读取xml文件
- 从设计到实现,一步步教你实现Android-Universal-ImageLoader-辅助类
- android抓取各种log的方法
- Android中常用的三种存储方法浅析
- android音乐播放器开发SweetMusicPlayer载入歌曲列表
- Android 提示( The connection to adb is down, and a severe error has occured.)
- C# 不重启程序修改并保存配置文件(appSettings节点)
- java枚举在android项目应用