溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

詳解Android 視頻滾動(dòng)列表(偷懶型)

發(fā)布時(shí)間:2020-08-28 01:13:06 來源:腳本之家 閱讀:109 作者:書柜里的松鼠 欄目:移動(dòng)開發(fā)

公司的項(xiàng)目需要一個(gè)視頻的滾動(dòng)列表。

搜了些文章比較常見的是根據(jù)列表項(xiàng)的可視百分比來判斷的。實(shí)現(xiàn)起來略復(fù)雜。

這里想了一個(gè)在要求不高的情況下,實(shí)現(xiàn)相對(duì)簡(jiǎn)便的方法:根據(jù)列表滾動(dòng)時(shí)可見的第一個(gè)列表項(xiàng)的位置來播放和暫停對(duì)應(yīng)列表項(xiàng)內(nèi)的視頻。

它的效果大致是這樣的:

詳解Android 視頻滾動(dòng)列表(偷懶型) 

以下是它的實(shí)現(xiàn)。

首先當(dāng)然是建立列表。

這部分就直接用ListView吧,列表的具體的實(shí)現(xiàn)就不貼了。大致就是長(zhǎng)這樣的一個(gè)列表:

詳解Android 視頻滾動(dòng)列表(偷懶型) 

接下來就是添加播放器。

這里需要注意的是,在ListView里不能使用我們常用的那種VideoView?;赟urfaceView的VideoView由于沒有同步緩沖區(qū),它不能在ListView中正常顯示。(顯然SurfaceView+MediaPlayer的形式也不太適合了)我們需要基于TextureView的視頻播放器。

這里偷個(gè)懶,就直接用 PLDroidPlayer這個(gè)庫中的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就停,進(jìn)度條會(huì)留空
          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;
  }
}

添加完播放器大致長(zhǎng)這樣:

詳解Android 視頻滾動(dòng)列表(偷懶型) 

接下來就是重點(diǎn)了,要根據(jù)列表的滾動(dòng)來播放和暫停視頻。

這里根據(jù)當(dāng)前滾動(dòng)的位置來進(jìn)行判斷。

首先添加滾動(dòng)監(jiān)聽:

    myVideoList.setAdapter(playerAdapter);
    myVideoList.setOnScrollListener(new AbsListView.OnScrollListener() {
      @Override
      public void onScrollStateChanged(AbsListView view, int scrollState) {
//        Log.d(TAG,"onScrollStateChanged:"+scrollState);
        //SCROLL_STATE_FLING = 滾動(dòng)中;SCROLL_STATE_IDLE = 結(jié)束滾動(dòng);SCROLL_STATE_TOUCH_SCROLL = 開始滾動(dòng);
        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()獲得可見的第一個(gè)元素,并使用getTop()獲得該元素的偏移量。

接下來增加對(duì)元素內(nèi)視頻的操作,這里通過更新列表的數(shù)據(jù)來實(shí)現(xiàn)。

修改一下上面的監(jiān)聽,判斷當(dāng)前第二個(gè)可見item的位置,當(dāng)?shù)竭_(dá)指定位置時(shí)將播放標(biāo)識(shí)置為true。原先播放中的item的播放標(biāo)識(shí)置為false。
然后更新數(shù)據(jù)。

     myVideoList.setOnScrollListener(new AbsListView.OnScrollListener() {
      @Override
      public void onScrollStateChanged(AbsListView view, int scrollState) {
        //SCROLL_STATE_FLING = 滾動(dòng)中;SCROLL_STATE_IDLE = 結(jié)束滾動(dòng);SCROLL_STATE_TOUCH_SCROLL = 開始滾動(dòng);
        if(scrollState==SCROLL_STATE_IDLE){
          int _index=myVideoList.getFirstVisiblePosition()+1;
          View v1=myVideoList.getChildAt(1);//取可見元素的第二個(gè)
          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) {
      }
    });

這個(gè)的位置判斷上直接寫死了200像素,作為一個(gè)DEMO,位置判斷的數(shù)值上不是很講究。這個(gè)其實(shí)應(yīng)該根據(jù)滾動(dòng)方向和item的高度來計(jì)算的。

在Adapter的getView()方法中根據(jù)_playing的狀態(tài)播放或停止視頻:(停止的時(shí)候要記得釋放掉播放器資源哦,不然列表中這么多視頻的內(nèi)存占用是很可怕的哦。)

  @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;
  }

嗯,完工。

改天再整列表的可視百分比判斷。

相關(guān)github項(xiàng)目地址:https://github.com/codeqian/playerlist

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI