編輯:關於Android編程
之前群裡面有朋友問我,有沒有關於本地圖片選擇的Demo,類似微信的效果,他說網上沒有這方面的Demo,問我能不能寫一篇關於這個效果的Demo,於是我研究了下微信的本地圖片選擇的Demo,自己仿照的寫了下分享給大家,希望對以後有這樣子需求的朋友有一點幫助吧,主要使用的是ContentProvider掃描手機中的圖片,並用GridView將圖片顯示出來,關於GridView和ListView顯示圖片的問題,一直是一個很頭疼的問題,因為我們手機的內存有限,手機給每個應用程序分配的內存也有限,所以圖片多的情況下很容易伴隨著OOM的發生,不過現在也有很多的開源的圖片顯示框架,對顯示很多圖片進行了優化,大家有興趣的可以去了解了解,今天我的這篇文章使用的是LruCache這個類以及對圖片進行相對應的裁剪,這樣也可以盡量的避免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; 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來通知主線程
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()方法加載本地圖片,此類可作為一個加載本地圖片的工具類 */ 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滑動的流暢度,介紹裡面幾個比較重要的方法
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的圖片路徑設置Tag到該ImageView上面,然後利用NativeImageLoader來加載本地圖片,但是我們顯示的圖片的寬和高可能遠大於GirdView item中ImageView的大小,於是為了節省內存,我們需要對圖片進行裁剪,需要對圖片裁剪我們利用loadNativeImage(final String path, final Point mPoint, final NativeImageCallBack mCallBack)方法,我們就必須要獲取ImageView的寬和高了
自定義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的產生。
Android快捷方式解密Android快捷方式作為Android設備的殺手锏技能,一直都是非常重要的一個功能,也正是如此,各種流氓App也不斷通過快捷方式霸占著這樣一個
接著上次的問題,已經介紹過,在初始化或者說OnCreate方法中獲取加載的布局的寬高,最後說到,調用view.measure(0, 0);然後在調用getMeasur
LRecyclerView能做什麼?經過再三思考,同時也為了大家使用方便,LRecyclerView集成了SwipeMenu系列功能,包括Item側滑菜單、長按拖拽Ite
本文實例分析了Android開發之TimePicker控件用法。分享給大家供大家參考,具體如下:新建項目:New Android Project->Project