安卓加载视频缩略图,展示于ListView中,完美实现

安卓获取视频缩略图,展示于ListView中,完美实现

本篇博客为原创,来自于vitamio,转载请注明出处。

应用场景:

获取安卓手机外部存储视频列表,适配器继承至CursorAdapter,利用ViewHolder进行优化;并利用异步加载和缓存机制,在加上一个绑定TAG机制。在ListView中展示视频某一帧的图片,视频名称,视频大小以及视频时长。

分析说明:

在ListView中展示视频某一帧的画面,有以下几种方式。
1.从媒体库中查询

2. android 2.2以后使用ThumbnailUtils类获取

3.调用jni文件,实现MediaMetadataRetriever类

三种方法各有利弊:

第一种方法,新视频增加后需要SDCard重新扫描才能给新增加的文件添加缩略图,灵活性差,而且不是很稳定,适合简单应用

第二种方法,实现简单,但2.2以前的版本不支持

第三种方法,实现复杂,但比较灵活

为了在UI界面中展示视频缩略图不卡顿,不乱跳,不重复加载,简单方便的前提下,我选择第二种方式实现。

先定义一个MyVideoCursorAdapter类继承至CursorAdapter
package cn.lsj.mypalyer.adapter;

import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
import android.text.format.Formatter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import cn.lsj.mypalyer.R;
import cn.lsj.mypalyer.bean.VideoBean;
import cn.lsj.mypalyer.utils.MyUtils;
import cn.lsj.mypalyer.utils.MyVideoThumbLoader;
import cn.lsj.mypalyer.view.MyImageView;

public class MyVideoCursorAdapter extends CursorAdapter {

private MyVideoThumbLoader mVideoThumbLoader;

public MyVideoCursorAdapter(Context context, Cursor c) {
    super(context, c);
    mVideoThumbLoader = new MyVideoThumbLoader();// 初始化缩略图载入方法
}

@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {

    final VideoBean vb = VideoBean.getInstance(cursor);

    View view = View.inflate(mContext, R.layout.video_list_item, null);

    ViewHolder vh = new ViewHolder();

    vh.title = (TextView) view.findViewById(R.id.video_list_item_tv_title);
    vh.duration = (TextView) view
            .findViewById(R.id.video_list_item_tv_duration);
    vh.size = (TextView) view.findViewById(R.id.video_list_item_tv_size);
    vh.iv = (MyImageView) view.findViewById(R.id.iv);

    view.setTag(vh);

    vh.iv.setTag(vb.path);
    return view;
}

@Override
public void bindView(View view, final Context context, Cursor cursor) {

    final ViewHolder vh = (ViewHolder) view.getTag();
    final VideoBean vb = VideoBean.getInstance(cursor);

    vh.title.setText(vb.title);
    vh.duration.setText(MyUtils.DurationByMs(vb.duration));
    vh.size.setText(Formatter.formatFileSize(context, vb.size));

    if (vb.duration == 0) {
        vh.iv.setImageResource(R.drawable.btn_audio_play);
    } else {

        mVideoThumbLoader.showThumbByAsynctack(vb.path, vh.iv);
    }

}

    private class ViewHolder {
        private TextView title;
        private TextView duration;
        private TextView size;
        private MyImageView iv;
    }

}
其中的布局才用LinearLayout布局,左边展示视频缩略图,右边展示视频大小,中间展示视频名称和时长
<?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="match_parent"
android:background="@drawable/main_list_selector"
android:orientation="horizontal"
android:padding="6dp" >

<!-- icon -->

<cn.lsj.mypalyer.view.MyImageView
    android:id="@+id/iv"
    android:layout_width="45dp"
    android:layout_height="45dp"
    android:src="@drawable/btn_audio_play" />
<!-- 视频信息 -->

<LinearLayout
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="6dp"
    android:layout_marginTop="6dp"
    android:layout_weight="1"
    android:orientation="vertical" >

    <!-- 文件名 -->

    <TextView
        android:id="@+id/video_list_item_tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:text="文件名"
        android:textColor="@color/white"
        android:textSize="16sp" />
    <!-- 视频时长 -->

    <TextView
        android:id="@+id/video_list_item_tv_duration"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:text="时长"
        android:textColor="@color/halfwhite"
        android:textSize="14sp" />
</LinearLayout>
<!-- 文件大小 -->

<TextView
    android:id="@+id/video_list_item_tv_size"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="6dp"
    android:singleLine="true"
    android:text="文件大小"
    android:textColor="@color/halfwhite"
    android:textSize="16sp" />

</LinearLayout>
其中的cn.lsj.mypalyer.view.MyImageView是继承至ImageView,之所以要自定义,是因为在展示列表时,会报异常(这点很重要,后面会讲)
java.lang.IllegalArgumentException: Cannot draw recycled bitmapsat 
Android.view.GLES20Canvas.drawBitmap(GLES20Canvas.java:778)at 
android.view.GLES20RecordingCanvas.drawBitmap
(GLES20RecordingCanvas.java:117)at 
android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:393)at 
android.widget.ImageView.onDraw(ImageView.java:979)at 
android.view.View.draw(View.java:13458)at 
android.view.View.getDisplayList(View.java:12409)at     
android.view.View.getDisplayList(View.java:12453)at 
android.view.View.draw(View.java:13182)at 
android.view.ViewGroup.drawChild(ViewGroup.java:2929)at 
android.view.ViewGroup.dispatchDraw(ViewGroup.java:2799)at 
android.view.View.draw(View.java:13461)at 
android.view.View.getDisplayList(View.java:12409)at 
android.view.View.getDisplayList(View.java:12453)at 
android.view.View.draw(View.java:13182)at   
android.view.ViewGroup.drawChild(ViewGroup.java:2929)at 
android.widget.ListView.drawChild(ListView.java:3226)at 
android.view.ViewGroup.dispatchDraw(ViewGroup.java:2799)at  
android.widget.AbsListView.dispatchDraw(AbsListView.java:2433)at 
android.widget.ListView.dispatchDraw(ListView.java:3221)at  
android.view.View.draw(View.java:13461)at android.widget.AbsListView.draw
(AbsListView.java:3759)
为了解决这个异常,我自定义MyImageView类继承至ImageView
package cn.lsj.mypalyer.view;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.ImageView;

public class MyImageView extends ImageView {

public MyImageView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

public MyImageView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

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

@Override
protected void onDraw(Canvas canvas) {
    try {
        super.onDraw(canvas);
    } catch (Exception e) {
        System.out.println("trying to use a recycled bitmap");
    }
}
}
接下来,需要填充数据到列表展示
设置适配器代码如下:
MyVideoCursorAdapter adapter = new MyVideoCursorAdapter(mContext, null);
lv.setAdapter(adapter);
请求数据:
MyQueryHandler myQueryHandler = new MyQueryHandler(
            mContext.getContentResolver());

myQueryHandler.startQuery(100, adapter, Media.EXTERNAL_CONTENT_URI,
            new String[] { Media._ID, Media.TITLE, Media.DATA,
                    Media.DURATION, Media.SIZE }, null, null, null);
MyQueryHandler类继承至AsyncQueryHandler,用于异步请求数据
package cn.lsj.mypalyer.utils;

import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;

public class MyQueryHandler extends AsyncQueryHandler {

public MyQueryHandler(ContentResolver cr) {
    super(cr);
}

@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
    super.onQueryComplete(token, cookie, cursor);

    if (token == 100 && cookie instanceof CursorAdapter) {
        CursorAdapter adapter = (CursorAdapter) cookie;
        adapter.swapCursor(cursor);
    }

}

}
请求数据,然后展示在列表中,UI界面之所以不卡顿,是因为我用到了异步请求视频缩略图,异步加载和缓存,关键点就在MyVideoCursorAdapter类中,下面三行代码:
vh.iv.setTag(vb.path);
mVideoThumbLoader = new MyVideoThumbLoader();
mVideoThumbLoader.showThumbByAsynctack(vb.path, vh.iv);
自定义MyVideoThumbLoader类,主要的实现机制就是 异步加载 和 缓存机制 在加上一个绑定TAG机制
package cn.lsj.mypalyer.utils;

import android.graphics.Bitmap;
import android.media.ThumbnailUtils;
import android.os.AsyncTask;
import android.provider.MediaStore.Video.Thumbnails;
import android.support.v4.util.LruCache;
import cn.lsj.mypalyer.R;
import cn.lsj.mypalyer.activity.MainActivity;
import cn.lsj.mypalyer.view.MyImageView;

public class MyVideoThumbLoader {

// 创建cache
private LruCache<String, Bitmap> lruCache;

public MyVideoThumbLoader() {
    int maxMemory = (int) Runtime.getRuntime().maxMemory();// 获取最大的运行内存
    int maxSize = maxMemory / 4;
    // 拿到缓存的内存大小 
    lruCache = new LruCache<String, Bitmap>(maxSize) {
        @Override
        protected int sizeOf(String key, Bitmap value) {
            // 这个方法会在每次存入缓存的时候调用
            return value.getByteCount();
        }
    };
}

public void addVideoThumbToCache(String path, Bitmap bitmap) {
    if (getVideoThumbToCache(path) == null) {
        // 当前地址没有缓存时,就添加

        lruCache.put(path, bitmap);
    }
}

public Bitmap getVideoThumbToCache(String path) {

    return lruCache.get(path);

}

public void showThumbByAsynctack(String path, MyImageView imgview) {

    if (getVideoThumbToCache(path) == null) {
        // 异步加载
        new MyBobAsynctack(imgview, path).execute(path);
    } else {
        imgview.setImageBitmap(getVideoThumbToCache(path));
    }

}

class MyBobAsynctack extends AsyncTask<String, Void, Bitmap> {
    private MyImageView imgView;
    private String path;

    public MyBobAsynctack(MyImageView imageView, String path) {
        this.imgView = imageView;
        this.path = path;
    }

    @Override
    protected Bitmap doInBackground(String... params) {

        Bitmap bitmap = null;

        try {

            ThumbnailUtils tu = new ThumbnailUtils();

            bitmap = tu.createVideoThumbnail(params[0],
                    Thumbnails.MICRO_KIND);

            System.out.println("111111path: " + path + "  bitmap: "
                    + bitmap);

            if (bitmap == null) {

                bitmap = android.graphics.BitmapFactory.decodeResource(
                        MainActivity.mainActivity.getResources(),
                        R.drawable.btn_audio_play);

                System.out.println("5555555path: " + path + "  bitmap: "
                        + bitmap);

            }

            // //直接对Bitmap进行缩略操作,最后一个参数定义为OPTIONS_RECYCLE_INPUT ,来回收资源

            Bitmap bitmap2 = tu.extractThumbnail(bitmap, 50, 50,
                    ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
            System.out.println("path: " + path + "bitmap2: " + bitmap2);

            // 加入缓存中
            if (getVideoThumbToCache(params[0]) == null) {
                addVideoThumbToCache(path, bitmap2);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return bitmap;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (imgView.getTag().equals(path)) {// 通过 Tag可以绑定 图片地址和
                                            // imageView,这是解决Listview加载图片错位的解决办法之一
            imgView.setImageBitmap(bitmap);
        }
    }
}
}
这里一定要注意下面这几行行代码,因为得到bitmap有可能为空,当视频是mkv格式时,就会为null,所以加个判断,设置默认的图片。而且,ImageView要用自定义的MyImageView,否则会报异常java.lang.IllegalArgumentException: Cannot draw recycled bitmapsat
bitmap = tu.createVideoThumbnail(params[0],Thumbnails.MICRO_KIND);
if (bitmap == null) {

                bitmap = android.graphics.BitmapFactory.decodeResource(
                        MainActivity.mainActivity.getResources(),
                        R.drawable.btn_audio_play);

                System.out.println("5555555path: " + path + "  bitmap: "
                        + bitmap);

            }

至此,视频缩略图可以完美的展示于ListView中,不卡顿,不重复,不乱跳,并加入异步加载 和 缓存机制, 在加上一个绑定TAG机制。

本篇博客为原创,来自于vitamio,转载请注明出处http://blog.csdn.net/vitamio

  • 14
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
Android Studio使用Glide加载图片到ListView,需要遵循以下步骤: 1. 添加Glide依赖项 在项目的build.gradle文件添加以下依赖项: ``` dependencies { implementation 'com.github.bumptech.glide:glide:4.11.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' } ``` 2. 创建ListView的Adapter 创建一个继承BaseAdapter的Adapter类,并实现getView()方法。在getView()方法使用Glide加载图片。 ```java public class MyAdapter extends BaseAdapter { private Context context; private List<String> imageUrlList; public MyAdapter(Context context, List<String> imageUrlList) { this.context = context; this.imageUrlList = imageUrlList; } @Override public int getCount() { return imageUrlList.size(); } @Override public Object getItem(int position) { return imageUrlList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { convertView = LayoutInflater.from(context).inflate(R.layout.item_layout, parent, false); viewHolder = new ViewHolder(); viewHolder.imageView = convertView.findViewById(R.id.imageView); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } Glide.with(context) .load(imageUrlList.get(position)) .into(viewHolder.imageView); return convertView; } static class ViewHolder { ImageView imageView; } } ``` 3. 设置ListView的Adapter 在Activity或Fragment创建ListView并设置Adapter。 ```java public class MyActivity extends AppCompatActivity { private ListView listView; private MyAdapter adapter; private List<String> imageUrlList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); listView = findViewById(R.id.listView); imageUrlList = new ArrayList<>(); imageUrlList.add("https://example.com/image1.jpg"); imageUrlList.add("https://example.com/image2.jpg"); imageUrlList.add("https://example.com/image3.jpg"); adapter = new MyAdapter(this, imageUrlList); listView.setAdapter(adapter); } } ``` 以上就是在Android Studio使用Glide加载图片到ListView的步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值