详解Android 视频滚动列表(偷懒型)

发布时间:2020-08-28 01:13:06 作者:书柜里的松鼠
来源:脚本之家 阅读:110

公司的项目需要一个视频的滚动列表。

搜了些文章比较常见的是根据列表项的可视百分比来判断的。实现起来略复杂。

这里想了一个在要求不高的情况下,实现相对简便的方法:根据列表滚动时可见的第一个列表项的位置来播放和暂停对应列表项内的视频。

它的效果大致是这样的:

详解Android 视频滚动列表(偷懒型) 

以下是它的实现。

首先当然是建立列表。

这部分就直接用ListView吧,列表的具体的实现就不贴了。大致就是长这样的一个列表:

详解Android 视频滚动列表(偷懒型) 

接下来就是添加播放器。

这里需要注意的是,在ListView里不能使用我们常用的那种VideoView。基于SurfaceView的VideoView由于没有同步缓冲区,它不能在ListView中正常显示。(显然SurfaceView+MediaPlayer的形式也不太适合了)我们需要基于TextureView的视频播放器。

这里偷个懒,就直接用 PLDroidPlayer这个库中的PLVideoTextureView了

在列表的Adapter中的添加播放器。

Adapter的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
  <LinearLayout
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <RelativeLayout
      android:id="@+id/videoTable"
      android:gravity="center"
      android:layout_width="match_parent"
      android:layout_height="wrap_content">
      <ImageView
        android:src="@drawable/videoico"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
      <com.pili.pldroid.player.widget.PLVideoTextureView
        android:id="@+id/myVideoView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center" />
    </RelativeLayout>
    <TextView
      android:text="视频名称"
      android:id="@+id/videoName_t"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content" />
  </LinearLayout>
</LinearLayout>

Adapter部分代码:

package net.codepig.playerlist.adapers;

import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.pili.pldroid.player.AVOptions;
import com.pili.pldroid.player.PLMediaPlayer;
import com.pili.pldroid.player.widget.PLVideoTextureView;

import net.codepig.playerlist.R;
import net.codepig.playerlist.beans.VideoInfo;
import net.codepig.playerlist.deviceInfo;

import java.util.List;

/**
 * 视频单元页面
 * Created by QZD on 2017/11/13.
 */

public class PlayerAdapter extends BaseAdapter{
  private Context _context;
  private Activity mainActivity;
  private List<VideoInfo> myVideoData;
  private LayoutInflater inflater;
  private ViewHolder hodler = null;
  private PLMediaPlayer mPlayer=null;
//  private PLVideoTextureView myVideoView;
  private int _id;
  private String _name;
  private String _url="";

  private final String TAG="LOGCAT";

  public PlayerAdapter(Context context, List<VideoInfo> data) {
    super();
    _context = context;
    mainActivity=(Activity) context;
    myVideoData = data;
    inflater = LayoutInflater.from(context);
  }

  @Override
  public View getView(final int postion, View convertView, ViewGroup parent) {
    hodler = new ViewHolder();
    convertView = inflater.inflate(R.layout.player_adapter_l, null);
    hodler.videoName_t = convertView.findViewById(R.id.videoName_t);
    hodler.videoTable = convertView.findViewById(R.id.videoTable);
    hodler.myVideoView = convertView.findViewById(R.id.myVideoView);
    convertView.setTag(hodler);

    hodler.videoTable.getLayoutParams().width= deviceInfo._screenWidth;
    hodler.videoTable.getLayoutParams().height=deviceInfo._screenHeight;
//    Log.d(TAG,"screenSize:"+deviceInfo._screenWidth+"-"+deviceInfo._screenHeight);

    VideoInfo _vInfo=myVideoData.get(postion);
    _name=_vInfo.get_name();
    hodler.videoName_t.setText(_vInfo.get_name());
    _id=_vInfo.get_id();
    _url=_vInfo.get_url();
    if(!_url.equals("")) {
      setVideo(_url);
    }

    return convertView;
  }

