Android弹幕编程设计实现的解决方案(一)

来源:互联网 发布:windows pe权威指南 编辑:程序博客网 时间:2024/06/05 06:35


Android弹幕编程设计实现的解决方案(一)

在现在的一些视频类网站、视频类直播网站,比如A站和B站,当视频在播放的时候,会在屏幕上出现一些滚动的字幕,这些字幕是UGC,通常是用户的评论,称之为“弹幕”,这些弹幕一般从右往左滚动,以符合人类的阅读习惯。
现在给出一个实现Android平台上的弹幕编程设计实现方案。
(1)要注意的是,一般视频播放是一个view,比如是VideoView,弹幕是在VideoView上面滚动,这就要求弹幕必须和VideoView完全密合在一起,至少在视觉上让用户看上去觉得这就是直接在视频画面上写上去的。那么在下面的这个例子中,就干脆直接继承自一个相对布局RelativeLayout。因为是一个布局文件,如果使用VideoView这类的播放器,就好办了,直接设置view的layout宽和高均为match_parent即可。
(2)弹幕一般是随机批量显示。我观察了一些热门网站的弹幕显示,它们是随机的,我在处理弹幕字幕文本时候,设计了一个set方法,一次性批量喂给弹幕一个数据池子(实际上就是在弹幕类里面的一个LinkedList数据队列texts),让弹幕从这些池子中每次随机抽出一个显示。假设上层应用的文本有更新(比如用户有新的评论添加进来),直接追加到队列头部。

以下是全部源代码实现。
核心关键的弹幕相对布局BarrageRelativeLayout.java:

package zhangphil.demo;import android.content.Context;import android.graphics.Color;import android.graphics.Rect;import android.os.Handler;import android.os.Message;import android.text.TextPaint;import android.util.AttributeSet;import android.view.animation.AccelerateDecelerateInterpolator;import android.view.animation.Animation;import android.view.animation.TranslateAnimation;import android.widget.RelativeLayout;import android.widget.TextView;import java.util.LinkedList;import java.util.Random;public class BarrageRelativeLayout extends RelativeLayout {    private Context mContext;    private Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            if(msg.what==RANDOM_SHOW) {                String text = texts.get(random.nextInt(texts.size()));                BarrageTextItem item = new BarrageTextItem(text);                showBarrageItem(item);                //每个弹幕产生的间隔时间随机                int duration = (int) ((BARRAGE_GAP_MAX_DURATION - BARRAGE_GAP_MIN_DURATION) * Math.random());                this.sendEmptyMessageDelayed(RANDOM_SHOW, duration);            }        }    };    public static int RANDOM_SHOW=0x0a1;    public static int SEQ_SHOW=0x0a2;    private Random random = new Random(System.currentTimeMillis());    private static final long BARRAGE_GAP_MIN_DURATION = 1000;//两个弹幕的最小间隔时间    private static final long BARRAGE_GAP_MAX_DURATION = 2000;//两个弹幕的最大间隔时间    private int maxSpeed = 10000;//速度,ms    private int minSpeed = 5000;//速度,ms    private int maxSize = 30;//文字大小,dp    private int minSize = 15;//文字大小,dp    private int totalHeight = 0;    private int lineHeight = 0;//每一行弹幕的高度    private int totalLine = 0;//弹幕的行数    private LinkedList<String> texts = null;    public BarrageRelativeLayout(Context context) {        this(context, null);    }    public BarrageRelativeLayout(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public BarrageRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        mContext = context;        init();    }    private void init() {        texts = new LinkedList<String>();    }    public void show(int type){        int duration = (int) ((BARRAGE_GAP_MAX_DURATION - BARRAGE_GAP_MIN_DURATION) * Math.random());        mHandler.sendEmptyMessageDelayed(type, duration);    }    //显示一批弹幕文本    //相当于给弹幕设置数据源    public void setBarrageTexts(LinkedList<String> texts) {        this.texts = texts;    }    //头部第一个位置追加,最新的。    public void addBarrageText(String text) {        this.texts.add(0,text);    }    @Override    public void onWindowFocusChanged(boolean hasWindowFocus) {        super.onWindowFocusChanged(hasWindowFocus);        totalHeight = getMeasuredHeight();        lineHeight = getLineHeight();        totalLine = totalHeight / lineHeight;    }    private void showBarrageItem(final BarrageTextItem item) {        int leftMargin = this.getRight() - this.getLeft() - this.getPaddingLeft();        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);        params.addRule(RelativeLayout.ALIGN_PARENT_TOP);        params.topMargin = item.verticalPos;        this.addView(item.textView, params);        Animation anim = generateTranslateAnim(item, leftMargin);        anim.setAnimationListener(new Animation.AnimationListener() {            @Override            public void onAnimationStart(Animation animation) {            }            @Override            public void onAnimationEnd(Animation animation) {                item.textView.clearAnimation();                BarrageRelativeLayout.this.removeView(item.textView);            }            @Override            public void onAnimationRepeat(Animation animation) {            }        });        item.textView.startAnimation(anim);    }    private TranslateAnimation generateTranslateAnim(BarrageTextItem item, int leftMargin) {        TranslateAnimation anim = new TranslateAnimation(leftMargin, -item.textMeasuredWidth, 0, 0);        anim.setDuration(item.moveSpeed);        anim.setInterpolator(new AccelerateDecelerateInterpolator());        anim.setFillAfter(true);        return anim;    }    /**     * 计算TextView中字符串的长度     *     * @param text 要计算的字符串     * @param Size 字体大小     * @return TextView中字符串的长度     */    public float getTextWidth(BarrageTextItem item, String text, float Size) {        Rect bounds = new Rect();        TextPaint paint;        paint = item.textView.getPaint();        paint.getTextBounds(text, 0, text.length(), bounds);        return bounds.width();    }    /**     * 获得每一行弹幕的最大高度     *     * @return     */    private int getLineHeight() {        BarrageTextItem item = new BarrageTextItem();        //传递进去一个非空字符串,目的是为了获得一个计算值        String tx = "no null data";        item.textView = new TextView(mContext);        item.textView.setText(tx);        item.textView.setTextSize(maxSize);        Rect bounds = new Rect();        TextPaint paint;        paint = item.textView.getPaint();        paint.getTextBounds(tx, 0, tx.length(), bounds);        return bounds.height();    }    //弹幕的一个文本item    public class BarrageTextItem {        public TextView textView;        public int textColor;        public String text;        public int textSize;        public int moveSpeed;//移动速度        public int verticalPos;//垂直方向显示的位置        public int textMeasuredWidth;//字体显示占据的宽度        public BarrageTextItem() {        }        public BarrageTextItem(String text) {            this.textSize = (int) (minSize + (maxSize - minSize) * Math.random());            this.text = text;            this.textColor = Color.rgb(random.nextInt(256), random.nextInt(256), random.nextInt(256));            this.textView = new TextView(mContext);            textView.setText(text);            textView.setTextSize(textSize);            textView.setTextColor(textColor);            textMeasuredWidth = (int) getTextWidth(this, text, textSize);            moveSpeed = (int) (minSpeed + (maxSpeed - minSpeed) * Math.random());            if (totalLine == 0) {                totalHeight = getMeasuredHeight();                lineHeight = getLineHeight();                totalLine = totalHeight / lineHeight;            }            verticalPos = random.nextInt(totalLine) * lineHeight;        }    }}


直接写进布局当作一个一般的相对布局使用:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="zhangphil.demo.MainActivity">    <zhangphil.demo.BarrageRelativeLayout        android:id="@+id/barrageView"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:background="@android:color/white" /></RelativeLayout>


上层activity使用方法:

package zhangphil.demo;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import java.util.LinkedList;public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        BarrageRelativeLayout mBarrageRelativeLayout = (BarrageRelativeLayout) findViewById(R.id.barrageView);        String[] itemText = {"zhangphil@csdn 0", "zhangphil 1", "zhang phil 2","zhang 3","phil 4","zhangphil ... 5", "***zhangphil 6", "zhang phil csdn 7", "zhang ... phil 8", "phil... 9", "http://blog.csdn.net/zhangphil 10"};        LinkedList<String> texts=new LinkedList<String>();        for(int i=0;i<itemText.length;i++){            texts.add(itemText[i]);        }        mBarrageRelativeLayout.setBarrageTexts(texts);        mBarrageRelativeLayout.show(BarrageRelativeLayout.RANDOM_SHOW);    }}


