安卓加载视频缩略图,展示于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布局,左边展示视频缩略图,右边展示视频大小,中间展示视频名称和时长











    

    
    

    






其中的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 lruCache;

public MyVideoThumbLoader() {
    int maxMemory = (int) Runtime.getRuntime().maxMemory();// 获取最大的运行内存
    int maxSize = maxMemory / 4;
    // 拿到缓存的内存大小 
    lruCache = new LruCache(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 {
    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

你可能感兴趣的:(安卓)