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