Android在滾動列表中實現視頻的播放(RecyclerView)


新版的百度貼吧,網易新聞中有看視頻的界面。

是隨着view的滾動自動加載的。

 

如圖所示,很方便查看。

因為項目需要,我在開發一個APP,也需要查看視頻,便想實現一個差不多功能的。

 

經過搜索,我發現GITHUB上有這個開源的東西,可以很方便的實現這樣的效果

VideoPlayerManager

 

試着做了個Demo,在此記錄下,以后自己查起來也方便。

 

 

要使用這個很方便,只需要在android studio的build.gradle文件里加入以下內容就行了。

dependencies {
    compile 'com.github.danylovolokh:video-player-manager:0.2.0'
    compile 'com.github.danylovolokh:list-visibility-utils:0.2.0'
}

  

先從布局開始吧!

 

首先,需要一個recycleview。

所以定義一個布局:

  video_watch_layout.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:orientation="vertical" android:layout_width="match_parent"
 4     android:layout_height="match_parent">
 5 
 6     <android.support.v7.widget.RecyclerView
 7         android:id="@+id/video_watch_list"
 8         android:layout_width="match_parent"
 9         android:layout_height="match_parent">
10 
11     </android.support.v7.widget.RecyclerView>
12 
13 
14 
15 </LinearLayout>

 

然后再定義它的item

video_watch_list_item.xml

 
         
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="320dp"
android:layout_marginBottom="4dp"
android:layout_marginTop="4dp">

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<!--播放器-->
<com.volokh.danylo.video_player_manager.ui.VideoPlayerView
android:id="@+id/item_video_vpv_player"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/item_video_tv_title"/>

<!--背景-->
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/item_video_iv_cover"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/item_video_tv_title"
/>

<!--標題-->
<TextView
android:id="@+id/item_video_tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:layout_alignParentBottom="true"/>
</RelativeLayout>

</LinearLayout>
 

 

其中SimpleDraweeView這個控件,可以改成Imageview,用來顯示視頻封面圖的。



下面是展示視頻的Activity的代碼。
package com.duanqu.Idea.activity;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.duanqu.Idea.Adapter.OnlineVideoListItem;
import com.duanqu.Idea.Adapter.VideoListItem;
import com.duanqu.Idea.Adapter.VideoWatchAdapter;
import com.duanqu.Idea.R;
import com.volokh.danylo.video_player_manager.manager.PlayerItemChangeListener;
import com.volokh.danylo.video_player_manager.manager.SingleVideoPlayerManager;
import com.volokh.danylo.video_player_manager.manager.VideoItem;
import com.volokh.danylo.video_player_manager.manager.VideoPlayerManager;
import com.volokh.danylo.video_player_manager.meta.MetaData;
import com.volokh.danylo.visibility_utils.calculator.DefaultSingleItemCalculatorCallback;
import com.volokh.danylo.visibility_utils.calculator.ListItemsVisibilityCalculator;
import com.volokh.danylo.visibility_utils.calculator.SingleListViewItemActiveCalculator;
import com.volokh.danylo.visibility_utils.items.ListItem;
import com.volokh.danylo.visibility_utils.scroll_utils.RecyclerViewItemPositionGetter;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2016/7/28.
 */
public class VideoWatchActivity  extends AppCompatActivity implements View.OnClickListener{
    private RecyclerView mRecyclerView;

    //視頻數據,相當於普通adapter里的datas
    private List<VideoListItem> mLists = new ArrayList<>();

    //它充當ListItemsVisibilityCalculator和列表(ListView, RecyclerView)之間的適配器(Adapter)。
    private RecyclerViewItemPositionGetter mItemsPositionGetter;

    //ListItemsVisibilityCalculator可以追蹤滑動的方向並在過程中計算每個Item的可見度
    //SingleListViewItemActiveCalculator會在滑動時獲取每個View的可見度百分比.
    //所以其構造方法里需要傳入mLists,而mLists里的每個item實現了ListItem接口
    //的getVisibilityPercents方法,也就是返回當前item可見度的方法.
    //這樣ListItemsVisibilityCalculator就可以計算當前item的可見度了.

    private final ListItemsVisibilityCalculator mVideoVisibilityCalculator =
            new SingleListViewItemActiveCalculator(new DefaultSingleItemCalculatorCallback(), mLists);


    //SingleVideoPlayerManager就是只能同時播放一個視頻。
    //當一個view開始播放時,之前那個就會停止
    private final VideoPlayerManager<MetaData> mVideoPlayerManager = new SingleVideoPlayerManager(new PlayerItemChangeListener() {
        @Override
        public void onPlayerItemChanged(MetaData metaData) {
        }
    });

