2021-04-02

自定义视图视频加弹幕
视频布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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:orientation="vertical"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="300dp">
        <com.example.day11_surfaceview.view.YaoVideoView
            android:id="@+id/yao"
            android:layout_width="match_parent"
            android:layout_height="match_parent"></com.example.day11_surfaceview.view.YaoVideoView>
        <LinearLayout
            android:layout_alignParentBottom="true"
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:textSize="17sp"
                android:textColor="#fff"
                android:id="@+id/tv_start"
                android:text="   播放/暂停   "
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"></TextView>

            <TextView
                android:textSize="17sp"
                android:textColor="#fff"
                android:id="@+id/tv_quick"
                android:text="   快进  "
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"></TextView>
            <TextView
                android:textSize="17sp"
                android:textColor="#fff"
                android:id="@+id/tv_tui"
                android:text="   快退   "
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"></TextView>
            <TextView
                android:textSize="17sp"
                android:textColor="#fff"
                android:id="@+id/tv_bei"
                android:text="   倍速   "
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"></TextView>
            <TextView
                android:textSize="17sp"
                android:textColor="#fff"
                android:id="@+id/tv_full"
                android:text="   全屏   "
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"></TextView>
        </LinearLayout>


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:textSize="17sp"
                android:textColor="#fff"
                android:text="00:00"
                android:id="@+id/tv_current"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="wrap_content"></TextView>
            <SeekBar
                android:id="@+id/bar"
                android:layout_width="0dp"
                android:layout_weight="7"
                android:layout_height="wrap_content"></SeekBar>
            <TextView
                android:textSize="17sp"
                android:textColor="#fff"
                android:text="00:00"
                android:id="@+id/tv_duration"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="wrap_content"></TextView>
        </LinearLayout>

    </RelativeLayout>

</LinearLayout>

视频自定义视图

public class YaoVideoView extends SurfaceView implements SurfaceHolder.Callback {
    private MediaPlayer mediaPlayer = new MediaPlayer();
    //设置视频资源
    public void setData(String path){
        if(mediaPlayer != null){
            mediaPlayer.reset();//重置
            try {
                mediaPlayer.setDataSource(path);
            } catch (IOException e) {
                e.printStackTrace();
            }
            mediaPlayer.prepareAsync();
            mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                    mediaPlayer.start();
                }
            });

        }
    }

    public void init(){
        getHolder().addCallback(this);//获得surfaceview的生命周期
    }
    public YaoVideoView(Context context) {//java代码执行该构造
        super(context);
        init();
    }

    public YaoVideoView(Context context, AttributeSet attrs) {//xml布局执行该构造,一般是他
        super(context, attrs);
        init();
    }

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

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public YaoVideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {
        Log.d("ytx", "被创建了: ");
        if (mediaPlayer != null){
            mediaPlayer.setDisplay(holder);//展示视频画面
        }

    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
        Log.d("ytx", "改变了: ");
    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
        Log.d("ytx", "销毁了: ");
        if(mediaPlayer != null){
            mediaPlayer.release();//释放资源
            mediaPlayer = null;
        }

    }

    /**
     * 播放和暂停
     */
    public void play(){
        if (mediaPlayer != null){
            if(mediaPlayer.isPlaying()){
                mediaPlayer.pause();
            }else{
                mediaPlayer.start();
            }
        }
    }

    /**
     * 快进3秒
     */
    public void quick(){
        if(mediaPlayer != null){
            int currentPosition = mediaPlayer.getCurrentPosition();//获得当前进度
            currentPosition+=3000;
            mediaPlayer.seekTo(currentPosition);//播放到指定进度
        }
    }

    /**
     * 快退3秒
     */
    public void tui(){
        if(mediaPlayer != null){
            int currentPosition = mediaPlayer.getCurrentPosition();//获得当前进度
            currentPosition-=3000;
            mediaPlayer.seekTo(currentPosition);//播放到指定进度
        }
    }

    /**
     * 倍速播放
     */
    @RequiresApi(api = Build.VERSION_CODES.M)
    public void bei(){
        if(mediaPlayer != null){
            PlaybackParams playbackParams = mediaPlayer.getPlaybackParams();//获得播放参数
            playbackParams.setSpeed(2f);//修改速度
            mediaPlayer.setPlaybackParams(playbackParams);//设置回去

        }
    }

    /**
     * 获得当前进度
     * @return
     */
    public int getCurrrent(){
        if(mediaPlayer != null){
            return mediaPlayer.getCurrentPosition();
        }
        return 0;
    }

    /**
     * 获得当前时间 00:00
     */
    public String getCurrrentStr(){
        if(mediaPlayer != null){
            int currentPosition = mediaPlayer.getCurrentPosition();//毫秒
            SimpleDateFormat format = new SimpleDateFormat("mm:ss");//将毫秒---》00:00
            String format1 = format.format(currentPosition);
            return format1;

        }
        return "00:00";
    }

    /**
     * 获得总长度
     * @return
     */
    public int getDuration(){
        if(mediaPlayer != null){
            return mediaPlayer.getDuration();
        }
        return 0;
    }
    /**
     * 获得总时间 00:00
     */
    public String getDurationStr(){
        if(mediaPlayer != null){
            int currentPosition = mediaPlayer.getDuration();//毫秒
            SimpleDateFormat format = new SimpleDateFormat("mm:ss");//将毫秒---》00:00
            String format1 = format.format(currentPosition);
            return format1;

        }
        return "00:00";
    }

    /***
     * 拖动进度条
     * @param progress
     */
    public void seekTo(int progress){
        if(mediaPlayer != null){
            mediaPlayer.seekTo(progress);
        }
    }
}

