600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > Android仿微信图片选择器 支持图片选择和拍照

Android仿微信图片选择器 支持图片选择和拍照

时间:2021-01-04 17:50:55

相关推荐

Android仿微信图片选择器 支持图片选择和拍照

Android图片选择器

本文是我的第二篇博客,如果有错误的地方,希望大家多多指点,相信大家都知道网上有很多第三方的图片选择器,但是在真正的项目中却遇到例如一个很重要的问题,定制属于自己的图品选择器,很多第三方的无法满足我们的这一需求,因此我自己写了一个,代码简单,我在下面附带了源码,刚兴趣的朋友可以下载了自己修改.

本项目没有依赖人任何第三方的类库,主要设计思路就是:一个图片加载类(单例)+利用ContentProvider扫描手机的图片+GridView显示图片

本篇博客的主要内容是仿微信图片选择器,主要步骤有如下几点

图片加载类(单例)获取手机本地图片可以设置图片选择个数

本项目的主要功能:

1、默认显示图片最多的文件夹图片,以及底部显示图片总数量;

2、点击底部,弹出popupWindow,popupWindow包含所有含有图片的文件夹,以及显示每个文件夹中图片数量;注:此时Activity变暗

3、选择任何文件夹,进入该文件夹图片显示,可以点击选择图片,当然了,点击已选择的图片则会取消选择;注:选中图片变暗

当然了,最重要的效果一定流畅,不能动不动OOM~~

本人测试手机乐视1s,图片3802张,和小米红米,小米2s,未出现OOM异常,效果也是非常流畅,

不过存在bug在所难免,大家可以留言说下自己发现的bug;文末会提供源码下载。

第一步:核心方法:图片加载工具类 ImageLoader

本方法是图片选择器的核心代码,该方法为单例,可以设置图片加载打方式:1\FIFO(先进先加载)2\LILO(后进先加载)。

调用该方法:ImageLoader.getInstance(最大图片加载并发数,ImageLoader.Type.FIFO(图片加载的方式))

.LoadImage(图片的本地存放路径(据对路径), viewHolder.mImg(要显示图片的ImageView布局));