    private int mScrollState;
    private LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
    private static final String URL =
            "http://dn-chunyu.qbox.me/fwb/static/images/home/video/video_aboutCY_A.mp4";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.video_watch_layout);
        mRecyclerView = (RecyclerView) findViewById(R.id.video_watch_list);


        //添加視頻數據
        for (int i = 0; i < 10; ++i) {
            mLists.add(new OnlineVideoListItem(mVideoPlayerManager, "測試", "http://115.159.159.65:8080/EAsy/cover.jpg", URL));
 } mRecyclerView.setLayoutManager(mLayoutManager); VideoWatchAdapter adapter = new VideoWatchAdapter(mLists); mRecyclerView.setAdapter(adapter); //////////////////////////////////////////////

        //這里是文檔上默認的寫法,直接復制下來。 //查看了下源碼其中VisibilityCalculator.onScrollStateIdle的這 //個方法又調用了方法calculateMostVisibleItem,用來計算滑動狀態改變時 //的最大可見度的item.這個方法的計算方法是這樣的:當view無論是向上還是 //向下滾動時,在滾動的過程中,計算可見度最大的item。當滾動狀態為空閑時 //此時最后一個計算得出的可見度最大的item就是當前可見度最大的item //而onScroll方法是處理item滾出屏幕后的計算,用於發現新的活動item
        mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int scrollState) { mScrollState = scrollState; if(scrollState == RecyclerView.SCROLL_STATE_IDLE && !mLists.isEmpty()){ mVideoVisibilityCalculator.onScrollStateIdle( mItemsPositionGetter, mLayoutManager.findFirstVisibleItemPosition(), mLayoutManager.findLastVisibleItemPosition()); } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if(!mLists.isEmpty()){ mVideoVisibilityCalculator.onScroll( mItemsPositionGetter, mLayoutManager.findFirstVisibleItemPosition(), mLayoutManager.findLastVisibleItemPosition() - mLayoutManager.findFirstVisibleItemPosition() + 1, mScrollState); } } }); mItemsPositionGetter = new RecyclerViewItemPositionGetter(mLayoutManager, mRecyclerView); /////////////////////////////////////////////
 } //文檔上的默認實現,復制下來 //onResume()中調用方法,使屏幕亮起時啟動對View的可見度的計算。
 @Override public void onResume() { super.onResume(); if(!mLists.isEmpty()){ // need to call this method from list view handler in order to have filled list
 mRecyclerView.post(new Runnable() { @Override public void run() { mVideoVisibilityCalculator.onScrollStateIdle( mItemsPositionGetter, mLayoutManager.findFirstVisibleItemPosition(), mLayoutManager.findLastVisibleItemPosition()); } }); } } @Override public void onClick(View v) { } @Override public void onStop() { super.onStop(); mVideoPlayerManager.resetMediaPlayer(); // 頁面不顯示時, 釋放播放器
 } }

 

 

其次是

ListItem的實現,其實現的是當前item的可見度計算。
當然也保存了視頻數據的一些信息,供Adapter使用
package com.duanqu.Idea.Adapter;

import android.graphics.Rect;
import android.support.annotation.DrawableRes;
import android.view.View;

import com.volokh.danylo.video_player_manager.manager.VideoItem;
import com.volokh.danylo.video_player_manager.manager.VideoPlayerManager;
import com.volokh.danylo.video_player_manager.meta.CurrentItemMetaData;
import com.volokh.danylo.video_player_manager.meta.MetaData;
import com.volokh.danylo.visibility_utils.items.ListItem;


/**
 * 基本視頻項, 實現適配項和列表項
 * <p/>
 * Created by wangchenlong on 16/1/27.
 */
public abstract class VideoListItem implements VideoItem, ListItem {

    private final Rect mCurrentViewRect; // 當前視圖的方框
    private final VideoPlayerManager<MetaData> mVideoPlayerManager; // 視頻播放管理器
    private final String mTitle; // 標題
    private final String CoverImageUrl; // 圖片資源

    // 構造器, 輸入視頻播放管理器
    public VideoListItem(
            VideoPlayerManager<MetaData> videoPlayerManager,
            String title,
            String imageResource) {
        mVideoPlayerManager = videoPlayerManager;
        mTitle = title;
        CoverImageUrl = imageResource;

        mCurrentViewRect = new Rect();
    }

    // 視頻項的標題
    public String getTitle() {
        return mTitle;
    }

    public String getCoverImageUrl() {
        return CoverImageUrl;
    }

    // 顯示可視的百分比程度
    @Override
    public int getVisibilityPercents(View view) {
        int percents = 100;

        view.getLocalVisibleRect(mCurrentViewRect);
        int height = view.getHeight();

        if (viewIsPartiallyHiddenTop()) {
            percents = (height - mCurrentViewRect.top) * 100 / height;
        } else if (viewIsPartiallyHiddenBottom(height)) {
            percents = mCurrentViewRect.bottom * 100 / height;
        }



        return percents;
    }

    @Override
    public void setActive(View newActiveView, int newActiveViewPosition) {
        VideoWatchAdapter.VideoViewHolder viewHolder =
                (VideoWatchAdapter.VideoViewHolder) newActiveView.getTag();
        playNewVideo(new CurrentItemMetaData(newActiveViewPosition, newActiveView),
                viewHolder.getVpvPlayer(), mVideoPlayerManager);
    }