主页面

public class MainActivity extends AppCompatActivity {
    private YaoVideoView yaoVideoView;
    private TextView tv_start;
    private TextView tv_quick;
    private TextView tv_tui;
    private TextView tv_bei;
    private TextView tv_full;
    private TextView tv_current;
    private SeekBar bar;
    private TextView tv_duration;
    private Timer timer;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            requestPermissions(new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            },101);
        }
        initView();
        initTimer();
    }

    private void initTimer() {
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                final int duration = yaoVideoView.getDuration();
                final int currrent = yaoVideoView.getCurrrent();
                final String durationStr = yaoVideoView.getDurationStr();
                final String currrentStr = yaoVideoView.getCurrrentStr();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        bar.setMax(duration);
                        bar.setProgress(currrent);
                        tv_duration.setText(durationStr+"");
                        tv_current.setText(currrentStr+"");

                    }
                });
            }
        },0,100);
    }

    private void initView() {
        tv_start = (TextView) findViewById(R.id.tv_start);
        tv_quick = (TextView) findViewById(R.id.tv_quick);
        tv_tui = (TextView) findViewById(R.id.tv_tui);
        tv_bei = (TextView) findViewById(R.id.tv_bei);
        tv_full = (TextView) findViewById(R.id.tv_full);
        tv_current = (TextView) findViewById(R.id.tv_current);
        bar = (SeekBar) findViewById(R.id.bar);
        tv_duration = (TextView) findViewById(R.id.tv_duration);
        yaoVideoView = findViewById(R.id.yao);
        yaoVideoView.setData("/sdcard/Movies/1807A.mp4");//播放视频
        tv_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                yaoVideoView.play();
            }
        });
        tv_quick.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                yaoVideoView.quick();
            }
        });
        tv_tui.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                yaoVideoView.tui();
            }
        });

        tv_bei.setOnClickListener(new View.OnClickListener() {
            @RequiresApi(api = Build.VERSION_CODES.M)
            @Override
            public void onClick(View v) {
                yaoVideoView.bei();
            }
        });
        tv_full.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                change();
            }
        });
        bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if(fromUser){
                   yaoVideoView.seekTo(progress);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });
    }
    //横竖屏切换---????视频重头播放---》activity会重新执行---》清单文件配置
    private void change() {
        int width = getWindow().getDecorView().getWidth();
        int height = getWindow().getDecorView().getHeight();
        if(width >= height){//横屏---》竖屏
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//PORTRAIT 肖像 竖着
        }else{//竖屏--->横屏
           setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//LANDSCAPE 山水画 横着
        }
    }
}

/
普通弹幕视图

/***
  绘制文字:
  1.画笔:Paint
  2.画布:Canvas


 */
public class DanMuView extends SurfaceView implements SurfaceHolder.Callback {
    public void init(){
        getHolder().addCallback(this);
        setZOrderOnTop(true);//置顶
        getHolder().setFormat(PixelFormat.TRANSPARENT);//透明
    }
    public DanMuView(Context context) {
        super(context);
        init();
    }