  /**
   * 初始化播放器
   * @param url
   */
  private void setVideo(String url){
    int codec = mainActivity.getIntent().getIntExtra("mediaCodec", AVOptions.MEDIA_CODEC_AUTO);
    AVOptions options = new AVOptions();
    options.setInteger(AVOptions.KEY_PREPARE_TIMEOUT, 10 * 1000);
    options.setInteger(AVOptions.KEY_MEDIACODEC, codec);
    hodler.myVideoView.setAVOptions(options);
    hodler.myVideoView.setVideoPath(url);
    hodler.myVideoView.start();

    hodler.myVideoView.setOnErrorListener(new PLMediaPlayer.OnErrorListener(){
      @Override
      public boolean onError(PLMediaPlayer mp, int errorCode) {
        Log.d(TAG,"errorCode:"+errorCode);
        return true;
      }
    });
    hodler.myVideoView.setOnCompletionListener(new PLMediaPlayer.OnCompletionListener() {
      @Override
      public void onCompletion(PLMediaPlayer mp) {
//        Log.d(TAG, "player onCompletion:"+videoDuration/1000+"-"+_curTime/1000);
        hodler.myVideoView.seekTo(0);
        hodler.myVideoView.start();
      }
    });
    hodler.myVideoView.setOnPreparedListener(new PLMediaPlayer.OnPreparedListener() {
      @Override
      public void onPrepared(PLMediaPlayer mediaPlayer, int percent) {
        Log.d(TAG, "player onPrepared");
        if(mPlayer==null){
          mPlayer=mediaPlayer;
        }
        //播放
        if(hodler.myVideoView!=null){
          hodler.myVideoView.start();
        }else{
          Log.d(TAG, _name+"no myVideoView");
        }
      }
    });
    hodler.myVideoView.setOnBufferingUpdateListener(new PLMediaPlayer.OnBufferingUpdateListener() {
      @Override
      public void onBufferingUpdate(PLMediaPlayer mp, int percent) {
        try {
          int _pec = hodler.myVideoView.getBufferPercentage();//百分比到99就停,进度条会留空
          if (_pec == 99) {
            _pec = 100;
          }
        }catch (Exception e){
          Log.d(TAG,"percentage error:"+e.toString());
        }
      }
    });
    hodler.myVideoView.setOnVideoSizeChangedListener(new PLMediaPlayer.OnVideoSizeChangedListener() {
      @Override
      public void onVideoSizeChanged(PLMediaPlayer plMediaPlayer, int width, int height) {
        Log.d(TAG,"VideoSize:"+width+"_"+height);
      }
    });
  }

  @Override
  public int getCount() {
    if (myVideoData != null) {
      return myVideoData.size();
    } else {
      return 0;
    }
  }

  @Override
  public Object getItem(int position) {
    return myVideoData.get(position);
  }

  @Override
  public long getItemId(int postion) {
    // TODO Auto-generated method stub
    return postion;
  }

  public static class ViewHolder {
    public TextView videoName_t;
    public RelativeLayout videoTable;
    public PLVideoTextureView myVideoView;
  }
}

添加完播放器大致长这样:

详解Android 视频滚动列表(偷懒型) 

接下来就是重点了,要根据列表的滚动来播放和暂停视频。

这里根据当前滚动的位置来进行判断。

首先添加滚动监听:

    myVideoList.setAdapter(playerAdapter);
    myVideoList.setOnScrollListener(new AbsListView.OnScrollListener() {
      @Override
      public void onScrollStateChanged(AbsListView view, int scrollState) {
//        Log.d(TAG,"onScrollStateChanged:"+scrollState);
        //SCROLL_STATE_FLING = 滚动中;SCROLL_STATE_IDLE = 结束滚动;SCROLL_STATE_TOUCH_SCROLL = 开始滚动;
        if(scrollState==SCROLL_STATE_IDLE){
          Log.d(TAG,"FirstVisiblePosition:"+myVideoList.getFirstVisiblePosition());
          View v0=myVideoList.getChildAt(0);
          if(v0!=null){
            int scrollTop=v0.getTop();
            Log.d(TAG,"scroll top:"+scrollTop);
          }
        }
      }

      @Override
      public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
      }
    });

