Android中仿微信选择图片并展示在RecyclerView中
Android图片选择
大家都知道网上有很多第三方的图片选择器,但是到了自己真正的项目中,可能会有不同的需求,需要自己去修改。因此我自己根据鸿洋大神的慕课网视频写了一个图片选择器,又对代码进行了修改,方便大家进行使用。
本项目主要设计思路就是:一个图片加载类(单例)+利用ContentProvider扫描手机的图片+GridView显示图片 +RecyclerView在界面上显示图片。
本项目的主要步骤有以下几步:
1.图片加载类
2.扫描手机中的图片
3.选择图片展示在recyclerview中
当然最重要的是:
1.尽可能的去避免内存溢出
2.用户操作UI控件必须充分的流畅
3.用户预期显示的图片尽可能的快(图片的加载策略的选择)LIFO
本项目的主要功能:
1.扫描手机中的图片,默认显示图片最多的文件夹,在底部显示文件夹的名字以及图片的数量。
2.点击底部,弹出popupWindow,此popupWindow显示所有包含图片的文件夹以及文件夹的名字。
3.选择文件夹,进入图片选择界面,点击选择图片,再次点击取消。
4.点击右上方的“选择”按钮,将选择的图片呈现在recyclerView中。
展示一下效果:
代码展示及讲解
1.图片加载类 ImageLoader(核心类)
本类是图片选择器的核心类,该类为单例,可以设置图片加载打方式:
1.FIFO(先进先加载)
2.LILO(后进先加载)。
调用该方法:ImageLoader.getInstance(最大图片加载并发数,ImageLoader.Type.FIFO(图片加载的方式))
.LoadImage(图片的本地存放路径(绝对路径), 要显示图片的ImageView布局);
public class ImageLoader {private static ImageLoader mInStance; //图片缓存的核心对象 private LruCache<String,Bitmap> mLruCache; //线程池 private ExecutorService mThreadPool; private static final int DEAFULT_THREAD_COUNT=1; //队列的调度方式 private Type mType=Type.LIFO; //任务队列 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; }public ImageLoader(int threadCount,Type type) {init(threadCount,type); }/** * 初始化 * @param threadCount * @param type */ private void init(int threadCount, Type type) {//后台轮询线程 mPoolThread=new Thread(){@Override public void run() {Looper.prepare(); mPoolThreadHandler=new Handler(){@Override public void handleMessage(Message msg) {//线程池取出一个任务进行执行 mThreadPool.execute(getTask()); try {mSemaphoreThreadPool.acquire(); } catch (InterruptedException e) {e.printStackTrace(); }}}; //释放一个信号量 mSemaphorePoolThreadHandler.release(); Looper.loop(); }}; mPoolThread.start(); //获取我们应用的最大可用内存 int maxMemory= (int) Runtime.getRuntime().maxMemory(); int cacheMemory = maxMemory / 8; mLruCache=new LruCache<String,Bitmap>(cacheMemory){@Override protected int sizeOf(String key, Bitmap value) {return value.getRowBytes()*value.getHeight(); }}; mThreadPool= Executors.newFixedThreadPool(threadCount); mTaskQueue=new LinkedList<>(); mType=type==null?Type.LIFO: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(DEAFULT_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; }/** * 根据path为imageview设置图片 * @param path * @param imageView */ public void loadImage(final String path, final ImageView imageView){imageView.setTag(path); if (mUIHandler==null){mUIHandler=new Handler(){@Override public void handleMessage(Message msg) {//获取得到的图片,为imageview回调设置图片 ImgBeanHolder holder= (ImgBeanHolder) msg.obj; Bitmap bitmap = holder.bitmap; ImageView imageview= holder.imageView; String path = holder.path; if (imageview.getTag().toString().equals( path)){imageview.setImageBitmap(bitmap); }}}; }//根据path在缓存中获取bitmap Bitmap bm=getBitmapFromLruCache(path); if (bm!=null){refreshBitmap(bm, path, imageView); }else{addTask(new Runnable(){@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)@Override public void run() {//加载图片 //图片的压缩 //1.获得图片需要显示的大小 ImageSize imageSize= getImageViewSize(imageView); //压缩图片 Bitmap bm=decodeSampledBitmapFromPath(imageSize.width,imageSize.height,path); //把图片加入到缓存 addBitmapToLruCache(path,bm); // refreshBitmap(bm, path, imageView); mSemaphoreThreadPool.release(); }}); }}private void refreshBitmap(Bitmap bm, String path, ImageView imageView) {Message message = Message.obtain(); ImgBeanHolder holder=new ImgBeanHolder(); holder.bitmap=bm; holder.path=path; holder.imageView=imageView; message.obj=holder; mUIHandler.sendMessage(message); }/** * 将图片加入到LruCache * @param path * @param bm */ private void addBitmapToLruCache(String path, Bitmap bm) {if (getBitmapFromLruCache(path)==null){if (bm!=null){mLruCache.put(path,bm); }}}/** * 根据图片需要显示的宽和高进行压缩 * @param width * @param height * @param path * @return */ private Bitmap decodeSampledBitmapFromPath(int width, int height, String path) {//获得图片的宽和高,并不把图片加载到内存中 BitmapFactory.Options options=new BitmapFactory.Options(); options.inJustDecodeBounds=true; BitmapFactory.decodeFile(path,options); 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(BitmapFactory.Options options, int width, int height) {int outWidth = options.outWidth; int outHeight = options.outHeight; int inSampleSize=1; if (outWidth>width||outHeight>height){int widthRadio=Math.round(outWidth*1.0f/width); int heightRadio=Math.round(outHeight*1.0f/height); inSampleSize=Math.max(widthRadio,heightRadio); }return inSampleSize; }/** *根据ImageView获得适当的压缩的宽和高 * @param imageView */ private ImageSize getImageViewSize(ImageView imageView) {ImageSize imageSize=new ImageSize(); DisplayMetrics displayMetrics = imageView.getContext().getResources().getDisplayMetrics(); ViewGroup.LayoutParams lp = imageView.getLayoutParams(); // int width=(lp.width== ViewGroup.LayoutParams.WRAP_CONTENT?0:imageView.getWidth()); int width = imageView.getWidth();//获取imageview的实际宽度 if (width <=0){width=lp.width;//获取imageview再layout声明的宽度 }if (width<=0){width= getImageViewFieldValue(imageView,"mMaxWidth");//检查最大值 }if (width<=0){width=displayMetrics.widthPixels; }//int height = lp.height == ViewGroup.LayoutParams.WRAP_CONTENT ? 0 : imageView.getHeight(); int height = imageView.getHeight();//获取imageview的实际高度 if (height <=0){height=lp.height;//获取imageview再layout声明的高度 }if (height<=0){height=getImageViewFieldValue(imageView,"mMaxHeight");;//检查最大值 }if (height<=0){height=displayMetrics.heightPixels; }imageSize.width=width; imageSize.height=height; return imageSize; }/** * 通过反射获得imageView的某个属性值 * @return */ private static int getImageViewFieldValue(Object object,String fieldName){int value=0; try {Field field=ImageView.class.getDeclaredField(fieldName); field.setAccessible(true); int fieldValue = field.getInt(object); if (fieldValue>0&&fieldValue<Integer.MAX_VALUE){value=fieldValue; }} catch (NoSuchFieldException e) {e.printStackTrace(); } catch (IllegalAccessException e) {e.printStackTrace(); }return value; }private synchronized void addTask(Runnable runnable) {mTaskQueue.add(runnable); try {if (mPoolThreadHandler==null){mSemaphorePoolThreadHandler.acquire(); }} catch (InterruptedException e) {e.printStackTrace(); }mPoolThreadHandler.sendEmptyMessage(0x110); }/** * 根据path为imageview设置图片 * @param key * @return */ private Bitmap getBitmapFromLruCache(String key) {return mLruCache.get(key); }private class ImageSize{int width; int height; }private class ImgBeanHolder{Bitmap bitmap; ImageView imageView; String path; } }
2.图片的列表
首先扫描手机上的所有图片信息,拿到图片数量最多的文件夹,直接显示在GridView上;并且扫描结束,得到一个所有包含图片的文件夹信息的List;
对于文件夹信息,我们单独创建了一个Bean:
public class FolderBean {private String dir;//当前文件夹路径 private String firstImamgPath;//第一张图片的路径 private String name;//文件夹的名字 private int count; //图片数量 public String getDir() {return dir; }public void setDir(String dir) {this.dir = dir; int indexOf = this.dir.lastIndexOf("/")+1; this.name=this.dir.substring(indexOf); }public String getFirstImamgPath() {return firstImamgPath; }public void setFirstImamgPath(String firstImamgPath) {this.firstImamgPath = firstImamgPath; }public String getName() {return name; }public void setName(String name) {this.name = name; }public int getCount() {return count; }public void setCount(int count) {this.count = count; } }
其次是扫描手机中的图片:
注意:在6.0系统以上,要手动开启读取内存卡的权限,否则程序是运行不起来的。
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {if (ContextCompat.checkSelfPermission(SelectPhotoActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(SelectPhotoActivity.this, new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE}, 1); }else{aboutScanPhoto();//未开启权限,先开启权限。以开启权限后,直接扫描图片 } }else{aboutScanPhoto();//扫描图片的方法 }
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {switch (requestCode){case 1:if(grantResults.length>0 &&grantResults[0] == PackageManager.PERMISSION_GRANTED){aboutScanPhoto(); }else {Toast.makeText(this, "请打开权限!", Toast.LENGTH_SHORT).show(); }break; default:} }
扫描图片的方法
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){Toast.makeText(this,"当前存储卡不可用!",Toast.LENGTH_LONG); return; } mProgressDialog= ProgressDialog.show(this,null,"正在加载。。。"); new Thread(){@Override public void run() {Uri mImgUri= MediaStore.Images.Media.EXTERNAL_CONTENT_URI; ContentResolver cr=SelectPhotoActivity.this.getContentResolver(); Cursor mCursor = 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); Set<String> mDirPaths=new HashSet<String>(); while (mCursor.moveToNext()){String path = mCursor.getString(mCursor.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.setFirstImamgPath(path); }if (parentFile.list()==null){continue; }int picSize=parentFile.list(new FilenameFilter() {@Override public boolean accept(File dir, String name) {if (name.endsWith(".jpg")||name.endsWith("jpeg")||name.endsWith("png")){return true; }return false; }}).length; folderBean.setCount(picSize); mFolderBeans.add(folderBean); if (picSize>mMaxCount){mMaxCount=picSize; mCurrentDir=parentFile; }}mCursor.close(); handler.sendEmptyMessage(0x110); } }.start();
然后我们通过handler发送消息,在handleMessage里面:
1、创建GridView的适配器,为我们的GridView设置适配器,显示图片;
2、创建我们的popupWindow了
private Handler handler=new Handler(){@Override public void handleMessage(Message msg) {if (msg.what==0x110){mProgressDialog.dismiss(); dataToView(); initPopupWindow(); }} };
dataToView()就是我们为view设置数据
private void dataToView() {if (mCurrentDir==null){Toast.makeText(this,"未扫描到任何图片",Toast.LENGTH_LONG).show(); return; }mImgs= Arrays.asList(mCurrentDir.list()); adapter = new ImageAdapter(this,mImgs,mCurrentDir.getAbsolutePath()); mGridView.setAdapter(adapter); mTvDirName.setText(mCurrentDir.getName()); mTvDirCount.setText(mMaxCount+""); }
我们还用到了一个GridView的adapter
public class ImageAdapter extends BaseAdapter {private String mDirPath; private List<String> mImgPaths; private LayoutInflater mInflater; private static List<String> mSelectImg=new LinkedList<>(); public ImageAdapter(Context context, List<String> mDatas, String dirPath) {this.mDirPath=dirPath; this.mImgPaths=mDatas; mInflater=LayoutInflater.from(context); }@Override public int getCount() {return mImgPaths.size(); }@Override public Object getItem(int position) {return mImgPaths.get(position); }@Override public long getItemId(int position) {return position; }@Override public View getView(final int position, View convertView, ViewGroup parent) {ViewHolder vh=null; if (convertView==null){convertView= mInflater.inflate(R.layout.item,parent,false); vh=new ViewHolder(); vh.mImg=convertView.findViewById(R.id.iv_item); vh.mSelect=convertView.findViewById(R.id.ib_select); convertView.setTag(vh); }else {vh = (ViewHolder) convertView.getTag(); }vh.mImg.setImageResource(R.mipmap.default_error); vh.mSelect.setImageResource(R.mipmap.btn_unselected); vh.mImg.setColorFilter(null); final String filePath=mDirPath+"/"+mImgPaths.get(position); // new ImageLoader(3, ImageLoader.Type.LIFO).loadImage(mDirPath + "/" + mImgPaths.get(position),vh.mImg); ImageLoader.getInStance(3, ImageLoader.Type.LIFO).loadImage(mDirPath+"/"+mImgPaths.get(position),vh.mImg); final ViewHolder finalVh = vh; vh.mImg.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View v) {//已经被选择 if (mSelectImg.contains(filePath)){mSelectImg.remove(filePath); finalVh.mImg.setColorFilter(null); finalVh.mSelect.setImageResource(R.mipmap.btn_unselected); }else{//未被选中 mSelectImg.add(filePath); finalVh.mImg.setColorFilter(Color.parseColor("#77000000")); finalVh.mSelect.setImageResource(R.mipmap.btn_selected); }}}); if (mSelectImg.contains(filePath)){vh.mImg.setColorFilter(Color.parseColor("#77000000")); vh.mSelect.setImageResource(R.mipmap.btn_selected); }return convertView; }public List<String> selectPhoto(){if (!mSelectImg.isEmpty()){return mSelectImg; }return null; }private class ViewHolder{ImageView mImg; ImageButton mSelect; } }
3.到这一步图片就已经显示在GridView中,下一步就是我们的popupWindow
要实现的效果是:点击底部的布局弹出我们的文件夹选择框,并且我们弹出框后面的Activity要变暗;
首先我们创建了一个popupWindow使用的类,和Activity类似:
public class ListImageDirPopupWindow extends PopupWindow {private int mWidth; private int mHeight; private View mConvertView; private List<FolderBean> mDatas; private ListView mListView; public interface OnDirSelectedListener{void onSelected(FolderBean folderBean); }public OnDirSelectedListener mListener; public void setOnDirSelectedListener(OnDirSelectedListener mListener) {this.mListener = mListener; }public ListImageDirPopupWindow(Context context, List<FolderBean> datas) {calWidthAndHeight(context); mConvertView= LayoutInflater.from(context).inflate(R.layout.popup_main,null); mDatas=datas; setContentView(mConvertView); setWidth(mWidth); setHeight(mHeight); setFocusable(true); setTouchable(true); setOutsideTouchable(true); setBackgroundDrawable(new BitmapDrawable()); setTouchInterceptor(new View.OnTouchListener() {@Override public 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= mConvertView.findViewById(R.id.lv_dir); mListView.setAdapter(new ListDirAdapter(context,mDatas)); }private void initEvent() {mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) {if (mListener!=null){mListener.onSelected(mDatas.get(position)); }}}); }/** * 计算popupWindow的宽度和高度 * @param context */ private void calWidthAndHeight(Context context) {WindowManager wm= (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics=new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); mWidth= outMetrics.widthPixels; mHeight= (int) (outMetrics.heightPixels*0.7); }private class ListDirAdapter extends ArrayAdapter<FolderBean>{private LayoutInflater mInflater; private List<FolderBean> mDatas; public ListDirAdapter(@NonNull Context context, List<FolderBean> datas) {super(context, 0, datas); mInflater= LayoutInflater.from(context); }@NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {ViewHolder vh=null; if (convertView==null){vh=new ViewHolder(); convertView= mInflater.inflate(R.layout.popup_item,parent,false); vh.mDirName=(TextView) convertView.findViewById(R.id.tv_dir_item_name); vh.mDirCount=(TextView) convertView.findViewById(R.id.tv_dir_item_count); vh.mImg= (ImageView) convertView.findViewById(R.id.iv_dir_image); convertView.setTag(vh); }else{vh= (ViewHolder) convertView.getTag(); }FolderBean bean = getItem(position); //重置 vh.mImg.setImageResource(R.mipmap.default_error); ImageLoader.getInStance(3, ImageLoader.Type.LIFO).loadImage(bean.getFirstImamgPath(),vh.mImg); vh.mDirName.setText(bean.getName()); vh.mDirCount.setText(bean.getCount()+""); return convertView; }private class ViewHolder{ImageView mImg; TextView mDirName; TextView mDirCount; }} }
然后我们需要和Activity交互,当我们点击某个文件夹的时候,外层的Activity需要改变它GridView的数据源,展示我们点击文件夹的图片。在这里我们创建一个接口OnDirSelectedListener ,对Activity设置回调;
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) {if (mListener!=null){mListener.onSelected(mDatas.get(position)); }} });
4.选择不同的文件夹
前面我们handleMessage中初始化调用popupWindow,通过activity的回调,实现选择文件夹显示图片
mImageDirPopupWindow=new ListImageDirPopupWindow(this,mFolderBeans); mImageDirPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {@Override public void onDismiss() {lightOn(); } }); mImageDirPopupWindow.setOnDirSelectedListener(new ListImageDirPopupWindow.OnDirSelectedListener() {@Override public void onSelected(FolderBean folderBean) {mCurrentDir=new File(folderBean.getDir()); mImgs= Arrays.asList(mCurrentDir.list(new FilenameFilter() {@Override public boolean accept(File dir, String name) {if (name.endsWith(".jpg")||name.endsWith("jpeg")||name.endsWith("png")){return true; }return false; }})); adapter=new ImageAdapter(SelectPhotoActivity.this,mImgs,mCurrentDir.getAbsolutePath()); mGridView.setAdapter(adapter); mTvDirCount.setText(mImgs.size()+""); mTvDirName.setText(folderBean.getName()); mImageDirPopupWindow.dismiss(); } });
5.图片展示在RecyclerView中
在ImageAdapter中定义了一个方法,获得选择图片的路径,点击“选择”按钮的时候,把这个路径的集合传过去
public List<String> selectPhoto(){if (!mSelectImg.isEmpty()){return mSelectImg; }return null; }
然后通过ImageLoader这个类将图片展示出来,我们在recyclerview设置图片展示方式为网格方式。
ImageLoader.getInStance(3, ImageLoader.Type.LIFO).loadImage(mDatas.get(position),holder.iv);
if (photoSelect!=null) {final SimpleAdapter mAdapter = new SimpleAdapter(this, photoSelect); mListView.setAdapter(mAdapter); mListView.setLayoutManager(new GridLayoutManager(this,3)); }
其中涉及到了一个adapter,是recyclerview所需要的
public class SimpleAdapter extends RecyclerView.Adapter<SimpleAdapter.MyViewHolder> {private LayoutInflater mInflater; private Context mContext; protected List<String> mDatas; public SimpleAdapter(Context mContext, List<String> mDatas) {this.mContext = mContext; this.mDatas = mDatas; mInflater=LayoutInflater.from(mContext); }@Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view = mInflater.inflate(R.layout.list_item, parent, false); MyViewHolder viewHolder=new MyViewHolder(view); return viewHolder; }@Override public void onBindViewHolder(final MyViewHolder holder, int position) {ImageLoader.getInStance(3, ImageLoader.Type.LIFO).loadImage(mDatas.get(position),holder.iv); }@Override public int getItemCount() {return mDatas.size(); }class MyViewHolder extends RecyclerView.ViewHolder {ImageView iv; public MyViewHolder(View itemView) {super(itemView); iv= itemView.findViewById(R.id.iv_photo); }} }
如果大家想学习RecyclerView的使用,可以看一下我的博客
https://blog.csdn.net/wen_haha/article/details/80775056
总结:本项目的主要代码基本已经贴出来了,感兴趣的朋友可以去下载本人的项目,最后,如有不对的地方,请多多指教.
Demo
CSDN地址:
https://download.csdn.net/download/wen_haha/10499827
Github地址:
https://github.com/kongkongdaren/SelectPhotoDemo
Android中仿微信选择图片并展示在RecyclerView中相关推荐
- android 微信相册功能,Android仿微信选择图片和拍照功能
本文实例为大家分享了 Android微信选择图片的具体代码,和微信拍照功能,供大家参考,具体内容如下 1.Android6.0系统,对于权限的使用都是需要申请,选择图片和拍照需要申请Manifest. ...
- Android仿微信选择图片
因为需要上传图片,选择图片的效果微信效果很好,所以我在网上找了一些仿微信的例子,但是都不是很全,所以我找了几个并和在一起,效果还行,不废话了上代码. 首先,是一个九宫格显示图片的页面.因为我的代码是f ...
- android高仿微信的图片查看
最近改造了别人的git项目(修复了一些bug,和新增了一些功能),实现高仿微信图片查看,传送门在此PicWatcher,效果图如下(gif显示的比较卡顿,实际体验是很OK的):
- Android高仿微信微博多图展示
NineGridLayout 1.简介 这是一个用于实现像微信朋友圈和微博的类似的九宫格图片展示控件,通过自定义viewgroup实现,使用方便. 多图根据屏幕适配,单张图片时需要自己指定图片的宽高: ...
- android访问图库,android通过访问相册获取图片并展示在ImageView中
第一步:添加相应的权限以及属性: ①在manifest中设置权限 ②在中设置相应属性,这一步很关键,解决了我在这一方面最后的一个有关deny的权限问题 android:requestLegacyExt ...
- android通过访问相册获取图片并展示在ImageView中
第一步:添加相应的权限以及属性: ①在manifest中设置权限 <uses-feature android:name="android.hardware.camera" / ...
- android仿微信图片上传进度,android高仿微信发布动态(选择图片)
[实例简介]Android 超高仿微信图片选择器 [实例截图] [核心代码] public class MainActivity extends Activity implements OnImage ...
- android 仿微信选取相册_Android模仿微信选择图片
前言 最近公司需要做一个类似微信那种选择头像和上传图片的功能,本想上github上找的,后来想了想,还是自己做一个,不仅方便以后用(毕竟自己写的修改起来也比较方便),还可以学到一些知识,废话少说,先看 ...
- android 仿微信选取相册_Android 实现一个仿微信的图片选择器
现在大部分的App都上传图片的功能,比如设置用户头像.聊天发送图片.发表动态.论坛帖子等.上传图片需要先从选择手机中选择要上传的图片,所以图片选择器在App中是很常见的组件,一般的手机都会自带一个图片 ...
- android仿微信的图片选择器
PictureSelector 项目地址: arvinljw/PictureSelector 简介:包含:多选.单选.拍照.预览.裁剪:兼容大图,兼容 7.0 更多: 作者 提 Bug ...
最新文章
- [GWCTF 2019]pyre.pyc [CISCN2018]2ex
- 求最小生成树-Prim(普里姆算法)
- wxIntegerValidator< T > 类模板用法
- [数据库] Navicat for Oracle设置唯一性和递增序列实验
- 榜单类应用我所喜欢的算法
- [PHP 安全] pcc —— PHP 安全配置检测工具
- 【TensorFlow官方文档】MNIST机器学习入门
- HDU 5410 CRB and His Birthday ——(完全背包变形)
- 生产者-消费者 BlockingQueue 运用示例
- 删除了电脑硬盘的数据能恢复吗,硬盘数据删除了还能恢复吗
- Arduino笔记五三轴陀螺仪L3G4200D
- VScode下载安装及使用教程
- CSR3026开发问题总结-1
- r7 2700X装Linux,R7-2700X配什么主板?AMD锐龙7 2700X主板推荐 (全文)
- 浏览器访问IPv6地址
- Scala+HuffmanCoding实现无损压缩
- 网络数据采集技术snmp/netflow/sflow/network telemetry简介
- 尚硅谷python入门
- 一图看懂自然资源资金监测监管系统
- R语言与克朗巴哈alpha系数