Android ListVideo之TextureView实现

3 篇文章 0 订阅
3 篇文章 0 订阅
转载注明出处:
文章出自 我不只是看客/NotLooker

  产(yi)品(tiao)经(si)理(gou)需要实现瀑布流式的视频播放,就像今日头条那样的。
  今日头条样例:
  这里写图片描述

  猜测大概是RecycleView + VideoView/TextureVeiw 实现瀑布流式video
  经过上一篇博客 Android视频播放 (一)——TextureView+Meidaplayer播放视频分析,VideoView继承与SurfaceView应该不可用,但在查看API之后发现,自从Android N之后 SurfaceView渲染方式发生改变,原文: SurfaceView API

Note: Starting in platform version N, SurfaceView’s window position is updated synchronously with other View rendering. This means that translating and scaling a SurfaceView on screen will not cause rendering artifacts. Such artifacts may occur on previous versions of the platform when its window is positioned asynchronously.

注:自从Android N开始,SurfaceView的窗口位置与其他View渲染同步更新。这意味着SurfaceView的翻转和平移不会导致组件的重新渲染。这种重新渲染组件出现在以前的平台版本上也许会导致窗口位置不同步。

从以上的note得知,在android 7.0上 SurfaceView可以经行翻转和平移操作了。于是在list中使用surfaceView也是可行的,那么android自带的VideoVeiw就可用。


这里使用TextureView进行尝试,VideoView也可实现,但是在MediaController显示会出现问题,
  RecycleView+TextureView+MediaPlayer
  
  Activity.class:
  
本地资源替换为自己的视频

  RVAdapter.class:继承自RecyclerView.Adapter,放出关键函数 onCreateViewHolder和onBindViewHolder,viewholder类;

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        MyViewHolder holder = new MyViewHolder(LayoutInflater.from(
                context).inflate(R.layout.item, parent,
                false));
        return holder;
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int position) {
        holder.tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                holder.pb_waiting.setVisibility(View.VISIBLE);
                MyApplication.getInstance().getMediaPlayer().reset();
                holder.tv.setmMediaPlayer(MyApplication.getInstance().getMediaPlayer());
                holder.tv.setPreparedListener(new MediaPlayer.OnPreparedListener() {
                    @Override
                    public void onPrepared(MediaPlayer mp) {
                        holder.pb_waiting.setVisibility(View.GONE);
                        holder.tv.startPlay();

                    }
                });
                holder.tv.setUrl(data.get(position)) ;
            }
        });
    }
    class MyViewHolder extends RecyclerView.ViewHolder {
        MyTextureView tv;
        Button btn;
        ProgressBar pb_waiting;
        public MyViewHolder(View view) {
            super(view);
            tv = (MyTextureView) view.findViewById(R.id.textureview);
            btn = (Button) view.findViewById(R.id.button);
            pb_waiting = (ProgressBar) view.findViewById(R.id.pb_waiting);
        }
    }

为了使瀑布流同时只能播放一个视频,这里把MediaPlayer提取出来,做一个单例,放到Application中。当item需要播放时,就把MediaPlayer设置给textureview,设置监听, 然后设置播放地址;

MyApplication:

public class MyApplication extends Application implements LogUtil {
    private static MyApplication instance = null;

    private MediaPlayer mediaPlayer;

    public static MyApplication getInstance(){
         return  instance ;
       }

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
    }

    public MediaPlayer getMediaPlayer() {
        if(mediaPlayer == null)
            mediaPlayer = new MediaPlayer();
        return mediaPlayer;
    }

    public void setMediaPlayer(MediaPlayer mediaPlayer) {
        this.mediaPlayer = mediaPlayer;
    }
}

布局文件activity_main.xml:

     <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

布局文件item.xml:

自定义的MyTextureView.class,继承自TextureView,根据需求,封装了MediaPlayer、SurfaceTextureListener和播放暂停等操作;

public class MyTextureView extends TextureView implements TextureView.SurfaceTextureListener {
    private static final String TAG = "MyTextureView";

    private String playingUrl = "";

    private MediaPlayer mMediaPlayer;
    private Surface surface;
    private MediaPlayer.OnPreparedListener preparedListener;
    private MediaPlayer.OnErrorListener errorListener;
    private MediaPlayer.OnCompletionListener completionListener;
    private MediaPlayer.OnInfoListener infoListener;
    private MediaPlayer.OnSeekCompleteListener seekCompleteListener;

    public MyTextureView(Context context) {
        super(context);
        initView();
    }

