Backlight87
8/24/2017 - 3:45 PM

选取本地视频相册,然后转换为recycleView展示出来,具体的实现逻辑需要的时候再捋清楚,MVP方式实现

选取本地视频相册,然后转换为recycleView展示出来,具体的实现逻辑需要的时候再捋清楚,MVP方式实现

/**
 * 从本地的视频相册选取视频
 * Created by hsp on 2017/7/12.
 */
public class VideoAlbumActivity extends AbstractMvpActivity<VideoAlbumPresenter> implements VideoAlbumContract.View {

    // ===========================================================
    // Constants
    // ===========================================================

    public static final int NUM_COLUMNS = 3;//每一行放三个视频
    // ===========================================================
    // Fields
    // ===========================================================

    private RecyclerView mRecyclerView;
    private VideoAdapter mVideoAdapter;
    private View mEmptyContentView;

    // ===========================================================
    // Override Methods
    // ===========================================================

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //super.onCreate执行三步,初始化view :initViews(),把view赋值给presenter,调用presenter的加载数据的方法,在把数据显示在view上
        super.onCreate(savedInstanceState);
        //发一下广播更新本地视频相册
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            final Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
            final Uri contentUri = Uri.parse("file://" + Environment.getExternalStorageDirectory());
            scanIntent.setData(contentUri);
            sendBroadcast(scanIntent);
        } else {
            final Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory()));
            sendBroadcast(intent);
        }
        initEventAndData();
    }

    @Override
    protected VideoAlbumPresenter onLoadPresenter() {
        return new VideoAlbumPresenter();
    }

    /**
     * 初始化控件,这里主要是初始化展现视频列表的recyclerView和本地视频为空的时候的tipView
     */
    @Override
    protected void initViews(Bundle savedInstanceState) {
        setContentView(R.layout.musv_video_album_activity);
        mEmptyContentView = findViewById(R.id.framework_content_empty_rlayout);
        mRecyclerView = (RecyclerView) findViewById(R.id.musv_show_video_rv);
        mRecyclerView.setLayoutManager(new GridLayoutManager(this, NUM_COLUMNS));//每行展示三张图片
        mRecyclerView.addItemDecoration(new VideoAlbumActivity.SpaceItemDecoration(DeviceUtils.dip2px(0)));
    }

    /**
     * 初始化控件的一些事件和其他数据
     */
    @Override
    protected void initEventAndData() {
        //初始化导航栏右部叉的点击事件
        TopActionBar topActionBar = (TopActionBar) findViewById(R.id.musv_toolbar_tab);
        topActionBar.setRightOnClickLinstener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }

    /**
     * recyclerView的数据更新了提醒刷新
     */
    @Override
    public void showLocalVideoList(ArrayList<VideoEntity> LocalVideoList) {
        updateRecyclerView(LocalVideoList);
    }

    @Override
    public void showDialog() {
        showProgressDialog();
    }

    @Override
    public void closeDialog() {
        dismissProgressDialog();
    }

    /**
     * 本地视频为空的时候显示提示信息
     */
    @Override
    public void showEmptyContent() {
        mEmptyContentView.setVisibility(View.VISIBLE);
    }

    // ===========================================================
    // Define Methods
    // ===========================================================

    /**
     * 通知更新recycleView
     */
    private void updateRecyclerView(final ArrayList<VideoEntity> LocalVideoList) {
        if (null == mVideoAdapter) {
            mVideoAdapter = new VideoAdapter(LocalVideoList);
            mRecyclerView.setAdapter(mVideoAdapter);
            mVideoAdapter.setOnRecyclerViewItemListener(new OnRecyclerViewItemListener() {
                @Override
                public void onItemClick(View v, int position) {
                    // Toast.makeText(VideoAlbumActivity.this, "我要跳到视频裁剪页面了", Toast.LENGTH_SHORT).show();
                    Intent intent = new Intent(VideoAlbumActivity.this, VideoCropActivity.class);
                    intent.putExtra(VideoCropActivity.VIDEO_PATH, LocalVideoList.get(position).getPath());
                    startActivity(intent);
                }
            });
        } else {
            mVideoAdapter.notifyDataSetChanged();
        }
    }

    // ===========================================================
    // Inner and Anonymous Classes
    // ===========================================================

    /**
     * 控制recycle view 的item的间距
     */
    private class SpaceItemDecoration extends RecyclerView.ItemDecoration {
        int mSpace;//间距

        private SpaceItemDecoration(int space) {
            this.mSpace = space;
        }

        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            int position = parent.getChildAdapterPosition(view); // item position
            int column = position % NUM_COLUMNS; // item column
            outRect.left = mSpace - column * mSpace / NUM_COLUMNS; // spacing - column * ((1f / spanCount) * spacing)
            outRect.right = (column + 1) * mSpace / NUM_COLUMNS; // (column + 1) * ((1f / spanCount) * spacing)
            if (position < NUM_COLUMNS) { // top edge
                outRect.top = mSpace;
            }
            outRect.bottom = mSpace; // item bottom
        }
    }

    /**
     * recyclerView的适配器
     */
    private class VideoAdapter extends RecyclerView.Adapter<VideoAlbumActivity.VideoAdapter.ViewHolder> {

        private List<VideoEntity> mData;
        private int imageHeight = getScreenWidth() / NUM_COLUMNS;//每张图片的宽和高都是屏幕横的NUM_COLUMNS分之一
        private OnRecyclerViewItemListener mListener;//设置点击事件的监听

        private void setOnRecyclerViewItemListener(OnRecyclerViewItemListener l) {
            mListener = l;
        }

        VideoAdapter(List<VideoEntity> data) {
            this.mData = data;
        }

        @Override
        public VideoAlbumActivity.VideoAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(VideoAlbumActivity.this).inflate(R.layout.musv_video_album_list_item, parent, false);
            view.getLayoutParams().height = imageHeight;
            return new VideoAlbumActivity.VideoAdapter.ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(final VideoAlbumActivity.VideoAdapter.ViewHolder holder, final int position) {
            final VideoEntity mediaModel = mData.get(position);
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mListener != null) {
                        mListener.onItemClick(holder.itemView, holder.getAdapterPosition());
                    }
                }
            });
            holder.tvDuration.setText(NumberUtils.getMinAndSec(mediaModel.getDuration()));
            String mediaPath = "file://" + mediaModel.getPath();
            //设置加载的时候的一些选项
            DisplayImageOptions options = new DisplayImageOptions.Builder().memoryCacheExtraOptions(imageHeight, imageHeight)
                    .diskCacheExtraOptions(imageHeight, imageHeight).cacheImageMultipleSizesInDiskCache().compressQuality(75)
                    .cacheInMemory(true).cacheOnDisk(true).build();
            //加载图片
            ImageLoaderUtils.displayImage(mediaPath, holder.mThumb, options);

        }

        @Override
        public int getItemCount() {
            return null == mData ? 0 : mData.size();
        }

        class ViewHolder extends RecyclerView.ViewHolder {
            ImageView mThumb;
            TextView tvDuration;

            ViewHolder(View view) {
                super(view);
                mThumb = (ImageView) view.findViewById(R.id.musv_video_thumb_iv);
                tvDuration = (TextView) view.findViewById(R.id.musv_time_tv);
            }
        }
    }

    /**
     * recycle View的监听接口
     */
    interface OnRecyclerViewItemListener {
        void onItemClick(View v, int position);
    }
    // ===========================================================
    // Getter & Setter
    // ===========================================================


}

