上次我写了一个关于MVC框架怎么用在项目里,相关基类的封装方法。其实就是为今天的这篇文章做准备。相册,也就是图片选择器,在我们的项目中用的还是比较多的。但就我了解,多数程序员还是通过引用第三方框架实现这个功能。但是如果UI有要求,或者我们自己有什么特殊需要,可能根本无法满足。

所以今天我给大家带来自己做的相册。既给你一个完整的相册,也给你一套做相册的方法。同时我们可以从其中学到很多的知识点。

在开始之前要说明一下,这个相册要依赖之前讲的MVC框架,所以有两种选择,一种是把那个框架先搭建好,然后无感复制粘贴,就可以实现相册效果。或者自己分析,用现有框架也可以实现。

首先在res下建立anim

其中有两个类

slide_down.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><translateandroid:duration="200"android:fromXDelta="0"android:fromYDelta="0"android:toXDelta="0"android:toYDelta="100%"/>
</set>

slide_up.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><translateandroid:duration="200"android:fromXDelta="0"android:fromYDelta="100%"android:toXDelta="0"android:toYDelta="0"/>
</set>

这两个是选择文件夹的动画,在styles.xml中加入

<style name="dir_popupwindow_anim"><item name="android:windowEnterAnimation">@anim/slide_up</item><item name="android:windowExitAnimation">@anim/slide_down</item>
</style>

这样动画就准备好了。接着我们准备布局几个图片

这是我准备的三张图片,左边的两张是图片右上角和文件夹选择弹框的最右边是否选中的标识图标。第三张是一个图片未加载出时的占位图片。

清单文件中我们要加入两条权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

接着我们有四个布局

activity_main.xml

这个布局文件是相册主页面的布局,代码如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><GridViewandroid:id="@+id/mGridView"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:cacheColorHint="@android:color/transparent"android:horizontalSpacing="3dp"android:listSelector="@android:color/transparent"android:numColumns="4"android:stretchMode="columnWidth"android:verticalSpacing="3dp" /><RelativeLayoutandroid:id="@+id/mBottomly"android:layout_width="match_parent"android:layout_height="45dp"android:background="#000"><TextViewandroid:id="@+id/mDirName"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_marginLeft="15dp"android:text="文件夹名称"android:textColor="#fff"android:textSize="14sp" /><TextViewandroid:id="@+id/mDirCount"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:layout_marginRight="15dp"android:text="图片数量"android:textColor="#fff"android:textSize="14sp" /></RelativeLayout>
</LinearLayout>

如果我们需要修改相册的样式,就在这布局中修改

然后是item_album.xml,这个是每个照片格子的布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:layout_width="match_parent"android:layout_height="100dp"android:id="@+id/id_item_image"android:src="@mipmap/noimage"android:scaleType="centerCrop"/><ImageButtonandroid:layout_width="15dp"android:layout_height="15dp"android:id="@+id/id_item_select"android:layout_alignParentRight="true"android:layout_alignParentTop="true"android:layout_marginRight="3dp"android:layout_marginTop="3dp"android:background="@null"android:clickable="false"android:scaleType="fitXY"android:src="@mipmap/ic_unchoose"/>
</RelativeLayout>

接着是item_popup_main.xml,这个是文件夹选择弹框的布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:padding="5dp"><ImageViewandroid:layout_width="100dp"android:layout_height="100dp"android:id="@+id/id_id_dir_item_image"android:layout_alignParentLeft="true"android:paddingTop="9dp"android:paddingBottom="17dp"android:paddingLeft="12dp"android:paddingRight="12dp"android:scaleType="fitXY"android:src="@mipmap/noimage"android:layout_centerVertical="true"/><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_marginLeft="10dp"android:layout_toRightOf="@id/id_id_dir_item_image"android:orientation="vertical"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/id_dir_item_name"android:text="所有图片"android:textSize="12sp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/id_dir_item_count"android:layout_gravity="center"android:text="1212张"android:textSize="10sp"android:textColor="#444"/></LinearLayout><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:layout_marginRight="20sp"android:src="@mipmap/ic_choose"/>
</RelativeLayout>