代码运行结果(弹幕从右往左滚动显示):



1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 1岁宝宝胃胀气怎么办 孩子胃胀气还吐怎么办 肚子里进了凉气怎么办 肠胃涨气肚子变大怎么办 感觉肚子胀胀的怎么办 肚子胀撑的难受怎么办 肚子着凉了很疼怎么办 来月经肚子疼怎么办最快的方法 孕妇上大便有血怎么办 做完爱小腹坠痛怎么办 月经不来肚子胀怎么办 月经期间肚子疼的厉害怎么办 大姨吗来了肚子疼该怎么办 孕妇7个月拉肚子怎么办 胃疼肚子也疼怎么办 6个月孕妇肚子疼怎么办 孕妇4个月肚子疼怎么办 孕妇5个月拉肚子怎么办 4个月孕妇拉肚子怎么办 怀孕5个月拉肚子怎么办 肠胃老是胀气很不舒服怎么办 肚子里有气排不出来怎么办 小兔子不吃兔粮怎么办 泰迪肚子一直叫怎么办 狗狗肚子响该怎么办 狗狗肚子一直响怎么办 一刮风空调就响怎么办 胃里有气往上顶怎么办 胃里难受想吐怎么办 胃里感觉有水怎么办 喉咙总有气堵着怎么办 胃难受恶心想吐怎么办 胃里有气怎么办总放屁 肚子里有气很痛怎么办 胃有气排不出来怎么办 狗狗又吐又拉稀怎么办 狗吐了又拉稀怎么办 金毛狗又拉又吐怎么办 狗狗拉稀像水怎么办 狗狗拉稀带血怎么办 小狗狗又吐又拉怎么办