安卓Android弹幕效果实现-简单易懂

安卓Android弹幕功能实现-使用SurfaceView

先来一张效果图

安卓Android弹幕效果实现-简单易懂

安卓Android弹幕实现-思路

每一条弹幕其实都是一个文本,用drawText即可绘制,我们可以建立一个集合用来存储弹幕,动态改变弹幕的X轴,即可移动弹幕,当弹幕移出屏幕时,再进行销毁操作。

下面我们一步一步来实现弹幕效果:

第一步:新建mSurfaceView类,继承SurfaceView,并实现SurfaceHolder.Callback以及Runnable接口

 

public class mSurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable{


    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {

    }


    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        Flag=false;

    } 
    
    @Override 
    public void run() { }
}

第二步:定义如下成员变量,并实现构造方法:

//用于标注线程是否继续
private boolean Flag=true;

//SurfaceHolder
SurfaceHolder surfaceHolder;

//弹幕的集合
public List<mText> Barrages=new ArrayList<>();

//用于随机生成弹幕的Y轴坐标
Random random=new Random();

public mSurfaceView(Context context) {
    super(context);
}

public mSurfaceView(Context context, AttributeSet attrs) {
    super(context, attrs);

    surfaceHolder=getHolder();
    surfaceHolder.addCallback(this);

    //设置背景透明
    this.setZOrderOnTop(true);
    surfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
}


public mSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

前面说过,每一条弹幕都需要存入集合,绘制的时候再遍历集合取出里面的弹幕进行绘制,那下面第三步:添加弹幕类


public class mText {
    //文字
    private String text;
    //文字大小
    private float size;
    //文字颜色
    private Integer color;
    //文字X轴
    private float x;
    //文字Y轴
    private float y;
    //文字移动速度
    private int speed;
    //画笔
    private Paint paint;
    
    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public float getSize() {
        return size;
    }

    public void setSize(float size) {
        this.size = size;
    }

    public Integer getColor() {
        return color;
    }

    public void setColor(Integer color) {
        this.color = color;
    }

    public float getX() {
        return x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    public Paint getPaint() {
        return paint;
    }

    public void setPaint(Paint paint) {
        this.paint = paint;
    }
    

}

疑问:

其实大家可能注意到,上方弹幕类中,定义有文字内容、文字颜色、文字大小等成员变量,既然有颜色、大小等,那我们为什么还有定义画笔呢?

其实画笔可以传一个null,考虑到有时候仅靠颜色和大小可能无法完全满足我们的需求,所以我们可以定义一个画笔,更方便扩展,如果传过来了画笔,则直接用画笔进行绘制,如果画笔为null,则用默认的画笔,通过类中的颜色、大小等进行绘制。

既然已经定义弹幕类,那肯定也需要添加弹幕到弹幕集合的方法,第四步:定义add方法:

//添加弹幕
public void add(mText mText) {
    if (mText.getX()==0){
        mText.setX(getWidth());
    }
    if (mText.getY()==0){
        int i=getHeight();
        if (i>0){
            mText.setY(random.nextInt(i));
        }

    }
    Barrages.add(mText);
}

  大家看到,上面有对x和y轴参数的非空判断,这样做的目的是当我们在调用add方法时,可以不给mText类的x、y赋值,节省我们的时间,如果检测到y轴为0,就随机指定一个y轴,如果x轴为0,则默认从屏幕的最右侧开始。


截止目前,准备工作已经做完,下面我们开始绘制:

@Override
public void run() {
    Canvas canvas=null;
    mText mtext = null;
    Paint paint=null;
    while (Flag){
        //如果集合为0,则跳过本次循环
        if (Barrages.size()==0){
            continue;
        }
        try {
            //获取画布
            canvas=surfaceHolder.lockCanvas();
            //清空画布
            canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        }catch (Exception e){
            e.printStackTrace();
            break;
        }


        //遍历弹幕集合
        for (int i = 0; i < Barrages.size(); i++) {
            mtext=Barrages.get(i);
            //如果弹幕类中的画笔为空,则在此处定义画笔,根据弹幕类中的颜色大小等进行绘制
            if (mtext.getPaint()==null){
                if (paint==null){
                    paint=new Paint();
                }
                paint.setColor(mtext.getColor());
                paint.setTextSize(mtext.getSize());
                paint.setStrokeWidth(3f);
            }else {
                //如果弹幕类中的画笔不为空,则直接用弹幕类中的画笔绘制
                paint=mtext.getPaint();
            }
            //绘制文本
            canvas.drawText(mtext.getText(),mtext.getX(),mtext.getY(),paint);
            //如果弹幕超出屏幕左侧,则从集合中删除,否则进行移动
            if (mtext.getX()<-getWidth()){
                Barrages.remove(mtext);
            }else {
                mtext.setX((mtext.getX()-mtext.getSpeed()));
            }
        }

        //解锁画布
        surfaceHolder.unlockCanvasAndPost(canvas);
        
        try {
            Thread.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

大功告成,我们来看看如何调用,只需要执行msurfaceview.add方法添加弹幕即可。

public class MainActivity extends Activity {

    private mSurfaceView msurfaceView;
    private VideoView videoView;
    private Random random=new Random();

    private String[] strings={"6666","厉害了我的国","加油!!!","欢迎收看晨间新闻","程序猿很苦逼","我能怎么办,我也很无奈"};

    private int[] colors={Color.WHITE,Color.MAGENTA,Color.CYAN,Color.RED,Color.BLUE,Color.GREEN};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        init();
    }

    private void init() {
        msurfaceView = findViewById(R.id.msv);
        videoView = findViewById(R.id.mvv);

        //申请播放网络视频权限
        per();

        startVideo();

        //动态生成弹幕
        startBarrage();
    }

    //生成弹幕
    private void startBarrage() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    mText mText=new mText();
                    mText.setText(strings[random.nextInt(strings.length)]);
                    mText.setSpeed(3);
                    mText.setColor(colors[random.nextInt(colors.length)]);
                    mText.setSize(40);
                    msurfaceView.add(mText);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }).start();
    }

    //申请视频播放权限
    private void per() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {//检查是否有了权限
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
            } else {
                //没有权限即动态申请
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
            }
        }

        }

    //开始播放视频
    private void startVideo() {
            String url1 = "http://flashmedia.eastday.com/newdate/news/2016-11/shznews1125-19.mp4";

            Uri uri=Uri.parse(url1);

            //设置视频控制器
            videoView.setMediaController(new MediaController(this));


            //设置视频路径
            videoView.setVideoURI(uri);

            //开始播放视频
            videoView.start();

        }


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <VideoView
        android:id="@+id/mvv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

    <barrage.surfaceview.com.surfaceviewbarragedemo.mSurfaceView
        android:id="@+id/msv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

github完整代码地址:安卓Android弹幕效果实现
PS:如有不正确的地方欢迎指出!

   转载请注明:https://blog.csdn.net/weixin_41549915/article/details/80306476