最后是我们的文件夹选项弹框主布局popup_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/white"><ListViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/id_list_dir"android:divider="#eee3d9"android:dividerHeight="1px"/>
</RelativeLayout>

一顿操作猛如虎,终于把我们的资源都整齐了。现在可以开始安心写java代码了。首先是我们的图片加载类

/*** 图片加载类* @author WaterWood*/
public class ImageLoader {/*** 单例对象*/private static ImageLoader mInstance;/*** 图片缓存的核心对象*/private LruCache<String,Bitmap> mLruCache;/*** 线程池*/private ExecutorService mThreadPool;/*** 默认线程个数*/private static final int DEAFAULT_THREAD_COUNT = 1;/*** 队列方式*/private Type mType = Type.LIFO;/*** 任务队列*/private LinkedList<Runnable> mTaskQueue;/*** 后台轮询线程*/private Thread mPoolThread;/*** 与线程绑定的Handler,对线程中的Queue发送消息*/private Handler mPoolThreadHandler;/*** UI线程的一个Handler*/private Handler mUIHandler;/*** addTask使用mPoolThreadHandler的信号量,防止mPoolThreadHandler为空*/private Semaphore mSemaphorePoolThreadHandler = new Semaphore(0);/*** 是内部队列执行完,再从TaskQueue中取下一个*/private Semaphore mSemaphoreThreadPool;/*** 私有构造方法*/private ImageLoader(int threadCount,Type type){init(threadCount,type);}/*** 单例获取* @return*/public static ImageLoader getInstance(){if (mInstance == null){synchronized (ImageLoader.class){if (mInstance == null) {mInstance = new ImageLoader(DEAFAULT_THREAD_COUNT,Type.LIFO);}}}return mInstance;}/*** 单例获取* @return*/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 enum Type{FIFO,LIFO}/*** 一系列的初始化操作* @param threadCount* @param type*/private void init(int threadCount,Type type){mPoolThread = new Thread(){@Overridepublic void run() {Looper.prepare();mPoolThreadHandler = new Handler() {@Overridepublic 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){@Overrideprotected int sizeOf(String key, Bitmap value) {return value.getRowBytes()*value.getHeight();}};mThreadPool = Executors.newFixedThreadPool(threadCount);mTaskQueue = new LinkedList<Runnable>();mType = type;mSemaphoreThreadPool = new Semaphore(threadCount);}/*** 根据path加载图片* @param path* @param imageView*/public void loadImage(final String path, final ImageView imageView){imageView.setTag(path);if (mUIHandler == null){mUIHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {//获得图片,为imageView回调设置图片ImgBeanHolder holder = (ImgBeanHolder) msg.obj;Bitmap bm = holder.bitmap;ImageView imageview = holder.imageView;String path = holder.path;//将path与getTag存储路径进行比较if (imageview.getTag().toString().equals(path)){imageview.setImageBitmap(bm);}}};}Bitmap bm = getBitmapFromLruCache(path);if (bm!=null){//内存中有的情况refreshBitmap(path,imageView,bm);}else{//内存中没有的情况addTask(new Runnable(){@Overridepublic void run() {if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {ImageSize imageSize = getImageViewSize(imageView);Bitmap bm = decodeSampledBitmapFromPath(path,imageSize.width,imageSize.height);addBitmapToLruCache(path,bm);refreshBitmap(path,imageView,bm);mSemaphoreThreadPool.release();}}});}}private Bitmap getBitmapFromLruCache(String key){return mLruCache.get(key);}private class ImgBeanHolder{Bitmap bitmap;ImageView imageView;String path;}private synchronized void addTask(Runnable runnable){mTaskQueue.add(runnable);try {if (mPoolThreadHandler==null) {mSemaphorePoolThreadHandler.acquire();}} catch (InterruptedException e) {e.printStackTrace();}mPoolThreadHandler.sendEmptyMessage(0x110);}private Runnable getTask(){if (mType == Type.FIFO){return mTaskQueue.removeFirst();}else if (mType == Type.LIFO){return mTaskQueue.removeLast();}return null;}/*** 计算压缩尺寸* @param imageView* @return*/private ImageSize getImageViewSize(ImageView imageView){ImageSize imageSize = new ImageSize();RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) imageView.getLayoutParams();DisplayMetrics displayMetrics = imageView.getContext().getResources().getDisplayMetrics();int width = imageView.getWidth();if (width == 0){width = lp.width;//获取imageview在layout中声明的宽度}if (width<=0){width = getImageViewFieldValue(imageView,"mMaxWidth");}if (width<=0){width = displayMetrics.widthPixels;}int height = imageView.getHeight();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;}private class ImageSize{int width;int height;}/*** 将path转换为bimap带压缩* @param path* @param width* @param height* @return*/protected Bitmap decodeSampledBitmapFromPath(String path,int width,int height){BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeFile(path,options);options.inSampleSize = caculateInSampleSize(options,width,height);options.inJustDecodeBounds = false;Bitmap bitmap = BitmapFactory.decodeFile(path,options);return bitmap;}/*** 计算压缩比* @param options* @param reqWidth* @param reqHeight* @return*/private int caculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){int width = options.outWidth;int height = options.outHeight;int inSampleSize = 1;if (width>reqWidth || height > reqHeight){int widthRadio = Math.round(width*1.0f/reqWidth);int heightRadio = Math.round(height*1.0f/reqHeight);inSampleSize = Math.max(widthRadio,heightRadio);}return inSampleSize;}/*** 将bitmap加入内存缓存中* @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 imageView* @param bitmap*/private void refreshBitmap(String path,ImageView imageView,Bitmap bitmap){Message message = Message.obtain();ImgBeanHolder holder = new ImgBeanHolder();holder.bitmap = bitmap;holder.path = path;holder.imageView = imageView;message.obj = holder;mUIHandler.sendMessage(message);}/*** 通过反射获取任何对象的任何属性值* @param object* @param fieldName* @return*/private static int getImageViewFieldValue(Object object,String fieldName){try {int value = 0;Field field = ImageView.class.getDeclaredField(fieldName);field.setAccessible(true);int fieldValue = field.getInt(object);if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE){value = fieldValue;}return value;} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return 0;}
}

接着是图片文件夹实体

public class FolderBean {/*** 文件夹路径*/private String dir;/*** 第一张图片路径*/private String firstImgPath;/*** 文件夹名称*/private String name;/*** 文件数量*/private int count;public String getDir() {return dir;}public void setDir(String dir) {this.dir = dir;int lastIndexOf = this.dir.lastIndexOf("/");this.name = this.dir.substring(lastIndexOf+1);}public String getFirstImgPath() {return firstImgPath;}public void setFirstImgPath(String firstImgPath) {this.firstImgPath = firstImgPath;}public String getName() {return name;}public int getCount() {return count;}public void setCount(int count) {this.count = count;}
}

图片主界面GridView的Adapter

public class ImageAdapter extends BaseAdapter {private String mDirPath;private List<String> mImgPaths;private LayoutInflater mInflater;private Context context;private static Set<String> mSeletedImg = new HashSet<String>();public ImageAdapter(Context context, List<String> mDatas, String dirPath){this.context = context;this.mDirPath = dirPath;this.mImgPaths = mDatas;mInflater = LayoutInflater.from(context);}@Overridepublic int getCount() {return mImgPaths.size();}@Overridepublic Object getItem(int position) {return mImgPaths.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;if (convertView == null){convertView = mInflater.inflate(R.layout.item_album,parent,false);viewHolder = new ViewHolder();viewHolder.mImg = convertView.findViewById(R.id.id_item_image);viewHolder.mSelect = convertView.findViewById(R.id.id_item_select);//这里要根据手机尺寸修改一下图片的大小int widthAndHeight = (CommonUtils.getScreenWidth(context) - CommonUtils.dp2px(context,9))/4;RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) viewHolder.mImg.getLayoutParams();params.width = widthAndHeight;params.height = widthAndHeight;viewHolder.mImg.setLayoutParams(params);convertView.setTag(viewHolder);}else{viewHolder = (ViewHolder) convertView.getTag();}//重置状态viewHolder.mImg.setImageResource(R.mipmap.noimage);viewHolder.mSelect.setImageResource(R.mipmap.ic_unchoose);viewHolder.mImg.setColorFilter(null);ImageLoader.getInstance(3, ImageLoader.Type.LIFO).loadImage(mDirPath+"/"+mImgPaths.get(position),viewHolder.mImg);final String filePath = mDirPath+"/"+mImgPaths.get(position);final ViewHolder finalViewHolder = viewHolder;viewHolder.mImg.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (mSeletedImg.contains(filePath)){//已经被选择mSeletedImg.remove(filePath);finalViewHolder.mImg.setColorFilter(null);finalViewHolder.mSelect.setImageResource(R.mipmap.ic_unchoose);}else{//未被选择mSeletedImg.add(filePath);finalViewHolder.mImg.setColorFilter(Color.parseColor("#77000000"));finalViewHolder.mSelect.setImageResource(R.mipmap.ic_choose);}
//                notifyDataSetChanged();}});if (mSeletedImg.contains(filePath)){viewHolder.mImg.setColorFilter(Color.parseColor("#77000000"));viewHolder.mSelect.setImageResource(R.mipmap.ic_choose);}return convertView;}private class ViewHolder{ImageView mImg;ImageButton mSelect;}
}

文件夹弹框的Adapter

public class ListImageDirPopupWindow extends PopupWindow{private int mWidth;private int mHeight;private View mConvertView;private ListView mListView;private List<FolderBean> mDatas;private OnDirSelectedListener 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() {@Overridepublic boolean onTouch(View v, MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_OUTSIDE){dismiss();return true;}return false;}});initView(context);initEvent();}private void initView(Context context) {mListView = mConvertView.findViewById(R.id.id_list_dir);mListView.setAdapter(new ListDirAdapter(context,mDatas));}private void initEvent(){mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {if (mListener!=null){mListener.onSelected(mDatas.get(position));}}});}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(Context context,List<FolderBean> objects) {super(context,0,objects);mInflater = LayoutInflater.from(context);}@NonNull@Overridepublic View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {ViewHolder holder = null;if (convertView == null){holder = new ViewHolder();convertView = mInflater.inflate(R.layout.item_popup_main,parent,false);holder.mImg = convertView.findViewById(R.id.id_id_dir_item_image);holder.mDirName = convertView.findViewById(R.id.id_dir_item_name);holder.mDirCount = convertView.findViewById(R.id.id_dir_item_count);convertView.setTag(holder);}else{holder = (ViewHolder) convertView.getTag();}FolderBean bean = getItem(position);//重置holder.mImg.setImageResource(R.mipmap.noimage);ImageLoader.getInstance().loadImage(bean.getFirstImgPath(),holder.mImg);holder.mDirCount.setText(bean.getCount()+"");holder.mDirName.setText(bean.getName());return convertView;}private class ViewHolder{ImageView mImg;TextView mDirName;TextView mDirCount;}}public interface OnDirSelectedListener{void onSelected(FolderBean folderBean);}public void setmListener(OnDirSelectedListener mListener) {this.mListener = mListener;}
}

主界面Activity

public class MainActivity extends WaterPermissionActivity<MainModel> implements MainCallback {/*** 控件四个*/private GridView mGridView;private RelativeLayout mBottomly;private TextView mDirName;private TextView mDirCount;/*** 图片数据集*/private List<String> mImgs;/*** 对应当前文件夹的File对象*/private File mCurrentDir;/*** 当前文件夹中的文件个数*/private int mMaxCount;/*** 文件夹列表*/private List<FolderBean> mFolderBeans = new ArrayList<>();/*** 进度条弹框*/private ProgressDialog mProgressDialog;/*** 弹框*/private ListImageDirPopupWindow mDirPopupWindow;/*** 该参数负责子线程查询图片后通知主线程更新UI*/private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {if (msg.what == 0x110){mProgressDialog.dismiss();//消失对话框data2View();initDirPopupWindow();}}};/*** 图片适配器*/private ImageAdapter mImgAdapter;@Overrideprotected MainModel getModelImp() {return new MainModel(this,this);}@Overrideprotected int getContentLayoutId() {return R.layout.activity_main;}@Overrideprotected void initWidget() {mGridView = findViewById(R.id.mGridView);mBottomly = findViewById(R.id.mBottomly);mDirName = findViewById(R.id.mDirName);mDirCount = findViewById(R.id.mDirCount);}@Overrideprotected void initData() {if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){Toast.makeText(this,"当前存储卡不可用!",Toast.LENGTH_SHORT).show();return;}requestPermission(READ_EXTERNAL_STORAGE);mBottomly.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mDirPopupWindow.setAnimationStyle(R.style.dir_popupwindow_anim);mDirPopupWindow.showAsDropDown(mBottomly,0,0);lightOff();}});}@Overrideprotected void doSDRead() {requestPermission(WRITE_EXTERNAL_STORAGE);}@Overrideprotected void doSDWrite() {mProgressDialog = ProgressDialog.show(this,null,"正在加载...");new Thread(){@Overridepublic void run() {Uri mImgUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;ContentResolver cr = MainActivity.this.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);Set<String> mDirPaths = new HashSet<String>();while (cursor.moveToNext()){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;}}cursor.close();mHandler.sendEmptyMessage(0x110);}}.start();}protected void data2View(){if (mCurrentDir == null){Toast.makeText(this,"未扫描到任何图片",Toast.LENGTH_SHORT).show();return;}mImgs = Arrays.asList(mCurrentDir.list());mImgAdapter = new ImageAdapter(this,mImgs,mCurrentDir.getAbsolutePath());mGridView.setAdapter(mImgAdapter);mDirCount.setText(mMaxCount+"");mDirName.setText(mCurrentDir.getName());}private void initDirPopupWindow(){mDirPopupWindow = new ListImageDirPopupWindow(this,mFolderBeans);mDirPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {@Overridepublic void onDismiss() {lightOn();}});mDirPopupWindow.setmListener(new ListImageDirPopupWindow.OnDirSelectedListener() {@Overridepublic void onSelected(FolderBean folderBean) {mCurrentDir = new File(folderBean.getDir());mImgs = Arrays.asList(mCurrentDir.list(new FilenameFilter() {@Overridepublic boolean accept(File dir, String filename) {if (filename.endsWith(".jpg")||filename.endsWith(".jpeg")||filename.endsWith(".png")){return true;}return false;}}));mImgAdapter = new ImageAdapter(MainActivity.this,mImgs,mCurrentDir.getAbsolutePath());mGridView.setAdapter(mImgAdapter);mDirCount.setText(mImgs.size()+"");mDirName.setText(folderBean.getName());mDirPopupWindow.dismiss();}});}private void lightOn(){WindowManager.LayoutParams lp = getWindow().getAttributes();lp.alpha = 1.0f;getWindow().setAttributes(lp);}private void lightOff(){WindowManager.LayoutParams lp = getWindow().getAttributes();lp.alpha = 0.3f;getWindow().setAttributes(lp);}
}

Callback和Model这里先省略了。

这样我们就可以无感制作出一个完整的相册,是不是很方便。时间有限,大家可以自己分析其中的知识点。能用到的很多,之后我会一一拆出来分别写出博客以供使用。

资源地址

之前没有贴上资源地址,所以很多小伙伴不知道问我,可以在这里下载。不过我更建议各位小伙伴按照上述代码一个类一个类考一下,这样可以加深我们对于相册制作的了解。

Android中相册的实现相关推荐