    public MyTextureView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public MyTextureView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView();
    }


    public void startPlay() {
        if (mMediaPlayer != null) {
            mMediaPlayer.start();
            Log.e(TAG, "startPlay");
        } else {
            Log.e(TAG, "start Error media is null");
        }
    }

    public void pausePlay() {
        if (mMediaPlayer != null) {
            mMediaPlayer.pause();
            Log.e(TAG, "stopPlay");
        } else {
            Log.e(TAG, "pause Error media is null");
        }
    }

    public void resetPlay() {
        if (mMediaPlayer != null) {
            mMediaPlayer.reset();
            Log.e(TAG, "resetPlay");
        } else {
            Log.e(TAG, "reset Error media is null");
        }
    }


    public void destory() {
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }


    public void setUrl(String path) {
        if (!playingUrl.equals(path)) {
            mMediaPlayer.reset();
            try {
                playingUrl = path;
                if (path.contains("http")) {
                    mMediaPlayer.setDataSource(path);
                } else {
                    FileInputStream fis = null;
                    fis = new FileInputStream(new File(path));
                    mMediaPlayer.setAudioStreamType(AudioManager.STREAM_RING);
                    mMediaPlayer.setDataSource(fis.getFD());
                }
                mMediaPlayer.prepareAsync();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else {
            mMediaPlayer.start();
        }
        if(preparedListener != null) {
            mMediaPlayer.setOnPreparedListener(preparedListener);
        }
    }

    private void initView() {
        setSurfaceTextureListener(this);
    }


    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i2) {
        initMediaPlayer(surfaceTexture);
    }

    /**
     * @param surfaceTexture
     * @return -1 初始化失败
     */
    private int initMediaPlayer(SurfaceTexture surfaceTexture) {
        if (surfaceTexture == null)
            return 1;

        try {
            if (mMediaPlayer == null) {
                mMediaPlayer = new MediaPlayer();
                Log.e(TAG, " initMediaPlayer new media");
            }
            surface = new Surface(surfaceTexture);
            mMediaPlayer.setSurface(surface);
            Log.e(TAG, " initMediaPlayer Success");
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG, " initMediaPlayer-" + e.getMessage());
            return -1;
        }

    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i2) {
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
        return true;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);
    }

    @Override
    public void setOnClickListener(OnClickListener l) {
        super.setOnClickListener(l);
    }

    public void setmMediaPlayer(MediaPlayer mMediaPlayer) {
        this.mMediaPlayer = mMediaPlayer;
        mMediaPlayer.setSurface(new Surface(this.getSurfaceTexture()));
    }

    public String getPlayingUrl() {
        return playingUrl;
    }

    public void setPreparedListener(MediaPlayer.OnPreparedListener preparedListener) {
        this.preparedListener = preparedListener;
    }

    public void setErrorListener(MediaPlayer.OnErrorListener errorListener) {
        this.errorListener = errorListener;
    }

    public void setCompletionListener(MediaPlayer.OnCompletionListener completionListener) {
        this.completionListener = completionListener;
    }

    public void setInfoListener(MediaPlayer.OnInfoListener infoListener) {
        this.infoListener = infoListener;
    }

    public void setSeekCompleteListener(MediaPlayer.OnSeekCompleteListener seekCompleteListener) {
        this.seekCompleteListener = seekCompleteListener;
    }
}

MyTextureView封装了 startPlay,pausePlay等播放相关操作;还有setUrl 统一了播放地址的入口,可以播放本地路径或者http协议的网络url,同时保存了正在播放的url;
这还可以封装更多操作,方便以后功能的扩展。例如 item跳转详情页无缝播放功能的实现;

demo如下


VideoView测试

用RecycleView+VideoView测试list播放,通过查看资料,得知videoview可以实现,更改adapter的代码,放出关键代码 onBindViewHolder()和ViewHolder

@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {

    holder.tv.setVideoPath(data.get(position));
    holder.tv.setMediaController(new MediaController(context));

    holder.tv.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
       holder.btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if( holder.tv.isPlaying())
                holder.tv.pause();
            else
                holder.tv.start();
        }
    });
}
class MyViewHolder extends RecyclerView.ViewHolder {

    VideoView tv;
    Button btn;

    public MyViewHolder(View view) {
        super(view);
        tv = (VideoView) view.findViewById(R.id.textureview);
        btn = (Button) view.findViewById(R.id.button);
    }
}

点击按钮播放,但有个问题,是MediaController是一个Framlayout,点击就会生成,so在item使用中会出现控制器位置错位如下:

而TextureView的话 可以后期自己重写相关单击操作、增加控制器等,so悬着TextureView实现listvideo.

本文结束,因为代码较齐全,不提供工程下载

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值