public class ImageLoader {private static ImageLoader mInstance;/*** 图片缓存的核心对象*/private LruCache<String, Bitmap> mLruCache;/*** 线程池*/private ExecutorService mThreadPool;private static final int DEFAULT_THREAD_COUNT = 1;/*** 队列的调度方式*/private Type mType = Type.FIFO;/*** 任务队列*/private LinkedList<Runnable> mTaskQueue;/*** 后台轮询线程*/private Thread mPoolThread;private Handler mPoolThreadHandler;/*** UI线程的handler*/private Handler mUIHandler;private Semaphore mSemaphorePoolThreadHandler = new Semaphore(0);private Semaphore mSemaphoreThreadPool;public enum Type {FIFO, LIFO;}private ImageLoader(int threadCount, Type type) {init(threadCount, type);}/*** 初始化* * @param threadCount* @param type*/private void init(int threadCount, Type type) {mPoolThread = new Thread() {@Overridepublic void run() {Looper.prepare();mPoolThreadHandler = new Handler() {public void handleMessage(android.os.Message msg) {// 线程池去取出一个任务进行执行mThreadPool.execute(getTask());try {mSemaphoreThreadPool.acquire();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}};// 释放一个信号量mSemaphorePoolThreadHandler.release();Looper.loop();}};mPoolThread.start();// 获取我们应用的最大可用内存int maxMemory = (int) Runtime.getRuntime().maxMemory();int cacheMemory = maxMemory / 4;mLruCache = new LruCache<String, Bitmap>(cacheMemory) {@Overrideprotected int sizeOf(String key, Bitmap value) {// TODO Auto-generated method stubreturn value.getRowBytes() * value.getHeight();}};mThreadPool = Executors.newFixedThreadPool(threadCount);mTaskQueue = new LinkedList<Runnable>();mType = type;mSemaphoreThreadPool = new Semaphore(threadCount);}/*** 从任务队列取出一个方法* * @return*/private Runnable getTask() {if (mType == Type.FIFO) {return mTaskQueue.removeFirst();} else if (mType == Type.LIFO) {return mTaskQueue.removeLast();}return null;};public static ImageLoader getInstance() {if (mInstance == null) {synchronized (ImageLoader.class) {if (mInstance == null) {mInstance = new ImageLoader(DEFAULT_THREAD_COUNT, Type.LIFO);}}}return mInstance;}public static ImageLoader getInstance(int threadCount, Type type) {if (mInstance == null) {synchronized (ImageLoader.class) {if (mInstance == null) {mInstance = new ImageLoader(threadCount, type);}}}return mInstance;}public void LoadImage(final String path, final ImageView imageView) {imageView.setTag(path);if (mUIHandler == null) {mUIHandler = new Handler() {public void handleMessage(android.os.Message msg) {// 获取得到图片.为imageview回调设置图片ImgBeanHolder holder = (ImgBeanHolder) msg.obj;Bitmap bm = holder.bitmap;if(bm!=null){bm = Bitmap.createScaledBitmap(bm, 200, 200, true);}ImageView imageView = holder.imageView;String path = holder.path;if (imageView.getTag().toString().equals(path)) {imageView.setImageBitmap(bm);}};};}// 根据path在缓存中获取bitmapBitmap bm = getBitmapFromLruCache(path);if (bm != null) {referashBitmap(path, imageView, bm);} else {addTasks(new Runnable() {@Overridepublic void run() {// 加载图片// 图片的压缩// 1获得图片需要显示的大小ImageSize imageSize = getImageViewSize(imageView);// 2.压缩图片Bitmap bm = decodeSampledBitmapFromPath(path, imageSize.width, imageSize.height);// 3.把图片加入到缓存addBitmapToLruCache(path, bm);referashBitmap(path, imageView, bm);mSemaphoreThreadPool.release();}});}}private void referashBitmap(final String path, final ImageView imageView, Bitmap bm) {Message message = Message.obtain();ImgBeanHolder holder = new ImgBeanHolder();holder.bitmap = bm;holder.path = path;holder.imageView = imageView;message.obj = holder;mUIHandler.sendMessage(message);}/*** 将图片加入到缓存* * @param path* @param bm*/protected void addBitmapToLruCache(String path, Bitmap bm) {if (getBitmapFromLruCache(path) == null) {if (bm != null) {mLruCache.put(path, bm);}}}/*** 根据图片需要显示的宽高进行啊压缩* * @param path* @param width* @param height* @return*/protected Bitmap decodeSampledBitmapFromPath(String path, int width, int height) {// h获取图片的宽和高,并不把图片加载到内存BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeFile(path);options.inSampleSize = caculateInSampleSize(options, width, height);// 使用获取到的InSampleSize再次解析图片options.inJustDecodeBounds = false;Bitmap bitmap = BitmapFactory.decodeFile(path, options);return bitmap;}/*** 根据需求的宽和高及实际的宽和高计算SampleSize* * @param options* @param width* @param height* @return*/private int caculateInSampleSize(Options options, int reqWidth, int reqHeight) {int width = options.outWidth;int height = options.outWidth;int inSampleSize = 1;if (width > reqWidth || height > reqHeight) {int widthRadio = Math.round(width * 1.0f / reqWidth);int hegihtRadio = Math.round(height * 1.0f / reqHeight);inSampleSize = Math.max(widthRadio, hegihtRadio);}return inSampleSize;}/*** 根据ImageView返回图片的大小* * @param imageView*/@SuppressLint("NewApi")protected ImageSize getImageViewSize(ImageView imageView) {ImageSize imageSize = new ImageSize();DisplayMetrics displayMetrics = imageView.getContext().getResources().getDisplayMetrics();LayoutParams lp = imageView.getLayoutParams();int width = imageView.getWidth();// (lp.width ==// LayoutParams.WRAP_CONTENT ? 0 :// imageView.getWidth());if (width <= 0) {width = lp.width;// 获取imageview在layout中声明的宽度}if (width <= 0) {width = imageView.getMaxWidth();}if (width <= 0) {width = displayMetrics.widthPixels;}int height = imageView.getHeight();if (height <= 0) {height = lp.height;// 获取imageview在layout中声明的高度}if (height <= 0) {height = imageView.getMaxHeight();}if (height <= 0) {height = displayMetrics.heightPixels;}imageSize.width = width;imageSize.height = height;return imageSize;}private synchronized void addTasks(Runnable runnable) {mTaskQueue.add(runnable);// if(mPoolThreadHandler==null)try {if (mPoolThreadHandler == null) {mSemaphorePoolThreadHandler.acquire();}} catch (InterruptedException e) {e.printStackTrace();}mPoolThreadHandler.sendEmptyMessage(0x110);}/*** * @param path* @return*/private Bitmap getBitmapFromLruCache(String path) {return null;}private class ImageSize {int width;int height;}private class ImgBeanHolder {Bitmap bitmap;ImageView imageView;String path;}}

第二步:主界面设计

主界面布局,主布局的设计思路是,用GridView布局显示图片,并为GridView编写响应的适配器.

<RelativeLayout xmlns:android="/apk/res/android"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/set_bg"tools:context="${relativePackage}.${activityClass}" ><RelativeLayout android:id="@+id/top"android:layout_width="match_parent"android:layout_height="50dip"android:background="#0079cd" ><ImageView android:layout_width="wrap_content"android:layout_height="match_parent"android:src="@drawable/return_icon" /><TextView android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_centerInParent="true"android:gravity="center"android:text="图片"android:textColor="#FFFFFF"android:textSize="19sp" /><TextView android:id="@+id/select_image"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_alignParentRight="true"android:layout_centerInParent="true"android:layout_marginBottom="15dp"android:layout_marginLeft="15dp"android:layout_marginRight="10dp"android:background="@drawable/finish_btn"android:gravity="center"android:text="完成(0/3)"android:textColor="#FFFFFF"android:textSize="16sp" /></RelativeLayout><GridView android:id="@+id/id_gridView"android:layout_marginTop="3dp"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_below="@+id/top"android:cacheColorHint="@android:color/transparent"android:horizontalSpacing="3dip"android:numColumns="3"android:stretchMode="columnWidth"android:verticalSpacing="3dp" ></GridView><RelativeLayout android:id="@+id/id_bottom_ly"android:layout_width="match_parent"android:layout_height="50dp"android:layout_alignParentBottom="true"android:background="#88000000"android:clickable="true" ><TextView android:id="@+id/id_dir_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_centerVertical="true"android:paddingLeft="10dp"android:text="所有图片"android:textColor="#e5e5e5"android:textSize="15sp" /><ImageView android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_marginLeft="5dip"android:layout_toRightOf="@+id/id_dir_name"android:src="@drawable/more_select" /><ImageView android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_marginRight="5dip"android:layout_toLeftOf="@+id/id_dir_num"android:src="@drawable/split_line" /><TextView android:id="@+id/id_dir_num"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:paddingRight="10dp"android:text="预览"android:textSize="15sp"android:textColor="@android:color/white" /></RelativeLayout></RelativeLayout>

第三步:获取手机上的全部图片

该方法会扫描手机上的所有图片信息,并显示到主界面上,扫描结束,得到一个所有包含图片的文件夹信息的List;

对于文件夹信息,我们单独创建了一个FolderBean:

扫描说及图片的方法

/*** 利用ContentProvider扫描手机的图片*/private void initDatas() {if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {Toast.makeText(context, "当前存储卡不可用", Toast.LENGTH_SHORT).show();return;}mProgress = ProgressDialog.show(context, null, "正在加载...");new Thread() {@Overridepublic void run() {FolderBean folderBean1 = new FolderBean();folderBean1.setCount(0);folderBean1.setName("所有图片");folderBean1.setDir("/所有图片");folderBean1.setFirstImgPath("/所有图片");mFolderBeans.add(folderBean1);Uri mImgUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;ContentResolver cr = context.getContentResolver();// 获取手机上的所有的图片Cursor cursor = cr.query(mImgUri, null,MediaStore.Images.Media.MIME_TYPE + " = ? or " + MediaStore.Images.Media.MIME_TYPE + " = ? ",new String[] { "image/jpeg", "image/png" }, MediaStore.Images.Media.DATE_MODIFIED);Log.e("图片选择器", "4");Set<String> mDirPaths = new HashSet<String>();Log.e("图片选择器", "5");while (cursor.moveToNext()) {final String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));File parentFile = new File(path).getParentFile();if (parentFile == null) {continue;}String dirPath = parentFile.getAbsolutePath();FolderBean folderBean = null;if (mDirPaths.contains(dirPath)) {continue;} else {mDirPaths.add(dirPath);folderBean = new FolderBean();folderBean.setDir(dirPath);folderBean.setFirstImgPath(path);}if (parentFile.list() == null)continue;int picSize = parentFile.list(new FilenameFilter() {//对文件进行过滤,获取图片文件@Overridepublic boolean accept(File dir, String filename) {if (filename.endsWith(".jpg") || filename.endsWith(".jpeg") || filename.endsWith(".png")) {return true;}return false;}}).length;folderBean.setCount(picSize);mFolderBeans.add(folderBean);if (picSize > mMaxCount) {mMaxCount = picSize;mCurrentDir = parentFile;}}Log.e("图片选择器", "6");cursor.close();// 通知handler扫描完成mHandler.sendEmptyMessage(DATA_LOADED);}}.start();}

FolderBean

public class FolderBean {private String dir;private String name;private String firstImgPath;private int count;public String getDir() {return dir;}public void setDir(String dir) {this.dir = dir;int lastIndexOf = this.dir.lastIndexOf("/") + 1;this.name = this.dir.substring(lastIndexOf);}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getFirstImgPath() {return firstImgPath;}public void setFirstImgPath(String firstImgPath) {this.firstImgPath = firstImgPath;}public int getCount() {return count;}public void setCount(int count) {this.count = count;}@Overridepublic String toString() {return dir;}}

第四步:把图片信息显示到布局

public class ImageAdapter extends BaseAdapter {private static Set<String> mSelectImg = new HashSet<String>();private Context context;private List<String> mImgPahts;private String mDirPath;private LayoutInflater mInflater;private Handler handler;public ImageAdapter(Context context, List<String> mDatas, String dirPath, Handler handler) {this.context = context;this.mImgPahts = mDatas;this.mDirPath = dirPath;mInflater = LayoutInflater.from(context);this.handler = handler;}@Overridepublic int getCount() {return mImgPahts.size();}@Overridepublic Object getItem(int position) {return mImgPahts.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {final ViewHolder viewHolder;if (convertView == null) {convertView = mInflater.inflate(R.layout.item_griadview, null);viewHolder = new ViewHolder();viewHolder.mImg = (ImageView) convertView.findViewById(R.id.id_item_image);viewHolder.mSelect = (ImageButton) convertView.findViewById(R.id.id_item_select);convertView.setTag(viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag();}if (mImgPahts.get(position).equals("null###")) {viewHolder.mImg.setImageResource(R.drawable.photograph_icon);viewHolder.mImg.setScaleType(ScaleType.CENTER);viewHolder.mSelect.setVisibility(View.GONE);viewHolder.mImg.setColorFilter(null);viewHolder.mImg.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubhandler.sendEmptyMessage(4);}});} else {viewHolder.mImg.setImageResource(R.drawable.not_loaded_icon);viewHolder.mImg.setScaleType(ScaleType.FIT_XY);viewHolder.mSelect.setVisibility(View.VISIBLE);viewHolder.mSelect.setImageResource(R.drawable.login_chexbox);viewHolder.mImg.setColorFilter(null);if (mDirPath.equals("")) {ImageLoader.getInstance(3, com.awang.imageloader.util.ImageLoader.Type.FIFO).LoadImage(mImgPahts.get(position), viewHolder.mImg);} else {ImageLoader.getInstance(3, com.awang.imageloader.util.ImageLoader.Type.FIFO).LoadImage(mDirPath + "/" + mImgPahts.get(position), viewHolder.mImg);}final String filePath = mDirPath.equals("") ? mImgPahts.get(position): mDirPath + "/" + mImgPahts.get(position);viewHolder.mImg.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubImgaeSelectActivity.getCount();if (ImgaeSelectActivity.contains(filePath)) {ImgaeSelectActivity.removeSelectImg(filePath);handler.sendEmptyMessage(2);viewHolder.mSelect.setImageResource(R.drawable.login_chexbox);viewHolder.mImg.setColorFilter(null);} else {if (ImgaeSelectActivity.getCount() >= 3) {handler.sendEmptyMessage(1);} else {ImgaeSelectActivity.addSelectImg(filePath);handler.sendEmptyMessage(1);viewHolder.mSelect.setImageResource(R.drawable.login_chexbox_s);viewHolder.mImg.setColorFilter(Color.parseColor("#77000000"));}}// notifyDataSetChanged();}});if (ImgaeSelectActivity.contains(filePath)) {viewHolder.mSelect.setImageResource(R.drawable.login_chexbox_s);viewHolder.mImg.setColorFilter(Color.parseColor("#77000000"));}}return convertView;}public class ViewHolder {ImageView mImg;ImageButton mSelect;}}

第五步:展现文件夹的PopupWindow

要实现的效果是,点击底部的布局弹出我们的文件夹选择框,并且我们弹出框后面的Activity要变暗;

public class ListImageDirPopupWindow extends PopupWindow {private int width;private int heightl;private View mContentView;private ListView mListView;private List<FolderBean> mDatas;private ListDirAdapter mAdapter;public interface setOnDirSelectListerener {void onSelected(FolderBean folderBean, int position);}private static int currentSelect = 0;public setOnDirSelectListerener dirSelectListerener;public ListImageDirPopupWindow(Context context, List<FolderBean> mDatas) {calWidthAndHeight(context);mContentView = LayoutInflater.from(context).inflate(R.layout.popuwid, null);this.mDatas = mDatas;setContentView(mContentView);setWidth(width);setHeight(heightl);setFocusable(true);setTouchable(true);setOutsideTouchable(true);setBackgroundDrawable(new BitmapDrawable());setTouchInterceptor(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {dismiss();return true;}return false;}});initViews(context);initEvent();}private void initViews(Context context) {mListView = (ListView) mContentView.findViewById(R.id.id_list_dir);mAdapter = new ListDirAdapter(context, mDatas);mListView.setAdapter(mAdapter);}public void initEvent() {mListView.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {if (dirSelectListerener != null) {dirSelectListerener.onSelected(mDatas.get(position), position);currentSelect = position;mAdapter.notifyDataSetChanged();}}});}public void setDirSelectListerener(setOnDirSelectListerener dirSelectListerener) {this.dirSelectListerener = dirSelectListerener;}/*** 计算popupWindow的宽度和高度*/private void calWidthAndHeight(Context context) {WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);DisplayMetrics outMetrics = new DisplayMetrics();wm.getDefaultDisplay().getMetrics(outMetrics);width = outMetrics.widthPixels;heightl = (int) (outMetrics.heightPixels * 0.7);}private class ListDirAdapter extends ArrayAdapter<FolderBean> {private LayoutInflater mInflater;private List<FolderBean> mDatas;public ListDirAdapter(Context context, List<FolderBean> mDatas) {super(context, 0, mDatas);mInflater = LayoutInflater.from(context);}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;if (convertView == null) {viewHolder = new ViewHolder();convertView = mInflater.inflate(R.layout.item_popuwid, parent, false);viewHolder.mImg = (ImageView) convertView.findViewById(R.id.id_dir_item_image);viewHolder.setSel = (ImageView) convertView.findViewById(R.id.set_sel);viewHolder.mDirName = (TextView) convertView.findViewById(R.id.id_dir_item_name);viewHolder.mDirCount = (TextView) convertView.findViewById(R.id.id_dir_item_count);convertView.setTag(viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag();}FolderBean bean = getItem(position);// 重置viewHolder.mImg.setImageResource(R.drawable.not_loaded_icon);ImageLoader.getInstance().LoadImage(bean.getFirstImgPath(), viewHolder.mImg);viewHolder.mDirName.setText(bean.getName());if (bean.getCount() == 0) {viewHolder.mDirCount.setText("");} else {viewHolder.mDirCount.setText("" + bean.getCount());}if (position == currentSelect) {viewHolder.setSel.setVisibility(View.VISIBLE);} else {viewHolder.setSel.setVisibility(View.GONE);}return convertView;}private class ViewHolder {ImageView setSel;ImageView mImg;TextView mDirName;TextView mDirCount;}}public static void setCurrent(int position){}}

布局:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#FFFFFF"android:orientation="vertical" ><ListView android:paddingLeft="10dp"android:layout_marginTop="10dp"android:layout_marginBottom="10dp"android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/id_list_dir"android:divider="#eee39d"android:dividerHeight="1px"></ListView></LinearLayout>

主要的代码就这么多,

总结:本项目的主要代码基本已经贴出来了,感兴趣的朋友也可以去下载本人的项目,下载地址也已经贴了出来,最后,如有不对的地方,请多多指教.

项目下载地址:

/download/waa_0618/9748835

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。