Android视频图片缩略图的获取

Android视频图片缩略图的获取

这次项目中使用到拍照和拍摄视频的功能,那么既然是拍照和拍视频肯定涉及到缩略图的处理。所以接下来我们要学习下载android中如何处理图片和视频来获取我们需要的缩略图。幸运的是,android已经给我们提供好了具体的工具类,所以我们只需要学习这么工具类的api如何使用。

现在直接切入正题,对于获取视频缩略图,android系统中提供了ThumbnailUtilsandroid.provider.MediaStore.Images.Thumbnailsandroid.provider.MediaStore.Video.ThumbnailsMediaMetadataRetriever几个类可以使用,在这篇文章中,我仅仅对ThumbnailsUtils进行分析,肯能后续文章会介绍下后面三个类。

ThumbnailUtils

ThumbnailUtils方法是在android2.2(api8)之后新增的一个,该类为我们提供了三个静态方法供我们使用。

  • ThumbnailUtils.createVideoThumbnail(filePath, kind)
    创建视频缩略图,filePath:文件路径,kind:MINI_KIND or MICRO_KIND
  • ThumbnailUtils.extractThumbnail(bitmap, width, height)
    将bitmap裁剪为指定的大小
  • ThumbnailUtils.extractThumbnail(bitmap, width, height, options):将bitmap裁剪为指定的大小,可以有参数BitmapFactory.Options参数

下面我们分别介绍下这三个方法:
(1)、createVideoThumbnail
我们先看看它的源码:

  /** 
     * Create a video thumbnail for a video. May return null if the video is 
     * corrupt or the format is not supported. 
     * 
     * @param filePath the path of video file 
     * @param kind could be MINI_KIND or MICRO_KIND 
     */  
    public static Bitmap createVideoThumbnail(String filePath, int kind) {  
        Bitmap bitmap = null;  
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();  
        try {  
            retriever.setDataSource(filePath);  
            bitmap = retriever.getFrameAtTime(-1);  
        } catch (IllegalArgumentException ex) {  
            // Assume this is a corrupt video file  
        } catch (RuntimeException ex) {  
            // Assume this is a corrupt video file.  
        } finally {  
            try {  
                retriever.release();  
            } catch (RuntimeException ex) {  
                // Ignore failures while cleaning up.  
            }  
        }  

        if (bitmap == null) return null;  

        if (kind == Images.Thumbnails.MINI_KIND) {  
            // Scale down the bitmap if it's too large.  
            int width = bitmap.getWidth();  
            int height = bitmap.getHeight();  
            int max = Math.max(width, height);  
            if (max > 512) {  
                float scale = 512f / max;  
                int w = Math.round(scale * width);  
                int h = Math.round(scale * height);  
                bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);  
            }  
        } else if (kind == Images.Thumbnails.MICRO_KIND) {  
            bitmap = extractThumbnail(bitmap,  
                    TARGET_SIZE_MICRO_THUMBNAIL,  
                    TARGET_SIZE_MICRO_THUMBNAIL,  
                    OPTIONS_RECYCLE_INPUT);  
        }  
        return bitmap;  
    }  

通过观看源码:我们发现该方法的内部也是使用了一个MediaMetadataRetriever的对象,那这个对象究竟是何方神圣呢?容我们稍后再说,反正就是通过这个对象获得了一个bitmap对象,然后再根据kind的类型进行图片的压缩。源码的总体思路就是这样。
参数说明:

  • filePath表示视频文件路径
  • kind表示类型,可以有两个选项,分别是Images.Thumbnails.MICRO_KIND和Images.Thumbnails.MINI_KIND,其中,MINI_KIND: 512 x 384,MICRO_KIND: 96 x 96,当然读了代码你会发现,你也可以传入任意的int型数字,只是不起作用罢了。

(2)、extractThumbnail(Bitmap source, int width, int height):进行图片的裁剪
我们先看看源码:

 /** 
     * Creates a centered bitmap of the desired size. 
     * 
     * @param source original bitmap source 
     * @param width targeted width 
     * @param height targeted height 
     */  
    public static Bitmap extractThumbnail(  
            Bitmap source, int width, int height) {  
        return extractThumbnail(source, width, height, OPTIONS_NONE);  
    }

源码很简单,就是调用了 extractThumbnail的兄弟。
参数说明:

  • source:表示图片源文件(Bitmap类型)
  • width:表示压缩成后的宽度
  • height:表示压缩成后的高度

(3)、extractThumbnail( Bitmap source, int width, int height, int options):进行图片的额裁剪,指定options选项。
看看源码:

/** 
     * Creates a centered bitmap of the desired size. 
     * 
     * @param source original bitmap source 
     * @param width targeted width 
     * @param height targeted height 
     * @param options options used during thumbnail extraction 
     */  
      public static Bitmap extractThumbnail(  
                Bitmap source, int width, int height, int options) {  
            if (source == null) {  
                return null;  
            }  

            float scale;  
            if (source.getWidth() < source.getHeight()) {  
                scale = width / (float) source.getWidth();  
            } else {  
                scale = height / (float) source.getHeight();  
            }  
            Matrix matrix = new Matrix();  
            matrix.setScale(scale, scale);  
            Bitmap thumbnail = transform(matrix, source, width, height,  
                    OPTIONS_SCALE_UP | options);  
            return thumbnail;  
     }  

源码中的处理采用Matrix对象进行变换操作,也不是很复杂,对Matrix不是很熟悉的同学可以搜下用法。在我们自定义view中还是很有用途的。
参数说明:

  • source:表示图片源文件(Bitmap类型)
  • width:表示压缩成后的宽度
  • height:表示压缩成后的高度
  • options:表示缩略图抽取时的选项,如果options定义为OPTIONS_RECYCLE_INPUT,则回收@param source这个资源文件(除非缩略图等于@param source)

ThumbnailUtils类的核心就是这三个方法,我们只需要知道如何使用即可。更多内容参考欧阳鹏写的这篇文章

下面我们通过一个案例来介绍下它的使用。案例的需求就是:调用系统的相机进行拍照和拍摄视频功能,然后展示缩略图。需求很简单,核心就是利用ThumbnailUtils工具类进行缩略图的处理。现在我们开始来完成这个需求。我们在eclipse中创建工程PicturePhotoDemo。

1、首先进行我们的主页面布局搭建:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.dsw.picturephotodemo.MainActivity" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:textSize="20sp"
        android:padding="10dp"
        android:text="@string/hello_world" />

    <LinearLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:gravity="center_horizontal"
        android:orientation="horizontal">
        <Button 
            android:id="@+id/take_picture"
            android:layout_height="wrap_content"
            android:layout_width="100dp"
            android:text="@string/take_picture"
            android:textSize="17sp"/>
        <Button 
            android:id="@+id/take_video"
            android:layout_height="wrap_content"
            android:layout_width="100dp"
            android:text="@string/take_video"
            android:textSize="17sp"/>
    </LinearLayout>
    <com.dsw.horizonlistview.HorizontalListView
        android:id="@+id/horizontalListView"
        android:layout_width="match_parent"
        android:layout_gravity="center_vertical"
        android:spacing="5dp"
        android:layout_height="100dp"
        android:scrollbars="@null">

    </com.dsw.horizonlistview.HorizontalListView>
    </LinearLayout>

效果图如下:
layout

在上面的布局中,我们使用了一个自定义View名为HorizontalListView(一个大牛写的,拿来用了)。HorizontalListView的源码就不贴了,太多了,而且不是我们的重点,有兴趣研究的同学可以稍后下载demo工程,在工程中有。我们只需知道HorizontalListView是一个横向的ListView,使用方法同ListView,同样需要Adapter的使用。

2、我们新建一个MeadiaInformation的实体,用于存储我们的照片信息。

public class MeadiaInformation {
        //图片路径
        public String srcPath;
        //图片:type=0 视频:type=1
        public int type;
        //bitmap资源图片
        public Bitmap bitmap;
    }

3、我们自定义HorizontalListViewAdapter适配器,用于处理我们的图片展示。

public class HorizontalListViewAdapter extends BaseAdapter{
        private Context mContext;
        private LayoutInflater mInflater;
        Bitmap iconBitmap;
        private int selectIndex;
        private List<MeadiaInformation> list;
        public HorizontalListViewAdapter(Context context, List<MeadiaInformation> list){
            this.mContext = context;
            this.list = list;
            mInflater=(LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);//LayoutInflater.from(mContext);
        }
        @Override
        public int getCount() {
            return list == null ? 0 : list.size();
        }
        @Override
        public Object getItem(int position) {
            return list == null ? null : list.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
            if(convertView==null){
                holder = new ViewHolder();
                convertView = mInflater.inflate(R.layout.horizontal_list_item, null);
                holder.mImage=(ImageView)convertView.findViewById(R.id.img_list_item);
                holder.mVideoImage = (ImageView) convertView.findViewById(R.id.img_video);
                convertView.setTag(holder);
            }else{
                holder=(ViewHolder)convertView.getTag();
            }

            //判断当前项是否为选中项
            if(position == selectIndex){
                convertView.setSelected(true);
            }else{
                convertView.setSelected(false);
            }
            if(list != null && list.size() > 0){
                iconBitmap = list.get(position).bitmap;
                holder.mImage.setImageBitmap(iconBitmap);
                if(list.get(position).type == 1){//如果是视频,就显示视频播放按钮
                    holder.mVideoImage.setVisibility(View.VISIBLE);
                }else{//如果不是视频就不显示该播放按钮
                    holder.mVideoImage.setVisibility(View.INVISIBLE);
                }
            }
            return convertView;
        }

        private static class ViewHolder {
            //展示图片的额ImageView
            private ImageView mImage;
            //展示视频中间的播放图片
            private ImageView mVideoImage;
        }

        /**
         * 添加展示项
         * @param infor
         */
        public void addInformation(MeadiaInformation infor){
            list.add(infor);
            notifyDataSetChanged();
        }

        /**
         * 添加音频集合信息
         * @param listInfo
         */
        public void addInformationList(List<MeadiaInformation> listInfo){
            list.addAll(listInfo);
            notifyDataSetChanged();
        }

        /**
         * 添加选中的item
         * @param i
         */
        public void setSelectIndex(int i){
            selectIndex = i;
        }
        /**
         * 获取当前选中项
         * @return
         */
        public int getSelectIndex(){
            return this.selectIndex;
        }
    }

3、万事具备,我们需要在MainActivity中处理我们的拍照逻辑,然后处理缩略图。本来我是想贴处理那部分的代码的,但是感觉逻辑有点接不上了,所以还是把MainActivity都贴出来吧!

public class MainActivity extends Activity {
        //拍照的请求码
        private static final int REQUEST_TAKE_PITURE = 100;
        //拍视频的请求码
        private static final int REQUEST_TAKE_VIDEO = 200;
        private MainActivity _this;
        //拍照按钮
        private Button btn_takePicture;
        //拍视频按钮
        private Button btn_takeVideo;
        //文件存储路径
        private String path;
        //文件file
        private File photoFile;
        //图片展示的ListView
        private HorizontalListView listView;
        //ListView的适配器
        private HorizontalListViewAdapter listViewAdapter;
        //构造的多媒体对象
        private MeadiaInformation infor;
        private DisplayMetrics metrics;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            _this =this;
            btn_takePicture = (Button) findViewById(R.id.take_picture);
            btn_takeVideo = (Button) findViewById(R.id.take_video);
            listView = (HorizontalListView) findViewById(R.id.horizontalListView);
            metrics = getResources().getDisplayMetrics();
            setOnListener();
            initAdapter();
            initPath();
        }

        /**
         * 初始化存储路径
         */
        private void initPath(){
            //判断是否有存储卡
            if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
                //有存储卡获取路径
                path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/PhotoDemo/";
            }else{
                //没有存储卡的时候,存储到这个路径
                path = getApplicationContext().getFilesDir().getPath()+ "/PhotoDemo/";
            }
            File file = new File(path);
            if(!file.exists()){
                file.mkdirs();
            }
        }

        /**
         * 设置适配器的初始化
         */
        private void initAdapter(){
            List<MeadiaInformation> list = new ArrayList<MeadiaInformation>();
            listViewAdapter = new HorizontalListViewAdapter(_this, list);
            listView.setAdapter(listViewAdapter);
        }

        /**
         * 设置控件监听
         */
        private void setOnListener(){
            btn_takePicture.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    Date date = new Date();
                    String name = path + "/" + date.getTime() + ".jpg";
                    photoFile = new File(name);
                    Uri uri = Uri.fromFile(photoFile);
                    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
                    intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 10);
                    //启动照相
                    startActivityForResult(intent, REQUEST_TAKE_PITURE);
                }
            });

            btn_takeVideo.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    Date date = new Date();
                    String name = path + "/" + date.getTime() + ".3gp";
                    photoFile = new File(name);
                    Uri uri = Uri.fromFile(photoFile);
                    Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
                    intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 10);
                    //启动照相
                    startActivityForResult(intent, REQUEST_TAKE_VIDEO);
                }
            });

            listView.setOnItemClickListener(new OnItemClickListener() {

                @Override
                public void onItemClick(AdapterView<?> adapterView, View view, int position,
                        long arg3) {
                    listViewAdapter.setSelectIndex(position);
                    listViewAdapter.notifyDataSetChanged();
                }
            });
        }

        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            //通过requestCode判断类型,通过resultCode判断是否成功
            if(resultCode == RESULT_OK){
                infor = new MeadiaInformation();
                infor.srcPath = photoFile.getAbsolutePath();
                switch(requestCode){
                case REQUEST_TAKE_PITURE:
                    infor.type =0;
                    break;
                case REQUEST_TAKE_VIDEO:
                    infor.type =1;
                    break;
                }
                getBitmapFromFile();
                //将此MeadiaInformation添加到Adapter
                listViewAdapter.addInformation(infor);
            }
        }

        //根据文件路径获取缩略图
        private void getBitmapFromFile() {
            /**
             * android系统中为我们提供了ThumbnailUtils工具类来获取缩略图的处理。
             * ThumbnailUtils.createVideoThumbnail(filePath, kind)
             *          创建视频缩略图,filePath:文件路径,kind:MINI_KIND or MICRO_KIND  
             * ThumbnailUtils.extractThumbnail(bitmap, width, height)
             *          将bitmap裁剪为指定的大小
             * ThumbnailUtils.extractThumbnail(bitmap, width, height, options)
             *          将bitmap裁剪为指定的大小,可以有参数BitmapFactory.Options参数
             * 
             */
            Bitmap bitmap = null;
            if(infor.type == 0){//若果是图片,即拍照
                //直接通过路径利用BitmapFactory来形成bitmap
                bitmap = BitmapFactory.decodeFile(infor.srcPath);
            }else if(infor.type == 1){//如果是视频,即拍摄视频
                //利用ThumnailUtils
                bitmap = ThumbnailUtils.createVideoThumbnail(infor.srcPath, Images.Thumbnails.MINI_KIND);
            }

            //获取图片后,我们队图片进行压缩,获取指定大小
            if(bitmap != null){
                //裁剪大小
                bitmap = ThumbnailUtils.extractThumbnail(bitmap, (int)(100*metrics.density), (int)(100*metrics.density));
            }else{//如果为空,采用我们的默认图片
                bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
            }
            infor.bitmap = bitmap;
        }
    }

在MainActivity中我们点击【拍照】、【拍视频】按钮进行拍照和拍视屏,然后重写onActivityResult方法,进行拍摄回来的视频处理,接着最后就是对图片或视频进行获取缩略图处理,最后添加到listviewAdatper中进行展示。总体的处理逻辑就是这样,代码注释的也很详细,有兴趣的同学可以下载demo玩玩。先贴几张效果图:

photo1
photo2

源码下载

========================================
作者:mr_dsw 欢迎转载,与人分享是进步的源泉!

转载请保留地址:http://blog.csdn.net/mr_dsw

  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值