    public DanMuView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

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

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public DanMuView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }
    private float x = 200;
    //创建:绘制文字
    @Override
    public void surfaceCreated(@NonNull final SurfaceHolder holder) {
        //TODO 1:画笔对象
        final Paint paint = new Paint();
        paint.setColor(Color.GREEN);//颜色
        paint.setStrokeWidth(10);//粗细
        paint.setTextSize(50);//文字大小
        //TODO 2:画布
//        Canvas canvas = holder.lockCanvas();//锁定画布
//        canvas.drawText("真帅!!!!!",200,400,paint);
//        holder.unlockCanvasAndPost(canvas);//解除锁定画布,必须写不然就报错
        //动态文字移动
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    Canvas canvas = holder.lockCanvas();//锁定画布
                    canvas.drawColor(Color.WHITE, PorterDuff.Mode.CLEAR);//将之前写的内容清空                  
                    canvas.drawText("真帅!!!!!",x++,400,paint);
                    holder.unlockCanvasAndPost(canvas);//解除锁定画布,必须写不然就报错
                }
            }
        }).start();
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {

    }
    //销毁
    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {

    }
}

高级弹幕

public class GaoJiDanMuView extends SurfaceView implements SurfaceHolder.Callback {
    private List<GaoJiDanMuEntity> list = new ArrayList<>();
    //添加弹幕
    public void addDanMu(GaoJiDanMuEntity gaoJiDanMuEntity){
        list.add(gaoJiDanMuEntity);
    }
    public void init(){

        getHolder().addCallback(this);
        list.add(new GaoJiDanMuEntity("我是穷鬼",20,200,false));
        list.add(new GaoJiDanMuEntity("我是富二代",40,300,true));
        list.add(new GaoJiDanMuEntity("没钱真帅",30,400,false));
        list.add(new GaoJiDanMuEntity("有钱真好",0,180,true));
    }
    public GaoJiDanMuView(Context context) {
        super(context);
        init();
    }

    public GaoJiDanMuView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

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

    public GaoJiDanMuView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }
    //绘制多条弹幕
    @Override
    public void surfaceCreated(@NonNull final SurfaceHolder holder) {
        //会员画笔
        final Paint paint = new Paint();
        paint.setColor(Color.RED);
        paint.setTextSize(70);

        //非会员画笔
        final Paint paint2 = new Paint();
        paint2.setColor(Color.GREEN);
        paint2.setTextSize(50);


        //文字动态
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    Canvas canvas = holder.lockCanvas();
                    canvas.drawColor(Color.WHITE, PorterDuff.Mode.CLEAR);
                    for (int i = 0; i < list.size(); i++) {
                        String text = list.get(i).getText();//文字
                        float x = list.get(i).getX();//x坐标
                        float y = list.get(i).getY();//y坐标
                        boolean vip = list.get(i).isVip();//是否为vip
                        if(vip){
                            canvas.drawText(text,x,y,paint);
                        }else{
                            canvas.drawText(text,x,y,paint2);
                        }
                        x++;
                        list.get(i).setX(x);//设置新的x坐标
                    }

                    holder.unlockCanvasAndPost(canvas);
                }

            }
        }).start();
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {

    }
}

清单文件

 <!-- configChanges 当屏幕方向 键盘 大小 发生改变的时候生命周期不会重新执行 -->
        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|keyboard|screenSize"></activity>

视频弹幕交互

<?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=".VideoDanMuActivity">
    <com.example.day11_surfaceview.view.YaoVideoView
        android:id="@+id/yao"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></com.example.day11_surfaceview.view.YaoVideoView>
    <com.example.day11_surfaceview.view.GaoJiDanMuView
        android:id="@+id/danmu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></com.example.day11_surfaceview.view.GaoJiDanMuView>
    <LinearLayout
        android:layout_alignParentBottom="true"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <EditText
            android:id="@+id/et"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"></EditText>
        <Button
            android:id="@+id/bt_send"
            android:text="发送"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"></Button>
    </LinearLayout>
</RelativeLayout>

主页面

public class VideoDanMuActivity extends AppCompatActivity {

    private YaoVideoView yao;
    private GaoJiDanMuView danmu;
    private EditText editText;
    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video_dan_mu);
        initView();
    }

    private void initView() {
        yao = (YaoVideoView) findViewById(R.id.yao);
        danmu = (GaoJiDanMuView) findViewById(R.id.danmu);
        editText = findViewById(R.id.et);
        button = findViewById(R.id.bt_send);
        yao.setData("/sdcard/Movies/1807A.mp4");//播放视频
        danmu.setZOrderOnTop(true);//弹幕置顶
        danmu.getHolder().setFormat(PixelFormat.TRANSPARENT);//弹幕透明的

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String trim = editText.getText().toString().trim();
                danmu.addDanMu(new GaoJiDanMuEntity(trim,0,400,true));
            }
        });

    }
}
上一篇:Android 软件开发,简易的登录界面与跳转效果


下一篇:POJ 1966 Cable TV Network