編輯:Android開發實例
我研究了下微信的本地圖片選擇的Demo,自己仿照的寫了下分享給大家,希望對以後有這樣子需求的朋友有一點幫助吧,主要使用的是ContentProvider掃描手機中的圖片,並用GridView將圖片顯示出來,關於GridView和ListView顯示圖片的問題,一直是一個很頭疼的問題,因為我們手機的內存有限,手機給每個應用程序分配的內存也有限,所以圖片多的情況下很容易伴隨著OOM的發生,不過現在也有很多的開源的圖片顯示框架,大家有興趣的可以去了解了解,今天我的這篇文章使用的是LruCache這個類(http://www.fengfly.com/plus/view-214548-1.html)以及對圖片進行相對應的裁剪,這樣也可以盡量的避免OOM的發生,我們先看下微信的效果吧
接下來我們就來實現這些效果吧,首先我們新建一個項目,取名ImageScan
首先我們先看第一個界面吧,使用將手機中的圖片掃描出來,然後根據圖片的所在的文件夾將其分類出來,並顯示所在文件夾裡面的一張圖片和文件夾中圖片個數,我們根據界面元素(文件夾名, 文件夾圖片個數,文件夾中的一張圖片)使用一個實體對象ImageBean來封裝這三個屬性
- package com.example.imagescan;
- /**
- * GridView的每個item的數據對象
- *
- * @author len
- *
- */
- public class ImageBean{
- /**
- * 文件夾的第一張圖片路徑
- */
- private String topImagePath;
- /**
- * 文件夾名
- */
- private String folderName;
- /**
- * 文件夾中的圖片數
- */
- private int imageCounts;
- public String getTopImagePath() {
- return topImagePath;
- }
- public void setTopImagePath(String topImagePath) {
- this.topImagePath = topImagePath;
- }
- public String getFolderName() {
- return folderName;
- }
- public void setFolderName(String folderName) {
- this.folderName = folderName;
- }
- public int getImageCounts() {
- return imageCounts;
- }
- public void setImageCounts(int imageCounts) {
- this.imageCounts = imageCounts;
- }
- }
接下來就是主界面的布局啦,上面的導航欄我沒有加進去,只有下面的GridView,所以說主界面布局中只有一個GridView
- <RelativeLayout 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" >
- <GridView
- android:id="@+id/main_grid"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:listSelector="@android:color/transparent"
- android:cacheColorHint="@android:color/transparent"
- android:stretchMode="columnWidth"
- android:horizontalSpacing="20dip"
- android:gravity="center"
- android:verticalSpacing="20dip"
- android:columnWidth="90dip"
- android:numColumns="2" >
- </GridView>
- </RelativeLayout>
接下來就是GridView的Item的布局,看上面的圖也行你會認為他的效果是2張圖片添加的效果,其實不是,後面的疊加效果只是一張背景圖片而已,代碼先貼上來
- <?xml version="1.0" encoding="UTF-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content" >
- <FrameLayout
- android:id="@+id/framelayout"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content" >
- <com.example.imagescan.MyImageView
- android:id="@+id/group_image"
- android:background="@drawable/albums_bg"
- android:src="@drawable/friends_sends_pictures_no"
- android:paddingLeft="20dip"
- android:paddingRight="20dip"
- android:paddingTop="18dip"
- android:paddingBottom="30dip"
- android:scaleType="fitXY"
- android:layout_width="fill_parent"
- android:layout_height="150dip" />
- <TextView
- android:id="@+id/group_count"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@drawable/albums_icon_bg"
- android:gravity="center"
- android:layout_marginBottom="10dip"
- android:text="5"
- android:layout_gravity="bottom|center_horizontal" />
- </FrameLayout>
- <TextView
- android:id="@+id/group_title"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:layout_below="@id/framelayout"
- android:layout_centerHorizontal="true"
- android:ellipsize="end"
- android:singleLine="true"
- android:text="Camera"
- android:textSize="16sp" />
- </RelativeLayout>
看到上面的布局代碼,也行你已經發現了,上面使用的是自定義的MyImageView,我先不說這個自定義MyImageView的作用,待會再給大家說,我們繼續看代碼
第一個界面的主要代碼
- package com.example.imagescan;
- import java.io.File;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import android.app.Activity;
- import android.app.ProgressDialog;
- import android.content.ContentResolver;
- import android.content.Intent;
- import android.database.Cursor;
- import android.net.Uri;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.provider.MediaStore;
- import android.view.View;
- import android.widget.AdapterView;
- import android.widget.AdapterView.OnItemClickListener;
- import android.widget.GridView;
- /**
- *
- * @author xiaanming
- *
- *
- */
- public class MainActivity extends Activity {
- private HashMap<String, List<String>> mGruopMap = new HashMap<String, List<String>>();
- private List<ImageBean> list = new ArrayList<ImageBean>();
- private final static int SCAN_OK = 1;
- private ProgressDialog mProgressDialog;
- private GroupAdapter adapter;
- private GridView mGroupGridView;
- private Handler mHandler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch (msg.what) {
- case SCAN_OK:
- //關閉進度條
- mProgressDialog.dismiss();
- adapter = new GroupAdapter(MainActivity.this, list = subGroupOfImage(mGruopMap), mGroupGridView);
- mGroupGridView.setAdapter(adapter);
- break;
- }
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mGroupGridView = (GridView) findViewById(R.id.main_grid);
- getImages();
- mGroupGridView.setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view,
- int position, long id) {
- List<String> childList = mGruopMap.get(list.get(position).getFolderName());
- Intent mIntent = new Intent(MainActivity.this, ShowImageActivity.class);
- mIntent.putStringArrayListExtra("data", (ArrayList<String>)childList);
- startActivity(mIntent);
- }
- });
- }
- /**
- * 利用ContentProvider掃描手機中的圖片,此方法在運行在子線程中
- */
- private void getImages() {
- //顯示進度條
- mProgressDialog = ProgressDialog.show(this, null, "正在加載...");
- new Thread(new Runnable() {
- @Override
- public void run() {
- Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
- ContentResolver mContentResolver = MainActivity.this.getContentResolver();
- //只查詢jpeg和png的圖片
- Cursor mCursor = mContentResolver.query(mImageUri, null,
- MediaStore.Images.Media.MIME_TYPE + "=? or "
- + MediaStore.Images.Media.MIME_TYPE + "=?",
- new String[] { "image/jpeg", "image/png" }, MediaStore.Images.Media.DATE_MODIFIED);
- if(mCursor == null){
- return;
- }
- while (mCursor.moveToNext()) {
- //獲取圖片的路徑
- String path = mCursor.getString(mCursor
- .getColumnIndex(MediaStore.Images.Media.DATA));
- //獲取該圖片的父路徑名
- String parentName = new File(path).getParentFile().getName();
- //根據父路徑名將圖片放入到mGruopMap中
- if (!mGruopMap.containsKey(parentName)) {
- List<String> chileList = new ArrayList<String>();
- chileList.add(path);
- mGruopMap.put(parentName, chileList);
- } else {
- mGruopMap.get(parentName).add(path);
- }
- }
- //通知Handler掃描圖片完成
- mHandler.sendEmptyMessage(SCAN_OK);
- mCursor.close();
- }
- }).start();
- }
- /**
- * 組裝分組界面GridView的數據源,因為我們掃描手機的時候將圖片信息放在HashMap中
- * 所以需要遍歷HashMap將數據組裝成List
- *
- * @param mGruopMap
- * @return
- */
- private List<ImageBean> subGroupOfImage(HashMap<String, List<String>> mGruopMap){
- if(mGruopMap.size() == 0){
- return null;
- }
- List<ImageBean> list = new ArrayList<ImageBean>();
- Iterator<Map.Entry<String, List<String>>> it = mGruopMap.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<String, List<String>> entry = it.next();
- ImageBean mImageBean = new ImageBean();
- String key = entry.getKey();
- List<String> value = entry.getValue();
- mImageBean.setFolderName(key);
- mImageBean.setImageCounts(value.size());
- mImageBean.setTopImagePath(value.get(0));//獲取該組的第一張圖片
- list.add(mImageBean);
- }
- return list;
- }
- }
首先看getImages()這個方法,該方法是使用ContentProvider將手機中的圖片掃描出來,我這裡只掃描了手機的外部存儲中的圖片,由於手機中可能存在很多的圖片,掃描圖片又比較耗時,所以我們在這裡開啟了子線程去獲取圖片,掃描的圖片都存放在Cursor中,我們先要將圖片按照文件夾進行分類,我們使用了HashMap來進行分類並將結果存儲到mGruopMap(Key是文件夾名,Value是文件夾中的圖片路徑的List)中,分類完了關閉Cursor並利用Handler來通知主線程
然後是subGroupOfImage()方法,改方法是將mGruopMap的數據組裝到List中,在List中存放GridView中的每個item的數據對象ImageBean, 遍歷HashMap對象,具體的邏輯看代碼,之後就是給GridView設置Adapter。
設置item點擊事件,點擊文件夾跳轉到展示文件夾圖片的Activity, 我們需要傳遞每個文件夾中的圖片的路徑的集合
看GroupAdapter的代碼之前,我們先看一個比較重要的類,本地圖片加載器NativeImageLoader
- package com.example.imagescan;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.Point;
- import android.os.Handler;
- import android.os.Message;
- import android.support.v4.util.LruCache;
- /**
- * 本地圖片加載器,采用的是異步解析本地圖片,單例模式利用getInstance()獲取NativeImageLoader實例
- * 調用loadNativeImage()方法加載本地圖片,此類可作為一個加載本地圖片的工具類
- *
- *
- * @author xiaanming
- *
- */
- public class NativeImageLoader {
- private LruCache<String, Bitmap> mMemoryCache;
- private static NativeImageLoader mInstance = new NativeImageLoader();
- private ExecutorService mImageThreadPool = Executors.newFixedThreadPool(1);
- private NativeImageLoader(){
- //獲取應用程序的最大內存
- final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
- //用最大內存的1/4來存儲圖片
- final int cacheSize = maxMemory / 4;
- mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
- //獲取每張圖片的大小
- @Override
- protected int sizeOf(String key, Bitmap bitmap) {
- return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
- }
- };
- }
- /**
- * 通過此方法來獲取NativeImageLoader的實例
- * @return
- */
- public static NativeImageLoader getInstance(){
- return mInstance;
- }
- /**
- * 加載本地圖片,對圖片不進行裁剪
- * @param path
- * @param mCallBack
- * @return
- */
- public Bitmap loadNativeImage(final String path, final NativeImageCallBack mCallBack){
- return this.loadNativeImage(path, null, mCallBack);
- }
- /**
- * 此方法來加載本地圖片,這裡的mPoint是用來封裝ImageView的寬和高,我們會根據ImageView控件的大小來裁剪Bitmap
- * 如果你不想裁剪圖片,調用loadNativeImage(final String path, final NativeImageCallBack mCallBack)來加載
- * @param path
- * @param mPoint
- * @param mCallBack
- * @return
- */
- public Bitmap loadNativeImage(final String path, final Point mPoint, final NativeImageCallBack mCallBack){
- //先獲取內存中的Bitmap
- Bitmap bitmap = getBitmapFromMemCache(path);
- final Handler mHander = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- mCallBack.onImageLoader((Bitmap)msg.obj, path);
- }
- };
- //若該Bitmap不在內存緩存中,則啟用線程去加載本地的圖片,並將Bitmap加入到mMemoryCache中
- if(bitmap == null){
- mImageThreadPool.execute(new Runnable() {
- @Override
- public void run() {
- //先獲取圖片的縮略圖
- Bitmap mBitmap = decodeThumbBitmapForFile(path, mPoint == null ? 0: mPoint.x, mPoint == null ? 0: mPoint.y);
- Message msg = mHander.obtainMessage();
- msg.obj = mBitmap;
- mHander.sendMessage(msg);
- //將圖片加入到內存緩存
- addBitmapToMemoryCache(path, mBitmap);
- }
- });
- }
- return bitmap;
- }
- /**
- * 往內存緩存中添加Bitmap
- *
- * @param key
- * @param bitmap
- */
- private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
- if (getBitmapFromMemCache(key) == null && bitmap != null) {
- mMemoryCache.put(key, bitmap);
- }
- }
- /**
- * 根據key來獲取內存中的圖片
- * @param key
- * @return
- */
- private Bitmap getBitmapFromMemCache(String key) {
- return mMemoryCache.get(key);
- }
- /**
- * 根據View(主要是ImageView)的寬和高來獲取圖片的縮略圖
- * @param path
- * @param viewWidth
- * @param viewHeight
- * @return
- */
- private Bitmap decodeThumbBitmapForFile(String path, int viewWidth, int viewHeight){
- BitmapFactory.Options options = new BitmapFactory.Options();
- //設置為true,表示解析Bitmap對象,該對象不占內存
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(path, options);
- //設置縮放比例
- options.inSampleSize = computeScale(options, viewWidth, viewHeight);
- //設置為false,解析Bitmap對象加入到內存中
- options.inJustDecodeBounds = false;
- return BitmapFactory.decodeFile(path, options);
- }
- /**
- * 根據View(主要是ImageView)的寬和高來計算Bitmap縮放比例。默認不縮放
- * @param options
- * @param width
- * @param height
- */
- private int computeScale(BitmapFactory.Options options, int viewWidth, int viewHeight){
- int inSampleSize = 1;
- if(viewWidth == 0 || viewWidth == 0){
- return inSampleSize;
- }
- int bitmapWidth = options.outWidth;
- int bitmapHeight = options.outHeight;
- //假如Bitmap的寬度或高度大於我們設定圖片的View的寬高,則計算縮放比例
- if(bitmapWidth > viewWidth || bitmapHeight > viewWidth){
- int widthScale = Math.round((float) bitmapWidth / (float) viewWidth);
- int heightScale = Math.round((float) bitmapHeight / (float) viewWidth);
- //為了保證圖片不縮放變形,我們取寬高比例最小的那個
- inSampleSize = widthScale < heightScale ? widthScale : heightScale;
- }
- return inSampleSize;
- }
- /**
- * 加載本地圖片的回調接口
- *
- * @author xiaanming
- *
- */
- public interface NativeImageCallBack{
- /**
- * 當子線程加載完了本地的圖片,將Bitmap和圖片路徑回調在此方法中
- * @param bitmap
- * @param path
- */
- public void onImageLoader(Bitmap bitmap, String path);
- }
- }
該類是一個單例類,提供了本地圖片加載,內存緩存,裁剪等邏輯,該類在加載本地圖片的時候采用的是異步加載的方式,對於大圖片的加載也是比較耗時的,所以采用子線程的方式去加載,對於圖片的緩存機制使用的是LruCache,使用手機分配給應用程序內存的1/4用來緩存圖片,除了使用LruCache緩存圖片之外,還對圖片進行了裁剪,舉個很簡單的例子,假如我們的控件大小是100 * 100, 而我們的圖片是400*400,我們加載這麼大的圖片需要很多的內存,所以我們采用了圖片裁剪,根據控件的大小來確定圖片的裁剪比例,從而減小內存的消耗,提過GridView滑動的流暢度,介紹裡面幾個比較重要的方法,computeScale()計算圖片需要裁剪的比例,根據控件的大小和圖片的大小確定比例,如果圖片比控件大,我們就進行裁剪,否則不需要。decodeThumbBitmapForFile()方法是根據計算好了圖片裁剪的比例之後從文件中加載圖片,我們先設置options.inJustDecodeBounds = true表示解析不占用內存,但是我們能獲取圖片的具體大小,利用computeScale()計算好比例,在將options.inJustDecodeBounds=false,再次解析Bitmap,這樣子就對圖片進行了裁剪。
loadNativeImage(final String path, final Point mPoint, final NativeImageCallBack mCallBack)我們在客戶端只需要調用該方法就能獲取到Bitmap對象,裡面的具體邏輯是先判斷內存緩存LruCache中是否存在該Bitmap,不存在就開啟子線程去讀取,為了方便管理加載本地圖片線程,這裡使用了線程池,池中只能容納一個線程,讀取完了本地圖片先將Bitmap加入到LruCache中,保存的Key為圖片路徑,然後再使用Handler通知主線程圖片加載好了,之後將Bitmap和路徑回調到方法onImageLoader(Bitmap bitmap, String path)中,該方法的mPoint是用來封裝控件的寬和高的對象,如果不對圖片進行裁剪直接這個方法的重載方法loadNativeImage(final String path, final NativeImageCallBack mCallBack)就行了,邏輯是一樣的,只是這個方法不對圖片進行裁剪
接下來就是GridView的Adapter類的代碼
- package com.example.imagescan;
- import java.util.List;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.Point;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.BaseAdapter;
- import android.widget.GridView;
- import android.widget.ImageView;
- import android.widget.TextView;
- import com.example.imagescan.MyImageView.OnMeasureListener;
- import com.example.imagescan.NativeImageLoader.NativeImageCallBack;
- public class GroupAdapter extends BaseAdapter{
- private List<ImageBean> list;
- private Point mPoint = new Point(0, 0);//用來封裝ImageView的寬和高的對象
- private GridView mGridView;
- protected LayoutInflater mInflater;
- @Override
- public int getCount() {
- return list.size();
- }
- @Override
- public Object getItem(int position) {
- return list.get(position);
- }
- @Override
- public long getItemId(int position) {
- return position;
- }
- public GroupAdapter(Context context, List<ImageBean> list, GridView mGridView){
- this.list = list;
- this.mGridView = mGridView;
- mInflater = LayoutInflater.from(context);
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final ViewHolder viewHolder;
- ImageBean mImageBean = list.get(position);
- String path = mImageBean.getTopImagePath();
- if(convertView == null){
- viewHolder = new ViewHolder();
- convertView = mInflater.inflate(R.layout.grid_group_item, null);
- viewHolder.mImageView = (MyImageView) convertView.findViewById(R.id.group_image);
- viewHolder.mTextViewTitle = (TextView) convertView.findViewById(R.id.group_title);
- viewHolder.mTextViewCounts = (TextView) convertView.findViewById(R.id.group_count);
- //用來監聽ImageView的寬和高
- viewHolder.mImageView.setOnMeasureListener(new OnMeasureListener() {
- @Override
- public void onMeasureSize(int width, int height) {
- mPoint.set(width, height);
- }
- });
- convertView.setTag(viewHolder);
- }else{
- viewHolder = (ViewHolder) convertView.getTag();
- viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no);
- }
- viewHolder.mTextViewTitle.setText(mImageBean.getFolderName());
- viewHolder.mTextViewCounts.setText(Integer.toString(mImageBean.getImageCounts()));
- //給ImageView設置路徑Tag,這是異步加載圖片的小技巧
- viewHolder.mImageView.setTag(path);
- //利用NativeImageLoader類加載本地圖片
- Bitmap bitmap = NativeImageLoader.getInstance().loadNativeImage(path, mPoint, new NativeImageCallBack() {
- @Override
- public void onImageLoader(Bitmap bitmap, String path) {
- ImageView mImageView = (ImageView) mGridView.findViewWithTag(path);
- if(bitmap != null && mImageView != null){
- mImageView.setImageBitmap(bitmap);
- }
- }
- });
- if(bitmap != null){
- viewHolder.mImageView.setImageBitmap(bitmap);
- }else{
- viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no);
- }
- return convertView;
- }
- public static class ViewHolder{
- public MyImageView mImageView;
- public TextView mTextViewTitle;
- public TextView mTextViewCounts;
- }
- }
首先我們將每個item的圖片路徑設置到該ImageView上面,然後利用NativeImageLoader來加載本地圖片,很普通,但是我們想在getView()中獲取ImageView的寬和高存在問題,在getView()裡面剛開始顯示item的時候利用ImageView.getWidth()獲取的都是0,這個比較郁悶,但是我們想要獲取ImageView的寬和高怎麼辦呢?於是我想到了自定義Image View,在onMeasure()中利用回調的模式主動通知我ImageView測量的寬和高,但是這有一個小小的問題,就是顯示GridView的第一個item的時候,獲取的寬和高還是0,第二個就能正常獲取了,第一個寬和高為0,我們不對第一張圖片進行裁剪而已,在效率上也沒啥問題,不知道大家有沒有好的方法,可以在getView()中獲取Item中某個控件的寬和高。
自定義MyImageView的代碼,我們只需要設置OnMeasureListener監聽,當MyImageView測量完畢之後,就會將測量的寬和高回調到onMeasureSize()中,然後我們可以根據MyImageView的大小來裁剪圖片
- package com.example.imagescan;
- import android.content.Context;
- import android.util.AttributeSet;
- import android.widget.ImageView;
- public class MyImageView extends ImageView {
- private OnMeasureListener onMeasureListener;
- public void setOnMeasureListener(OnMeasureListener onMeasureListener) {
- this.onMeasureListener = onMeasureListener;
- }
- public MyImageView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- public MyImageView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- //將圖片測量的大小回調到onMeasureSize()方法中
- if(onMeasureListener != null){
- onMeasureListener.onMeasureSize(getMeasuredWidth(), getMeasuredHeight());
- }
- }
- public interface OnMeasureListener{
- public void onMeasureSize(int width, int height);
- }
- }
上面這些代碼就完成了第一個界面的功能了,接下來就是點擊GridView的item跳轉另一個界面來顯示該文件夾下面的所有圖片,功能跟第一個界面差不多,也是使用GridView來顯示圖片,第二個界面的布局代碼我就不貼了,直接貼上界面的代碼
- package com.example.imagescan;
- import java.util.List;
- import android.app.Activity;
- import android.os.Bundle;
- import android.widget.GridView;
- import android.widget.Toast;
- public class ShowImageActivity extends Activity {
- private GridView mGridView;
- private List<String> list;
- private ChildAdapter adapter;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.show_image_activity);
- mGridView = (GridView) findViewById(R.id.child_grid);
- list = getIntent().getStringArrayListExtra("data");
- adapter = new ChildAdapter(this, list, mGridView);
- mGridView.setAdapter(adapter);
- }
- @Override
- public void onBackPressed() {
- Toast.makeText(this, "選中 " + adapter.getSelectItems().size() + " item", Toast.LENGTH_LONG).show();
- super.onBackPressed();
- }
- }
GridView的item上面一個我們自定義的MyImageView用來顯示圖片,另外還有一個CheckBox來記錄我們選中情況,Adapter的代碼如下
- package com.example.imagescan;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.Point;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.BaseAdapter;
- import android.widget.CheckBox;
- import android.widget.CompoundButton;
- import android.widget.ImageView;
- import android.widget.CompoundButton.OnCheckedChangeListener;
- import android.widget.GridView;
- import com.example.imagescan.MyImageView.OnMeasureListener;
- import com.example.imagescan.NativeImageLoader.NativeImageCallBack;
- import com.nineoldandroids.animation.AnimatorSet;
- import com.nineoldandroids.animation.ObjectAnimator;
- public class ChildAdapter extends BaseAdapter {
- private Point mPoint = new Point(0, 0);//用來封裝ImageView的寬和高的對象
- /**
- * 用來存儲圖片的選中情況
- */
- private HashMap<Integer, Boolean> mSelectMap = new HashMap<Integer, Boolean>();
- private GridView mGridView;
- private List<String> list;
- protected LayoutInflater mInflater;
- public ChildAdapter(Context context, List<String> list, GridView mGridView) {
- this.list = list;
- this.mGridView = mGridView;
- mInflater = LayoutInflater.from(context);
- }
- @Override
- public int getCount() {
- return list.size();
- }
- @Override
- public Object getItem(int position) {
- return list.get(position);
- }
- @Override
- public long getItemId(int position) {
- return position;
- }
- @Override
- public View getView(final int position, View convertView, ViewGroup parent) {
- final ViewHolder viewHolder;
- String path = list.get(position);
- if(convertView == null){
- convertView = mInflater.inflate(R.layout.grid_child_item, null);
- viewHolder = new ViewHolder();
- viewHolder.mImageView = (MyImageView) convertView.findViewById(R.id.child_image);
- viewHolder.mCheckBox = (CheckBox) convertView.findViewById(R.id.child_checkbox);
- //用來監聽ImageView的寬和高
- viewHolder.mImageView.setOnMeasureListener(new OnMeasureListener() {
- @Override
- public void onMeasureSize(int width, int height) {
- mPoint.set(width, height);
- }
- });
- convertView.setTag(viewHolder);
- }else{
- viewHolder = (ViewHolder) convertView.getTag();
- viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no);
- }
- viewHolder.mImageView.setTag(path);
- viewHolder.mCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- //如果是未選中的CheckBox,則添加動畫
- if(!mSelectMap.containsKey(position) || !mSelectMap.get(position)){
- addAnimation(viewHolder.mCheckBox);
- }
- mSelectMap.put(position, isChecked);
- }
- });
- viewHolder.mCheckBox.setChecked(mSelectMap.containsKey(position) ? mSelectMap.get(position) : false);
- //利用NativeImageLoader類加載本地圖片
- Bitmap bitmap = NativeImageLoader.getInstance().loadNativeImage(path, mPoint, new NativeImageCallBack() {
- @Override
- public void onImageLoader(Bitmap bitmap, String path) {
- ImageView mImageView = (ImageView) mGridView.findViewWithTag(path);
- if(bitmap != null && mImageView != null){
- mImageView.setImageBitmap(bitmap);
- }
- }
- });
- if(bitmap != null){
- viewHolder.mImageView.setImageBitmap(bitmap);
- }else{
- viewHolder.mImageView.setImageResource(R.drawable.friends_sends_pictures_no);
- }
- return convertView;
- }
- /**
- * 給CheckBox加點擊動畫,利用開源庫nineoldandroids設置動畫
- * @param view
- */
- private void addAnimation(View view){
- float [] vaules = new float[]{0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.25f, 1.2f, 1.15f, 1.1f, 1.0f};
- AnimatorSet set = new AnimatorSet();
- set.playTogether(ObjectAnimator.ofFloat(view, "scaleX", vaules),
- ObjectAnimator.ofFloat(view, "scaleY", vaules));
- set.setDuration(150);
- set.start();
- }
- /**
- * 獲取選中的Item的position
- * @return
- */
- public List<Integer> getSelectItems(){
- List<Integer> list = new ArrayList<Integer>();
- for(Iterator<Map.Entry<Integer, Boolean>> it = mSelectMap.entrySet().iterator(); it.hasNext();){
- Map.Entry<Integer, Boolean> entry = it.next();
- if(entry.getValue()){
- list.add(entry.getKey());
- }
- }
- return list;
- }
- public static class ViewHolder{
- public MyImageView mImageView;
- public CheckBox mCheckBox;
- }
- }
第二個界面的Adapter跟第一個界面差不多,無非多了一個CheckBox用來記錄圖片選擇情況,我們只需要對CheckBox設置setOnCheckedChangeListener監聽,微信的選中之後CheckBox有一個動畫效果,所以我利用nineoldandroids動畫庫也給CheckBox加了一個動畫效果,直接調用addAnimation()方法就能添加了,getSelectItems()方法就能獲取我們選中的item的position了,知道了選中的position,其他的信息就都知道了,微信有對圖片進行預覽的功能,我這裡就不添加了,如果有這個需求可以自行添加,給大家推薦一個https://github.com/chrisbanes/PhotoView
運行項目,效果如下
起來還不錯吧,采用的是異步讀取圖片,對圖片進行了緩存和裁剪,使得在顯示本地圖片方面比較流暢,GridView滑動也挺流暢的,也有效的避免OOM的產生,工程中有些東西還沒有貼完全,有興趣的朋友可以下載Demo來運行一下。
源碼下載
本人小菜一個。目前只見過兩種彈出框的實現方式,第一種是最常見的PopupWindow,第二種也就是Activity的方式是前幾天才見識過。感覺很霸氣哦。沒想到,a
今天,想在android手機上安裝兩個相同的應用,本以為可以安裝不同版本的,試了幾次,均相互覆蓋了,於是,只能設法修改apk所對應的包名(package name
繪制圓環其實很簡單,有大概以下三種思路. 這裡先說網上提到的一種方法。思路是先繪制內圓,然後繪制圓環(圓環的寬度就是paint設置的paint.setStroke
本文實例講述了android編程實現局部界面動態切換的方法。分享給大家供大家參考,具體如下: 局部界面固定,局部界面可以動態切換。效果如下: 這個效果由3個