Android影片圖片縮圖的獲取
這是我參與11月更文挑戰的第6天,活動詳情檢視:2021最後一次更文挑戰
Android影片圖片縮圖的獲取
這次專案中使用到拍照和拍攝影片的功能,那麼既然是拍照和拍影片肯定涉及到縮圖的處理。所以接下來我們要學習下載android中如何處理圖片和影片來獲取我們需要的縮圖。幸運的是,android已經給我們提供好了具體的工具類,所以我們只需要學習這麼工具類的api如何使用。
現在直接切入正題,對於獲取影片縮圖,android系統中提供了ThumbnailUtils、android.provider.MediaStore.Images.Thumbnails、android.provider.MediaStore.Video.Thumbnails、MediaMetadataRetriever幾個類可以使用,在這篇文章中,我僅僅對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、首先進行我們的主頁面佈局搭建:
```
<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>
```
效果圖如下:
在上面的佈局中,我們使用了一個自定義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
@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玩玩。先貼幾張效果圖: