GridView展示图片与视频:区分显示及添加视频时长的技术求助
解决Gallery中图片/视频区分及视频时长显示问题
咱们一步一步来搞定这个问题——核心思路就是先从数据源区分媒体类型,再在GridView的Item里针对性展示标识和时长,话不多说直接上方案:
第一步:完善数据模型,区分图片与视频
首先得给每个媒体文件建个数据类,存路径、类型、时长这些关键信息:
public class MediaItem { private String filePath; private boolean isVideo; private long durationMs; // 视频时长(毫秒) // 构造方法、Getter/Setter public MediaItem(String filePath, boolean isVideo, long durationMs) { this.filePath = filePath; this.isVideo = isVideo; this.durationMs = durationMs; } public String getFilePath() { return filePath; } public boolean isVideo() { return isVideo; } public long getDurationMs() { return durationMs; } }
然后在加载媒体文件时,通过MediaStore(比直接扫文件更可靠)判断类型并获取视频时长:
private void loadMediaContent() { List<MediaItem> mediaList = new ArrayList<>(); // 查询图片+视频的投影字段 String[] projection = { MediaStore.Files.FileColumns.DATA, MediaStore.Files.FileColumns.MEDIA_TYPE, MediaStore.Video.Media.DURATION }; // 筛选条件:只取图片和视频 String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "=" + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE + " OR " + MediaStore.Files.FileColumns.MEDIA_TYPE + "=" + MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO; Cursor cursor = getContentResolver().query( MediaStore.Files.getContentUri("external"), projection, selection, null, MediaStore.Files.FileColumns.DATE_ADDED + " DESC" ); if (cursor != null) { while (cursor.moveToNext()) { String path = cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA)); int mediaType = cursor.getInt(cursor.getColumnIndex(MediaStore.Files.FileColumns.MEDIA_TYPE)); boolean isVideo = mediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO; long duration = isVideo ? cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media.DURATION)) : 0; mediaList.add(new MediaItem(path, isVideo, duration)); } cursor.close(); } // 把数据传给Adapter更新UI ((MediaGridAdapter) yourGridView.getAdapter()).updateData(mediaList); }
第二步:修改GridView的Item布局
给Item加播放图标(仅视频显示)和时长文本(仅视频显示),比如item_media_grid.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 媒体缩略图 --> <ImageView android:id="@+id/iv_thumbnail" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop"/> <!-- 视频播放图标 --> <ImageView android:id="@+id/iv_play_mark" android:layout_width="48dp" android:layout_height="48dp" android:layout_centerInParent="true" android:src="@drawable/ic_play_circle" android:visibility="gone"/> <!-- 视频时长文本 --> <TextView android:id="@+id/tv_duration" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentEnd="true" android:padding="4dp" android:background="#80000000" android:textColor="#FFFFFF" android:textSize="12sp" android:visibility="gone"/> </RelativeLayout>
第三步:升级GridView Adapter的逻辑
在Adapter里根据MediaItem的类型控制视图显示,同时格式化时长:
public class MediaGridAdapter extends BaseAdapter { private Context mContext; private List<MediaItem> mMediaList; private Glide mGlide; // 用Glide加载缩略图,效率更高 public MediaGridAdapter(Context context) { mContext = context; mMediaList = new ArrayList<>(); mGlide = Glide.with(context); } // 更新数据的方法 public void updateData(List<MediaItem> newList) { mMediaList.clear(); mMediaList.addAll(newList); notifyDataSetChanged(); } // 省略getCount、getItem、getItemId方法 @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(mContext).inflate(R.layout.item_media_grid, parent, false); holder = new ViewHolder(); holder.ivThumbnail = convertView.findViewById(R.id.iv_thumbnail); holder.ivPlayMark = convertView.findViewById(R.id.iv_play_mark); holder.tvDuration = convertView.findViewById(R.id.tv_duration); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } MediaItem item = mMediaList.get(position); // 加载缩略图:Glide自动支持视频第一帧加载 mGlide.load(new File(item.getFilePath())) .placeholder(R.drawable.ic_media_placeholder) .into(holder.ivThumbnail); if (item.isVideo()) { // 显示视频专属元素 holder.ivPlayMark.setVisibility(View.VISIBLE); holder.tvDuration.setVisibility(View.VISIBLE); holder.tvDuration.setText(formatDuration(item.getDurationMs())); } else { // 隐藏视频元素,只显示图片 holder.ivPlayMark.setVisibility(View.GONE); holder.tvDuration.setVisibility(View.GONE); } return convertView; } // 格式化时长为 mm:ss 格式 private String formatDuration(long durationMs) { long seconds = durationMs / 1000; long minutes = seconds / 60; seconds = seconds % 60; return String.format("%02d:%02d", minutes, seconds); } static class ViewHolder { ImageView ivThumbnail; ImageView ivPlayMark; TextView tvDuration; } }
第四步:在Gallery Activity中初始化
把Adapter和GridView绑定,然后加载媒体内容:
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_share); // ... 其他初始化代码(比如Spinner、BottomNavigation) GridView mediaGridView = findViewById(R.id.your_grid_view_id); MediaGridAdapter adapter = new MediaGridAdapter(this); mediaGridView.setAdapter(adapter); // 记得动态申请存储权限(Android 6.0+) if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { loadMediaContent(); } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1001); } } // 权限回调 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 1001 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { loadMediaContent(); } }
额外优化小Tips
- 用Glide代替Picasso,Glide对视频缩略图的支持更稳定,还能缓存提升加载速度
- 如果追求极致性能,可以用MediaStore提供的缩略图接口,避免加载完整文件
- 可以给视频Item加个轻微的圆角或者边框,进一步区分图片
内容的提问来源于stack exchange,提问作者lllllllllllllll
相关产品推荐
相关产品推荐

