Android 获取相册媒体文件并轮播(图片展示、视频播放)

文末附源码:

先看最终实现的效果吧:

 csdn限制图片大小不得超过5M,所以图片看着有点糊

关于本文中将要讲到的实现,我觉得可以先单拎出来一些内容:

(1)Android 获取系统相册所有的图片以及视频资源

工具类:MediaHelper.java
public class MediaHelper {
    private static final String[] MEDIA_COLUMNS = {
            MediaStore.Images.Media._ID,
            MediaStore.Images.Media.DATA,
            MediaStore.Images.Media.DATE_ADDED,
            MediaStore.Images.Media.DISPLAY_NAME,
            MediaStore.Images.Media.SIZE,
            MediaStore.Images.Media.MIME_TYPE,
            MediaStore.Images.Media.ORIENTATION,
            MediaStore.Video.VideoColumns.DURATION
    };

    public static List<MediaItem> getMediaList(Context context) {
        List<MediaItem> mediaList = new ArrayList<>();
        ContentResolver contentResolver = context.getContentResolver();
        String sortOrder = MediaStore.Images.Media.DATE_ADDED + " DESC";

        // Get images
        Cursor imageCursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                MEDIA_COLUMNS, null, null, sortOrder);
        if (imageCursor != null && imageCursor.moveToFirst()) {
            do {
                MediaItem item = new MediaItem();
                item.setId(imageCursor.getLong(imageCursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)));
                item.setFilePath(imageCursor.getString(imageCursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)));
                item.setDisplayName(imageCursor.getString(imageCursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)));
                item.setSize(imageCursor.getLong(imageCursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE)));
                item.setMimeType(imageCursor.getString(imageCursor.getColumnIndexOrThrow(MediaStore.Images.Media.MIME_TYPE)));
                item.setOrientation(imageCursor.getInt(imageCursor.getColumnIndexOrThrow(MediaStore.Images.Media.ORIENTATION)));
                mediaList.add(item);
            } while (imageCursor.moveToNext());
            imageCursor.close();
        }

        // Get videos
        Cursor videoCursor = contentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
                MEDIA_COLUMNS, null, null, sortOrder);
        if (videoCursor != null && videoCursor.moveToFirst()) {
            do {
                MediaItem item = new MediaItem();
                item.setId(videoCursor.getLong(videoCursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)));
                item.setFilePath(videoCursor.getString(videoCursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)));
                item.setDisplayName(videoCursor.getString(videoCursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)));
                item.setSize(videoCursor.getLong(videoCursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE)));
                item.setMimeType(videoCursor.getString(videoCursor.getColumnIndexOrThrow(MediaStore.Images.Media.MIME_TYPE)));
                item.setDuration(videoCursor.getLong(videoCursor.getColumnIndexOrThrow(MediaStore.Video.VideoColumns.DURATION)));
                mediaList.add(item);
            } while (videoCursor.moveToNext());
            videoCursor.close();
        }

        return mediaList;
    }
}
实体类:MediaItem.java
public class MediaItem implements Serializable {
    private long id;
    private String filePath;
    private String displayName;
    private long size;
    private String mimeType;
    private int orientation;
    private long duration;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getFilePath() {
        return filePath;
    }

    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }

    public String getDisplayName() {
        return displayName;
    }

    public void setDisplayName(String displayName) {
        this.displayName = displayName;
    }

    public long getSize() {
        return size;
    }

    public void setSize(long size) {
        this.size = size;
    }

    public String getMimeType() {
        return mimeType;
    }

    public void setMimeType(String mimeType) {
        this.mimeType = mimeType;
    }

    public int getOrientation() {
        return orientation;
    }

    public void setOrientation(int orientation) {
        this.orientation = orientation;
    }

    public long getDuration() {
        return duration;
    }

    public void setDuration(long duration) {
        this.duration = duration;
    }

    @Override
    public String toString() {
        return "MediaItem{" +
                "id=" + id +
                ", filePath='" + filePath + '\'' +
                ", displayName='" + displayName + '\'' +
                ", size=" + size +
                ", mimeType='" + mimeType + '\'' +
                ", orientation=" + orientation +
                ", duration=" + duration +
                '}';
    }
}

实际调用就很简单了,直接获取列表即可

private List<MediaItem> mediaList;
mediaList = MediaHelper.getMediaList(this);
for (MediaItem item : mediaList) {
    Log.d("MediaHelper", "id: " + item.getId() + ", info: " + item.toString());
}

(2)利用Viewpagger2+fragment实现分页滑动效果,这部分可以参考这一篇博客

上面两步,基本上就实现了获取所有的数据,并且也把具体的UI框架搭起来了,那么接下来就是要分别判断媒体类型,来做不同的展示了,图片,就是普通的展示,视频,就是播放。

(3)图片展示

       引用三方库:

implementation 'com.github.chrisbanes:PhotoView:2.3.0'
<com.github.chrisbanes.photoview.PhotoView
        android:id="@+id/photoView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
binding.photoView.setImageURI(Uri.parse(mediaItem.getFilePath()));

(4)视频播放控制

引用aar,如图(文件可从demo中获取)

 初始化

binding.videoView.setZOrderOnTop(false);
initMediaPlayer();

private void initMediaPlayer() {
        if (mMediaPlayer == null) {
            mMediaPlayer = new IjkMediaPlayer();
            mMediaPlayer.setOnPreparedListener(this);
            try {
                // 使用本地视频
                mMediaPlayer.setDataSource(getActivity(), Uri.parse(mediaItem.getFilePath()));
            } catch (Exception e) {
                e.printStackTrace();
                Toast.makeText(getActivity(), "视频无法正常打开,请重新尝试", Toast.LENGTH_SHORT).show();
            }
            mMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
        }
    }

使用:

实现视频播放相关接口

implements SurfaceHolder.Callback, IMediaPlayer.OnPreparedListener

重写方法 

@Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {
        mMediaPlayer.setDisplay(holder);
        try {
            // 使用prepareAsync,防止阻塞主线程
            mMediaPlayer.prepareAsync();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {

    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {

    }

    @Override
    public void onPrepared(IMediaPlayer mp) {
        binding.videoView.setAspectRatio(mp.getVideoWidth(), mp.getVideoHeight());
    }

生命周期调用

@Override
    public void onStart() {
        super.onStart();
        binding.videoView.getHolder().addCallback(this);
        binding.videoView.setOnClickListener(v -> {
            if (NoDoubleClick.isDoubleClick()) {
                return;
            }
            if (isPlaying) {
                isPlaying = false;
                mMediaPlayer.pause();
                Toast.makeText(getActivity(), "停止播放!", Toast.LENGTH_SHORT).show();
            } else {
                isPlaying = true;
                mMediaPlayer.start();
                Toast.makeText(getActivity(), "开始播放!", Toast.LENGTH_SHORT).show();
            }
        });
    }
@Override
    public void onStop() {
        super.onStop();
        binding.videoView.getHolder().removeCallback(this);
        releaseMediaPlayer();
    }

    private void releaseMediaPlayer() {
        if (mMediaPlayer != null) {
            mMediaPlayer.reset();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

其实,至此,走完(1)(2)(3)这三部分,基本上就完成了本文中完成的效果了,笔者在写demo的时候也基本上是遵循此顺序完成的。

源码demo

猜你喜欢

转载自blog.csdn.net/weixin_53324308/article/details/130924713