    @Override
    public void deactivate(View currentView, int position) {
        stopPlayback(mVideoPlayerManager);
    }

    @Override
    public void stopPlayback(VideoPlayerManager videoPlayerManager) {
        videoPlayerManager.stopAnyPlayback();
    }

    // 頂部出現
    private boolean viewIsPartiallyHiddenTop() {
        return mCurrentViewRect.top > 0;
    }

    // 底部出現
    private boolean viewIsPartiallyHiddenBottom(int height) {
        return mCurrentViewRect.bottom > 0 && mCurrentViewRect.bottom < height;
    }
}

 

對這個類進行繼承

package com.duanqu.Idea.Adapter;

import android.support.annotation.DrawableRes;

import com.volokh.danylo.video_player_manager.manager.VideoPlayerManager;
import com.volokh.danylo.video_player_manager.meta.MetaData;
import com.volokh.danylo.video_player_manager.ui.VideoPlayerView;

/**
 * Created by Administrator on 2016/7/28.
 */
public class OnlineVideoListItem extends VideoListItem {

    private final String mOnlineUrl; // 資源文件描述

    public OnlineVideoListItem(
            VideoPlayerManager<MetaData> videoPlayerManager,
            String title,
            String imageResource,
            String onlineUrl
    ) {
        super(videoPlayerManager, title, imageResource);

        mOnlineUrl = onlineUrl;
    }

    @Override
    public void playNewVideo(MetaData currentItemMetaData, VideoPlayerView player, VideoPlayerManager<MetaData> videoPlayerManager) {
        videoPlayerManager.playNewVideo(currentItemMetaData, player, mOnlineUrl);
    }
}

復寫一個playNewVideo方法。

 

 

最后是recycleview的Adapter方法:

package com.duanqu.Idea.Adapter;


import android.content.Context;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.duanqu.Idea.R;
import com.volokh.danylo.video_player_manager.ui.MediaPlayerWrapper;
import com.volokh.danylo.video_player_manager.ui.VideoPlayerView;

import java.util.List;


/**
 * Created by Administrator on 2016/7/28.
 */
public class VideoWatchAdapter extends RecyclerView.Adapter<VideoWatchAdapter.VideoViewHolder>{

    private final List<VideoListItem> mList; // 視頻項列表

    // 構造器
    public VideoWatchAdapter(List<VideoListItem> list) {
        mList = list;
    }

    @Override
    public VideoWatchAdapter.VideoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.video_watch_list_item, parent, false);
        // 必須要設置Tag, 否則無法顯示
        VideoWatchAdapter.VideoViewHolder holder = new VideoWatchAdapter.VideoViewHolder(view);
        view.setTag(holder);
        return new VideoWatchAdapter.VideoViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final VideoWatchAdapter.VideoViewHolder holder, int position) {
        VideoListItem videoItem = mList.get(position);
        holder.bindTo(videoItem);
    }

    @Override
    public int getItemCount() {
        return mList.size();
    }

    public static class VideoViewHolder extends RecyclerView.ViewHolder {
        VideoPlayerView mVpvPlayer; // 播放控件
        ImageView mIvCover; // 覆蓋層
        TextView mTvTitle; // 標題

        private Context mContext;
        private MediaPlayerWrapper.MainThreadMediaPlayerListener mPlayerListener;

        public VideoViewHolder(View itemView) {
            super(itemView);
            mVpvPlayer = (VideoPlayerView) itemView.findViewById(R.id.item_video_vpv_player); // 播放控件
            mTvTitle = (TextView) itemView.findViewById(R.id.item_video_tv_title);
            mIvCover = (ImageView) itemView.findViewById(R.id.item_video_iv_cover);

            mContext = itemView.getContext().getApplicationContext();
            mPlayerListener = new MediaPlayerWrapper.MainThreadMediaPlayerListener() {
                @Override
                public void onVideoSizeChangedMainThread(int width, int height) {
                }

                @Override
                public void onVideoPreparedMainThread() {
                    // 視頻播放隱藏前圖
                    mIvCover.setVisibility(View.INVISIBLE);
                }

                @Override
                public void onVideoCompletionMainThread() {
                }

                @Override
                public void onErrorMainThread(int what, int extra) {
                }

                @Override
                public void onBufferingUpdateMainThread(int percent) {
                }

                @Override
                public void onVideoStoppedMainThread() {
                    // 視頻暫停顯示前圖
                    mIvCover.setVisibility(View.VISIBLE);
                }
            };

            mVpvPlayer.addMediaPlayerListener(mPlayerListener);
        }

        public void bindTo(VideoListItem vli) {
            mTvTitle.setText(vli.getTitle());
            mIvCover.setImageURI(Uri.parse(vli.getCoverImageUrl()));
        }

        // 返回播放器
        public VideoPlayerView getVpvPlayer() {
            return mVpvPlayer;
        }

    }
}

 

 

 

嘿,寫了一點,就開始復制粘貼了。。反正是寫給自己看的。。。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM