公司的項目需要一個視頻的滾動列表。
搜了些文章比較常見的是根據列表項的可視百分比來判斷的。實現起來略復雜。
這里想了一個在要求不高的情況下,實現相對簡便的方法:根據列表滾動時可見的第一個列表項的位置來播放和暫停對應列表項內的視頻。
它的效果大致是這樣的:
	
以下是它的實現。
首先當然是建立列表。
這部分就直接用ListView吧,列表的具體的實現就不貼了。大致就是長這樣的一個列表:
	
接下來就是添加播放器。
這里需要注意的是,在ListView里不能使用我們常用的那種VideoView?;赟urfaceView的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;  }}添加完播放器大致長這樣:
	
接下來就是重點了,要根據列表的滾動來播放和暫停視頻。
這里根據當前滾動的位置來進行判斷。
首先添加滾動監聽:
    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
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。
新聞熱點
疑難解答