一 翔曲、設(shè)計目標
在進行開發(fā)的時候有現(xiàn)成的圖片選擇器,通過內(nèi)容提供器可以直接打開系統(tǒng)相冊進行選擇,得到要選擇的圖片,界面比較簡陋,但可以實現(xiàn)簡單的圖片選擇功能惧财。要設(shè)計一個自己的圖片選擇器偎漫,要明確設(shè)計的目標:
- 訪問手機的媒體庫压怠,獲得有圖片的文件夾
- 為文件夾列表設(shè)置一個下拉選擇框杯巨,下拉選擇要訪問的文件夾
- 選擇目標文件夾剖张,遍歷文件夾所有圖片畅形,利用Glide進行圖片加載最后在RecyclerView中進行顯示
- 設(shè)置一個ImageView對要選擇的圖片進行預(yù)覽蛉顽,點擊預(yù)覽圖進入圖片編輯頁面
以上大概就是我要實現(xiàn)的圖片選擇器的設(shè)計目標护姆,對設(shè)計目標進行分析后確定使用的工具有
- MediaStore媒體庫
- Handler異步加載
- Glide圖片加載
- RecyclerView列表顯示
- Spinner下拉選擇框
二矾端、 相關(guān)內(nèi)容學(xué)習(xí)
1. MediaStore
MediaStore這個類是android系統(tǒng)提供的一個多媒體數(shù)據(jù)庫,android 中多媒體信息都可以從這里提取卵皂。這個MediaStore包括了多媒體數(shù)據(jù)庫的所有信息秩铆,包括音頻,視頻和圖像,android把所有的多媒體數(shù)據(jù)庫接口 進行了封裝,所有的數(shù)據(jù)庫不用自己進行創(chuàng)建殴玛,直接調(diào)用利用ContentResolver去掉用那些封裝好的接口就可以進行數(shù)據(jù)庫的操作了捅膘。今天我就介紹 一些這些接口的用法。在進行圖片選擇的時候滚粟,就是通過MediaStore進行查找圖片路徑等操作寻仗。
MediaStore詳細字段參考鏈接:https://blog.csdn.net/lemon_blue/article/details/52353851
2. Handler異步加載
Handler主要用于異步消息的處理: 有點類似輔助類,封裝了消息投遞凡壤、消息處理等接口署尤。當發(fā)出一個消息之后,首先進入一個消息隊列亚侠,發(fā)送消息的函數(shù)即刻返回曹体,而另外一個部分在消息隊列中逐一將消息取出,然后對消息進行處理盖奈,也就是發(fā)送消息和接收消息不是同步的處理混坞。 這種機制通常用來處理相對耗時比較長的操作。
3. Glide圖片加載
一行代碼
Glide.with(context).load(url).into(imageView);
4. RecyclerView
參考之前的內(nèi)容
5. Spinner控件
參考代碼钢坦,spinner的適配器是ArrayAdapter<String>類型。
三啥酱、 代碼分析
1. 圖片工具類
定義一個工具類對媒體庫進行查詢爹凹,返回所以包含圖片的文件夾列表,注釋比較詳細镶殷。
public class ImageUtils {
/**
* 圖片文件夾名列表
*/
public static List<String> folderNameList = new ArrayList<> ();
/**
* 圖片文件夾路徑列表
*/
public static List<String> folderPathList = new ArrayList<> ();
/**
* 文件夾列表禾酱,用于判斷當前文件夾是否遍歷
*/
public static HashSet<String> mFolderList = new HashSet<> ();
/**
* 得到圖片文件夾,利用ContentResolver進行遍歷
* @param context
* @param handler
*/
public static void getFolder(final Context context, final Handler handler) {
new Thread (new Runnable () {
@Override
public void run() {
Uri imageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver contentResolver = context.getContentResolver ();
//文件夾選擇條件绘趋,MIME_TYPE媒體類型為JPEG和png的文件
String selection = MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?";
String[] selectionArgs = new String[]{"image/jpeg", "image/png"};
Cursor cursor = contentResolver.query (imageUri, null, selection, selectionArgs, MediaStore.Images.Media.DATE_MODIFIED);
while (cursor.moveToNext ()) {
String imagePath = cursor.getString (cursor.getColumnIndex (MediaStore.Images.Media.DATA));
//獲得當前文件夾路徑
File imageFolder = new File (imagePath).getParentFile ();
if(imageFolder == null) {
continue;
}
String folderPath = imageFolder.getAbsolutePath ();
//利用HashSet進行判斷當前文件夾是否被選擇
if(mFolderList.contains (folderPath)) {
continue;
} else {
mFolderList.add (folderPath);
folderPathList.add (folderPath);
//截取文件夾的最后一個"/"后面的字符串作為文件夾名稱
int indexOfPath = folderPath.lastIndexOf ("/");
String folderName = folderPath.substring (indexOfPath);
folderNameList.add (folderName);
}
}
cursor.close ();
mFolderList.clear ();
Message msg = new Message ();
msg.what = 1;
handler.sendMessage (msg);
}
}).start ();
}
}
2. 圖片選擇器主界面
public class CreateFragment extends Fragment {
/**
* 圖片選擇界面的預(yù)覽圖
*/
private ImageView iv_selectedImage;
/**
* 顯示當前文件夾的圖片列表
*/
private RecyclerView recyclerView;
/**
* 圖片列表適配器
*/
private CreateAdapter createAdapter;
/**
* 列表網(wǎng)格布局管理器
*/
private GridLayoutManager gridLayoutManager;
/**
* 文件夾下拉選擇欄
*/
private Spinner spinner;
/**
* 下拉選擇欄適配器
*/
private ArrayAdapter<String> spinnerAdapter;
/**
* 文件夾下拉選擇欄顯示列表
*/
private List<String> nameList = new ArrayList<> ();
/**
* 所有圖片文件夾路徑
*/
private List<String> imageList = new ArrayList<> ();
/**
* 當前文件夾下所有圖片路徑
*/
private static List<String> selectedImageList = new ArrayList<> ();
/**
* 當前預(yù)覽圖片路徑
*/
private static String selectedImagePath;
public CreateFragment() {
}
/**
* 獲取當前fragment實例
* @return
*/
public static Fragment newInstance() {
Bundle args = new Bundle ();
Fragment fragment = new CreateFragment ();
fragment.setArguments (args);
return fragment;
}
/**
* Handler處理圖片工具類得到的文件夾列表和更新UI
*/
private Handler handler = new Handler () {
@Override
public void handleMessage(Message msg) {
super.handleMessage (msg);
if(msg.what == 1) {
nameList = ImageUtils.folderNameList;
imageList = ImageUtils.folderPathList;
spinnerAdapter = new ArrayAdapter<String> (getActivity (), R.layout.support_simple_spinner_dropdown_item, nameList);
spinner.setAdapter (spinnerAdapter);
}
}
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate (R.layout.fragment_create, container, false);
initView (view);
ImageUtils.getFolder (getActivity (), handler);
return view;
}
/**
* 初始化界面
* @param view
*/
public void initView(View view) {
nameList.clear ();
iv_selectedImage = view.findViewById (R.id.iv_create_selected_image);
//預(yù)覽圖點擊事件
iv_selectedImage.setOnClickListener (new View.OnClickListener () {
@Override
public void onClick(View view) {
Intent intent = new Intent (getActivity (), SelectedImageActivity.class);
intent.putExtra ("path", selectedImagePath);
startActivity (intent);
}
});
spinner = view.findViewById (R.id.create_spinner);
recyclerView = view.findViewById (R.id.create_select_recycler_view);
onSpinnerSelectedListener ();
}
/**
* 設(shè)置下拉選擇欄的選擇事件
*/
public void onSpinnerSelectedListener() {
spinner.setOnItemSelectedListener (new AdapterView.OnItemSelectedListener () {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
selectedImageList.clear ();
//利用排序工具類進行排序返回當前文件夾圖片路徑列表
selectedImageList = SortUtils.sortImage (imageList, i);
setRecyclerView ();
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
}
/**
* 得到當前文件夾的圖片路徑颤陶,為RecyclerView設(shè)置適配器,進行顯示
*/
private void setRecyclerView() {
//布局管理器一定要設(shè)置
gridLayoutManager = new GridLayoutManager (getActivity (), 4, GridLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager (gridLayoutManager);
createAdapter = new CreateAdapter (getActivity (), selectedImageList);
recyclerView.setAdapter (createAdapter);
/**
* 為RecyclerView的item設(shè)置點擊事件
*/
createAdapter.setOnItemClickListener (new CreateAdapter.OnItemClickListener () {
@Override
public void onClick(int position) {
selectedImagePath = selectedImageList.get (position);
Glide.with (getActivity ()).load (selectedImagePath).into (iv_selectedImage);
}
});
//加載每個文件夾的第一張圖片
selectedImagePath = selectedImageList.get (0);
Glide.with (getActivity ()).load (selectedImagePath).into (iv_selectedImage);
}
}
3. 按圖片修改時間排序工具類
public class SortUtils {
private static List<String> selectedImageList = new ArrayList<> ();
public static List<String> sortImage(List<String> imageList,int position) {
String imageFolder = imageList.get (position);
//獲取當前文件夾
File imageFile = new File (imageFolder).getAbsoluteFile ();
//獲取當前文件夾下的文件
File[] files = imageFile.listFiles (new FileFilter () {
@Override
public boolean accept(File file) {
String imageName = file.getName ().toString ();
if(imageName.endsWith (".jpeg") || imageName.endsWith (".jpg") || imageName.endsWith (".png")) {
return true;
}
return false;
}
});
//
List<File> fileList = new ArrayList<> ();
for (int j = 0; j < files.length; j++) {
fileList.add (files[j]);
}
Collections.sort (fileList, new FileComparator ());
for (int j = 0; j < fileList.size (); j++) {
selectedImageList.add (fileList.get (j).getAbsolutePath ());
}
return selectedImageList;
}
/**
* 文件比較類陷遮,實現(xiàn)Comparator接口滓走,重寫compare方法
* 傳入File泛型
* public int compare(Object o1, Object o2) 返回一個基本類型的整型
* 如果要按照升序排序,則o1< o2,返回-1(負數(shù))帽馋,相等返回0搅方,01大于02返回1(正數(shù))
* 如果要按照降序排序,則o1< o2,返回1(正數(shù))绽族,相等返回0姨涡,01大于02返回-1(負數(shù))
*/
public static class FileComparator implements Comparator<File> {
/**
* 按修改時間進行排序,lastModified()返回此抽象路徑名表示的文件最后一次被修改的時間吧慢。
* @param file1
* @param file2
* @return
*/
@Override
public int compare(File file1, File file2) {
if(file1.lastModified () < file2.lastModified ()) {
return 1;
} else {
return -1;
}
}
}
}
4. RecyclerView適配器
public class CreateAdapter extends RecyclerView.Adapter<CreateAdapter.CreateHolder> {
/**
* 使用適配器的上下文
*/
private Context context;
/**
* 傳入適配器的顯示列表
*/
private List<String> list;
/**
* 實例化一個接口
*/
private OnItemClickListener onItemClickListener;
public CreateAdapter(Context context, List<String> list) {
this.context = context;
this.list = list;
}
class CreateHolder extends RecyclerView.ViewHolder {
RelativeLayout rlItemLayout;
ImageView ivItemImage;
public CreateHolder(View itemView) {
super (itemView);
rlItemLayout = itemView.findViewById (R.id.create_list_item_layout_rl);
ivItemImage = itemView.findViewById (R.id.create_list_item_iv);
}
}
@NonNull
@Override
public CreateHolder onCreateViewHolder(@NonNull ViewGroup parent, final int viewType) {
View view = LayoutInflater.from (parent.getContext ()).inflate (R.layout.create_list_item_layout, parent, false);
final CreateHolder holder = new CreateHolder (view);
view.setOnClickListener (new View.OnClickListener () {
@Override
public void onClick(View view) {
onItemClickListener.onClick ((int) view.getTag ());
}
});
return holder;
}
@Override
public void onBindViewHolder(@NonNull CreateHolder holder, int position) {
Glide.with (context).load (list.get (position)).into (holder.ivItemImage);
holder.itemView.setTag (position);
}
@Override
public int getItemCount() {
return list.size ();
}
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView (recyclerView);
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager ();
if(manager instanceof GridLayoutManager) {
GridLayoutManager gridLayoutManager = (GridLayoutManager) manager;
gridLayoutManager.setSpanSizeLookup (new GridLayoutManager.SpanSizeLookup () {
@Override
public int getSpanSize(int position) {
return 1;
}
});
}
}
/**
* 定義一個item的點擊事件接口
*/
public interface OnItemClickListener {
/**
* 為item添加點擊事件涛漂,傳入一個點擊的item位置
*
* @param position
*/
void onClick(int position);
}
/**
* 為Activity提供一個監(jiān)聽點擊事件的方法,實現(xiàn)自定義的接口
*
* @param onItemClickListener
*/
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
}