  1. Android中使用封装的OKHttp上传图片,从相机和相册中获取图片并剪切

    Android中使用OKHttp上传图片,从相机和相册中获取图片并剪切 效果: 注意: 1:网络权限 <uses-permission android:name="android.pe ...

  2. Android中使用OKHttp上传图片,从相机和相册中获取图片并剪切

    Android中使用OKHttp上传图片,从相机和相册中获取图片并剪切 效果: 注意: 1:网络权限 <uses-permission android:name="android.pe ...

  3. android点击选择相册,android: 从相册中选择照片

    虽然调用摄像头拍照既方便又快捷,但并不是每一次我们都需要去当场拍一张照片的. 因为每个人的手机相册里应该都会存有许许多多张照片,直接从相册里选取一张现有的照 片会比打开相机拍一张照片更加常用.一个优秀 ...

  4. android 相册列表,Android中的图库相册列表

    我想获取手机相册的相册列表. 我试过码位它不工作Android中的图库相册列表 public void onCreate(Bundle savedInstanceState) { super.onCr ...

  5. Android 中拍照、相册选择、裁剪照片

    一个多月没总结知识点了,差点连博客账号都忘了...好了,步入正题,在 Android 中调用摄像头拍照获取图片或者是从相册中选取图片是很常见的功能,比如某些 APP 上传头像的功能就是一个例子. ** ...

  6. android中调用系统功能 来显示本地相册图片 拍照 视频 音频功能

    android中调用系统功能 来显示本地相册图片 拍照 视频 音频功能 效果图如下: 本地相册跟拍照可直接调用系统功能 Intent img = new Intent(MediaStore.ACTIO ...

  7. Android中通过访问本地相册或者相机设置用户头像

    目前几乎所有的APP在用户注册时都会有设置头像的需求,大致分为三种情况: (1)通过获取本地相册的图片,经过裁剪后作为头像. (2)通过启动手机相机,现拍图片然后裁剪作为头像. (3)在APP中添加一 ...

  8. android相册如何加背景音乐,Android中添加背景音乐的两种方法

    前些天在尝试自己写一个Android小游戏--flybird 基本功能实现了,就想添加声音,然后上网查了查,大多是一样,可是用到我这,有些却不可以用,所以我还用了两种方法. 下面谈谈这两种方法. 方法 ...

  9. android 自定义相册选择,Android通过手机拍照或从本地相册选取图片设置头像

    像微信.QQ.微博等社交类的APP,通常都有设置头像的功能,设置头像通常有两种方式: 1.让用户通过选择本地相册之类的图片库中已有的图像,裁剪后作为头像. 2.让用户启动手机的相机拍照,拍完照片后裁剪 ...

最新文章

  1. 动态规划 最小编辑代价
  2. 在PowerShell中创建对象并添加属性成员
  3. Linux 设置时区
  4. 【数据库】Ubuntu18.04安装MySQL详解
  5. c++ 删除二叉树的子树_平衡二叉树
  6. React开发(199):参数请求错误修改请求
  7. 加载中图片 转圈_对话洛可可平面设计师:平面设计中的效率瓶颈
  8. 2000年科技计算机,2000年华中科技大学计算机研究生机试真题 对称矩阵
  9. PHP实现简单的计算器
  10. ansible的delegate_to、connection、和local_action
  11. ie6下 jsonp无响应的问题
  12. java 按分割为数组中_[Java教程]JS中,split()用法(将字符串按指定符号分割成数组)...
  13. 某文件在桌面上,命令窗口中找不到,因为桌面是两个目录合成的
  14. 基于ZigBee cc2530单片机多传感器的智能阳台仿真设计与实现
  15. 奇偶性与魔术(一)——奇偶性的数学本质
  16. Python2/3的中、英文字符编码与解码输出: UnicodeDecodeError: 'ascii' codec can't decode/encode...
  17. android动态指示箭头,自定义选项卡指示器(箭头向下指示器)
  18. springBoot集成websocket报java.lang.IllegalStateException: Failed to register @ServerEndpoint class:错误
  19. win10扩展c盘容量(2022-11-17)亲测可用
  20. ISCE-stamps的sbas时序insar基本流程(一):ISCE的预处理

热门文章

  1. 蒙特卡罗(Monte Carlo)方法计算圆周率π
  2. 山东科技大学第二届ACM校赛解题报告
  3. 中国电信无线网服务器,如何使用路由器共享电信天翼无线网络?
  4. 常用损失函数和评价指标总结
  5. Masonry约束自定义TableViewCell自适应行高的约束冲突的问题
  6. uni-app学习 form表单(五)
  7. 【思维导图怎么画】万彩脑图大师教程 | 添加链接
  8. 使用D触发器完成带有异步清零clrn和同步使能wen的8位寄存器
  9. 两家“国网”合建5G,三大运营商“好日子到头”?
  10. 浅谈千万级高性能高并发网站架构