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;
            }

        }
    }

时间: 2025-01-15 18:48:52

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

Android弹幕框架 黑暗火焰使

笑谈风云,一语定乾坤.大家好,我是皖江. 今天我将分享由BiliBili开源的Android弹幕框架(DanmakuFlameMaster)的学习经验. 我是将整个框架以model的形式引入项目中的,这样更方便的观察源码.也可以通过依赖的方式注入进来 dependencies { compile 'com.github.ctiao:DanmakuFlameMaster:0.5.3' } 先放一下我要做成的效果图: 页面分析 从上图来看,整个UI分成了三层.最下面是视频层,中间是弹幕层,顶层是控制

Android 导航条效果实现(六) TabLayout+ViewPager+Fragment

TabLayout 一.继承结构 public class TabLayout extends HorizontalScrollView java.lang.Object ? android.view.View ? android.view.ViewGroup ? android.widget.FrameLayout ? android.widget.HorizontalScrollView ? android.support.design.widget.TabLayout 二.TabLayou

Android自定义弹窗效果

Android的弹窗效果有很多种,就最简单而言,就可以调用一个AlertDialog弹窗显示,可是要自定义弹窗效果有以下这种方法,就我个人而言感觉挺方便的,适用性也挺广的. 首先先简单写个AlertDialog的使用 public void showDialog(){ AlertDialog dialog = new AlertDialog.Builder(this) .setTitle("提示") .setMessage(getResources().getString("

Android弹幕实现:基于B站弹幕开源系统(4)-重构

?? Android弹幕实现:基于B站弹幕开源系统(4)-重构 弹幕在视频播放的APP中比较常见,但是逻辑比较复杂,现在在附录1,2,3的基础上,我再次对弹幕进行抽象和重构,把弹幕从底向上抽象成不同的层,便于复用. 第一步,抽象数据层.通常弹幕的来源是来源于后台的数据接口请求,在实时直播时候,是通过网络的轮询机制获取数据,那么,我把这部分代码抽出来设计成一个MGDanmakuHttpController,该类专注于数据的获取与分发: package zhangphil.danmaku; impo

Android弹幕实现:基于B站弹幕开源系统(1)

?? Android弹幕实现:基于B站弹幕开源系统(1) 如今的视频播放,流行在视频上飘弹幕.这里面做的相对比较成熟.稳定.使用量较多的弹幕系统,当推B站的弹幕系统,B站的弹幕系统已经作为开源项目在github上,其项目地址:https://github.com/Bilibili/DanmakuFlameMaster 以B站开源的弹幕项目为基础,现给出一个简单的例子,实现发送简单的文本弹幕.第一步,首先要在Android的build.gradle文件中引入B站的项目: repositories

实现Android半透明Menu效果的开发实例

不知道大家是否用过天天动听,对于它界面上的半透明Menu效果,笔者感觉非常漂亮.下面是天天动听半透明Menu的截图,欣赏下吧: 感觉还不错吧?那么如何实现这种半透明Menu效果呢?本文就重点讨论并给出这种Menu的具体代码实现过程. 首先分析下实现这种半透明Menu所需做的工作,并进行合理分解: 1.  利用Shaper设置一个半透明圆角背景. 2.  定义Menu布局,主要就GridView,把图标都放在这个GridView. 3.  Menu事件, 通过PopupWindow或者AlertD

每周荐书:机器学习、Spring MVC、Android(评论送书)

每周荐书:机器学习.Spring MVC.Android(评论送书) 先来公布一下上期中奖用户吧,恭喜下面三位用户! 杜瑞祺 <网络爬虫全解析--技术.原理与实践> 他叫自己MR张<视觉SLAM十四讲:从理论到实践> 资深架构师 <Vue2实践揭秘> 中奖通知由CSDN官方发布站内消息,请关注消息通知~ 移动互联网.大数据.人工智能等新技术浪潮在不断冲击着我们,为了拓展我们的学习路线,小编每周为大家推荐几本IT技术新书,以弥补扑面而来的各类技术热点知识. 在文末评论里回

Android弹幕实现:基于B站弹幕开源系统(2)

?? Android弹幕实现:基于B站弹幕开源系统(2) 在附录1的基础上,模拟实现一种实际开发的应用场景:从网络中不间断的周期取弹幕数据,这些弹幕数据往往是批量的,然后把这些从网络中取到的批量数据逐个的显示出来.注意本例中的Handler和线程安全队列ConcurrentLinkedQueue的使用.Java代码: package zhangphil.danmaku; import android.app.Activity; import android.graphics.Color; imp

【Android】时光轴效果

[Android]时光轴效果 支持平台:Android   运行环境:Eclipse   开发语言:Java 下载地址:http://t.cn/R71Ge5c 源码简介 利用ExpandableListView通过对布局的控制来实现时光轴效果,只要有点耐心,你,也可以~ 源码运行图