这里通过getFirstVisiblePosition()获得可见的第一个元素,并使用getTop()获得该元素的偏移量。

接下来增加对元素内视频的操作,这里通过更新列表的数据来实现。

修改一下上面的监听,判断当前第二个可见item的位置,当到达指定位置时将播放标识置为true。原先播放中的item的播放标识置为false。
然后更新数据。

     myVideoList.setOnScrollListener(new AbsListView.OnScrollListener() {
      @Override
      public void onScrollStateChanged(AbsListView view, int scrollState) {
        //SCROLL_STATE_FLING = 滚动中;SCROLL_STATE_IDLE = 结束滚动;SCROLL_STATE_TOUCH_SCROLL = 开始滚动;
        if(scrollState==SCROLL_STATE_IDLE){
          int _index=myVideoList.getFirstVisiblePosition()+1;
          View v1=myVideoList.getChildAt(1);//取可见元素的第二个
          if(v1!=null){
            int scrollTop=v1.getTop();
            if(scrollTop<200){
              if(_oldItem!=_index) {
                _infoList.get(_index).set_playing(true);
                _infoList.get(_oldItem).set_playing(false);
                _oldItem=_index;
                playerAdapter.notifyDataSetChanged();
              }
            }
//            Log.d(TAG,"scroll top:"+scrollTop);
          }
        }
      }

      @Override
      public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
      }
    });

这个的位置判断上直接写死了200像素,作为一个DEMO,位置判断的数值上不是很讲究。这个其实应该根据滚动方向和item的高度来计算的。

在Adapter的getView()方法中根据_playing的状态播放或停止视频:(停止的时候要记得释放掉播放器资源哦,不然列表中这么多视频的内存占用是很可怕的哦。)

  @Override
  public View getView(final int postion, View convertView, ViewGroup parent) {
    hodler = new ViewHolder();
    convertView = inflater.inflate(R.layout.player_adapter_l, null);
    hodler.videoName_t = convertView.findViewById(R.id.videoName_t);
    hodler.videoTable = convertView.findViewById(R.id.videoTable);
    hodler.myVideoView = convertView.findViewById(R.id.myVideoView);
    convertView.setTag(hodler);

    hodler.videoTable.getLayoutParams().width= deviceInfo._screenWidth;
    hodler.videoTable.getLayoutParams().height=deviceInfo._screenHeight;
    Log.d(TAG,"screenSize:"+deviceInfo._screenWidth+"-"+deviceInfo._screenHeight);

    VideoInfo _vInfo=myVideoData.get(postion);
    _name=_vInfo.get_name();
    hodler.videoName_t.setText(_vInfo.get_name());
    _id=_vInfo.get_id();
    _url=_vInfo.get_url();
    if(!_url.equals("")) {
    //视频的播放和停止
      if(_vInfo.get_playing()){
        setVideo(_url);
      }else{
        if(hodler.myVideoView!=null) {
          if (hodler.myVideoView.isPlaying()) {
            hodler.myVideoView.stopPlayback();
            hodler.myVideoView.releaseSurfactexture();
          }
        }
      }
    }
    return convertView;
  }

嗯,完工。

改天再整列表的可视百分比判断。

相关github项目地址:https://github.com/codeqian/playerlist

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持亿速云。

推荐阅读:
  1.   redis字符串内型详解
  2. Python基础详解

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

android 视频 列表

上一篇:详解python配置虚拟环境

下一篇:深入理解Spring Cloud Zuul过滤器

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》