/**
 * 获取本地视频相册的所有视频
 * Created by hsp on 2017/7/11.
 */
public class VideoProvider {
    private static final String TAG = "VideoProvider";
    private static final String SUPPORT_FORMAT = "('video/mp4','video/3gp')";//支持的格式

    /**
     * 获取video
     *
     * @param context 上下文
     * @return 返回满足支持格式的所有的video
     */
    public static ArrayList<VideoEntity> getVideos(Context context) {
        ArrayList<VideoEntity> list = null;
        Cursor cursor = null;
        try {
            list = new ArrayList<>();
            Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            String selection = MediaStore.Video.Media.MIME_TYPE + " in " + SUPPORT_FORMAT;//视频在我们支持的格式里面
            String sortOrder = MediaStore.Video.Media._ID + " DESC";//按照mediaID降序排序
            cursor = context.getContentResolver().query(uri, null, selection, null, sortOrder);
            if (null == cursor) {
                return null;
            }
            VideoEntity info;
            for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
                int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID));
                String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.TITLE));
                String album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.ALBUM));
                String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.ARTIST));
                String displayName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME));
                String mimeType = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.MIME_TYPE));
                String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA));
                long duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION));
                long size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE));
                if (isZ9WithMovVideo(path)) {
                    continue;
                }
                info = new VideoEntity(id, title, album, artist, displayName, mimeType, path, size, duration);
                list.add(info);
            }

        } catch (Throwable e) {
            Log.d(TAG, "getVideos:  get video error");
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return list;
    }


    private static String mDeviceName = null;

    // 努比亚Z9不支持mov格式,单机屏蔽
    private static boolean isZ9WithMovVideo(String path) {
        try {
            if (!TextUtils.isEmpty(path) && (path.endsWith(".mov") || path.endsWith(".MOV"))) {
                if (TextUtils.isEmpty(mDeviceName)) {
                    mDeviceName = Build.MODEL;//获取手机型号
                }
                if (!TextUtils.isEmpty(mDeviceName) && mDeviceName.equals("NX511J")) {
                    return true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
}

/**
 * 本地视频相册的presenter,获取本地视频筛选后把视频列表给对应的view
 * Created by hsp on 2017/7/12.
 */
public class VideoAlbumPresenter extends AbstractPresenter<VideoAlbumContract.View, VideoAlbumContract.Model>
        implements VideoAlbumContract.Presenter {

    /**
     * 手机上所有满足长度的视频
     */
    private List<VideoEntity> mVerifyVideoList = new ArrayList<>();

    /**
     * 手机上所有的的视频
     */
    private List<VideoEntity> mVideoList = new ArrayList<>();

    public VideoAlbumPresenter() {
        mModel = new VideoAlbumModel();
    }

    @Override
    public void subscribe() {
        loadLocalVideoList();
    }

    @Override
    public void loadLocalVideoList() {
        new VideoAlbumPresenter.LoadVideoTask().execute();
        mVideoList = mModel.getVideos((Context) mView);
        verifyVideoList();
        if (mVerifyVideoList == null || mVerifyVideoList.size() == 0) {
            mView.showEmptyContent();
        } else {
            mView.showLocalVideoList((ArrayList<VideoEntity>) mVerifyVideoList);
        }
    }

    /**
     * 把视频长度小于5秒或者大于10分钟的筛选掉
     */
    private static boolean verifyVideo(VideoEntity mediaModel) {
        if (mediaModel.getDuration() < 5000) {//至少要5秒
            return false;
        } else if (mediaModel.getDuration() > 10 * 60 * 1000) {
            //不能大于10分钟
            return false;
        }
        return true;
    }

    /**
     * 获取在我们支持的长度范围内的视频list
     */
    private void verifyVideoList() {
        mVerifyVideoList.clear();
        for (int i = 0; i < mVideoList.size(); i++) {
            if (verifyVideo(mVideoList.get(i))) {
                mVerifyVideoList.add(mVideoList.get(i));
            }
        }
    }

    /**
     * LoadVideoTask:使用asyncTask 去获取所有的视频实体VideoEntity的相关信息的列表
     */
    private class LoadVideoTask extends AsyncTask<String, Void, ArrayList<VideoEntity>> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mView.showDialog();
        }

        @Override
        protected ArrayList<VideoEntity> doInBackground(String... strings) {
            return VideoProvider.getVideos((Context) mView);
        }

        @Override
        protected void onPostExecute(ArrayList<VideoEntity> videoEntities) {
            super.onPostExecute(videoEntities);
            mVideoList.clear();
            if (videoEntities != null) {
                if (videoEntities.size() > 0) {
                    mVideoList.addAll(videoEntities);
                }
            }
            mView.closeDialog();
        }
    }

}


//列表的item用这个,以便于能够在右下角显示时间
<?xml version="1.0" encoding="utf-8"?>
<com.meitu.overseamv.musicvideo.app.videoalbum.view.RatioRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                                        xmlns:app="http://schemas.android.com/apk/res-auto"
                                                                        xmlns:tools="http://schemas.android.com/tools"
                                                                        android:layout_width="match_parent"
                                                                        android:layout_height="match_parent"
                                                                        android:background="@color/musv_video_album_dark_black"
                                                                        app:ratioHeight="1"
                                                                        app:ratioWidth="1"
                                                                        app:standard="w">

    <ImageView
        android:id="@+id/musv_video_thumb_iv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/musv_time_tv"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:background="@drawable/musv_video_album_duration_bg"
        android:gravity="right|bottom"
        android:paddingBottom="5dp"
        android:paddingRight="5dp"
        android:shadowColor="@color/framework_color_black"
        android:shadowDx="2"
        android:shadowDy="2"
        android:textColor="@color/framework_color_white"
        android:textSize="13sp"
        tools:text="1:02" />

    <FrameLayout
        android:id="@+id/fl_video_checked"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/framework_color_black50"
        android:visibility="gone">
    </FrameLayout>

</com.meitu.overseamv.musicvideo.app.videoalbum.view.RatioRelativeLayout>



/**
 * 自定义view,让时间透明的显示在视频的右下角
 * Created by hsp on 2017/7/11.
 */
public class RatioRelativeLayout extends RelativeLayout {
    // ===========================================================
    // Constants
    // ===========================================================
    
    // ===========================================================
    // Fields
    // ===========================================================
    private int mRatioWidth = -1;
    private int mRatioHeight = -1;
    private String mRatioStandard = "w";
    private boolean mIsWidthHeightEvenNumber = false;

    public RatioRelativeLayout(Context context) {
        super(context);
    }

    public RatioRelativeLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RatioRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a =
                context.getTheme().obtainStyledAttributes(attrs, R.styleable.Musv_Video_Album_RatioRelativeLayout, defStyleAttr, 0);
        mRatioWidth = a.getInteger(R.styleable.Musv_Video_Album_RatioRelativeLayout_ratioWidth, -1);
        mRatioHeight = a.getInteger(R.styleable.Musv_Video_Album_RatioRelativeLayout_ratioHeight, -1);
        mRatioStandard = a.getString(R.styleable.Musv_Video_Album_RatioRelativeLayout_standard);
        mIsWidthHeightEvenNumber = a.getBoolean(R.styleable.Musv_Video_Album_RatioRelativeLayout_isWidthHeightEvenNumber, false);
        a.recycle();
    }


    // ===========================================================
    // Override Methods
    // ===========================================================

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec));

        // Children are just made to fill our space.
        if (mRatioStandard.length() > 0 && mRatioWidth != -1 && mRatioHeight != -1) {
            int width = 0, height = 0;
            if (mRatioStandard.equals("w")) {// 以宽为标准
                width = getMeasuredWidth();
                height = width * mRatioHeight / mRatioWidth;

            } else if (mRatioStandard.equals("h")) {// 以高为标准
                height = getMeasuredHeight();
                width = height * mRatioWidth / mRatioHeight;
            }
            if (mIsWidthHeightEvenNumber) {
                if (width % 2 != 0) {
                    width++;
                }
                if (height % 2 != 0) {
                    height++;
                }
            }
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    // ===========================================================
    // Define Methods
    // ===========================================================


    // ===========================================================
    // Inner and Anonymous Classes
    // ===========================================================


    // ===========================================================
    // Getter & Setter
    // ===========================================================

}