圖片選擇器

一 、設(shè)計目標(biāo)

在進(jìn)行開發(fā)的時候有現(xiàn)成的圖片選擇器,通過內(nèi)容提供器可以直接打開系統(tǒng)相冊進(jìn)行選擇,得到要選擇的圖片,界面比較簡陋,但可以實現(xiàn)簡單的圖片選擇功能。要設(shè)計一個自己的圖片選擇器,要明確設(shè)計的目標(biāo):

  1. 訪問手機(jī)的媒體庫,獲得有圖片的文件夾
  2. 為文件夾列表設(shè)置一個下拉選擇框,下拉選擇要訪問的文件夾
  3. 選擇目標(biāo)文件夾,遍歷文件夾所有圖片,利用Glide進(jìn)行圖片加載最后在RecyclerView中進(jìn)行顯示
  4. 設(shè)置一個ImageView對要選擇的圖片進(jìn)行預(yù)覽,點擊預(yù)覽圖進(jìn)入圖片編輯頁面

以上大概就是我要實現(xiàn)的圖片選擇器的設(shè)計目標(biāo),對設(shè)計目標(biāo)進(jìn)行分析后確定使用的工具有

  1. MediaStore媒體庫
  2. Handler異步加載
  3. Glide圖片加載
  4. RecyclerView列表顯示
  5. Spinner下拉選擇框

二、 相關(guān)內(nèi)容學(xué)習(xí)

1. MediaStore

MediaStore這個類是android系統(tǒng)提供的一個多媒體數(shù)據(jù)庫,android 中多媒體信息都可以從這里提取。這個MediaStore包括了多媒體數(shù)據(jù)庫的所有信息,包括音頻,視頻和圖像,android把所有的多媒體數(shù)據(jù)庫接口 進(jìn)行了封裝,所有的數(shù)據(jù)庫不用自己進(jìn)行創(chuàng)建,直接調(diào)用利用ContentResolver去掉用那些封裝好的接口就可以進(jìn)行數(shù)據(jù)庫的操作了。今天我就介紹 一些這些接口的用法。在進(jìn)行圖片選擇的時候,就是通過MediaStore進(jìn)行查找圖片路徑等操作。

類結(jié)構(gòu)圖

MediaStore詳細(xì)字段參考鏈接:https://blog.csdn.net/lemon_blue/article/details/52353851

2. Handler異步加載

Handler主要用于異步消息的處理: 有點類似輔助類,封裝了消息投遞、消息處理等接口。當(dāng)發(fā)出一個消息之后,首先進(jìn)入一個消息隊列,發(fā)送消息的函數(shù)即刻返回,而另外一個部分在消息隊列中逐一將消息取出,然后對消息進(jìn)行處理,也就是發(fā)送消息和接收消息不是同步的處理。 這種機(jī)制通常用來處理相對耗時比較長的操作。

3. Glide圖片加載

一行代碼

Glide.with(context).load(url).into(imageView);

4. RecyclerView

參考之前的內(nèi)容

5. Spinner控件

參考代碼,spinner的適配器是ArrayAdapter<String>類型。

三、 代碼分析

1. 圖片工具類

定義一個工具類對媒體庫進(jìn)行查詢,返回所以包含圖片的文件夾列表,注釋比較詳細(xì)。

public class ImageUtils {
    /**
     * 圖片文件夾名列表
     */
    public static List<String> folderNameList = new ArrayList<> ();

    /**
     * 圖片文件夾路徑列表
     */
    public static List<String> folderPathList = new ArrayList<> ();

    /**
     * 文件夾列表,用于判斷當(dāng)前文件夾是否遍歷
     */
    public static HashSet<String> mFolderList = new HashSet<> ();

    /**
     * 得到圖片文件夾,利用ContentResolver進(jìn)行遍歷
     * @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));
                    //獲得當(dāng)前文件夾路徑
                    File imageFolder = new File (imagePath).getParentFile ();
                    if(imageFolder == null) {
                        continue;
                    }
                    String folderPath = imageFolder.getAbsolutePath ();
                    //利用HashSet進(jìn)行判斷當(dāng)前文件夾是否被選擇
                    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;

    /**
     * 顯示當(dāng)前文件夾的圖片列表
     */
    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<> ();

    /**
     * 當(dāng)前文件夾下所有圖片路徑
     */
    private static List<String> selectedImageList = new ArrayList<> ();

    /**
     * 當(dāng)前預(yù)覽圖片路徑
     */
    private static String selectedImagePath;

    public CreateFragment() {
    }

    /**
     * 獲取當(dāng)前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 ();
                //利用排序工具類進(jìn)行排序返回當(dāng)前文件夾圖片路徑列表
                selectedImageList = SortUtils.sortImage (imageList, i);
                setRecyclerView ();
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {

            }
        });
    }
    
    /**
     * 得到當(dāng)前文件夾的圖片路徑,為RecyclerView設(shè)置適配器,進(jìn)行顯示
     */
    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);
        //獲取當(dāng)前文件夾
        File imageFile = new File (imageFolder).getAbsoluteFile ();
        //獲取當(dāng)前文件夾下的文件
        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(負(fù)數(shù)),相等返回0,01大于02返回1(正數(shù))
     * 如果要按照降序排序,則o1< o2,返回1(正數(shù)),相等返回0,01大于02返回-1(負(fù)數(shù))
     */
    public static class FileComparator implements Comparator<File> {

        /**
         * 按修改時間進(jìn)行排序,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;
    }
}

四、 成果展示

image.png

image.png

image.png

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 相信很多朋友在開發(fā)安卓App時都會有這樣需求,圖片選擇或拍照選擇,需求實現(xiàn)很簡單,如下: 圖片選擇:調(diào)用系統(tǒng)圖庫進(jìn)...
    QiuJay閱讀 4,053評論 0 7
  • 內(nèi)容 抽屜菜單 ListView WebView SwitchButton 按鈕 點贊按鈕 進(jìn)度條 TabLayo...
    小狼W閱讀 1,670評論 0 10
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,040評論 25 709
  • 最近因為遇到了一些事情,有很多感觸,所以就想寫一寫,記錄下來。 今年在我快要過生日的時候父親不幸患肺腺癌,當(dāng)時我是...
    散是滿天星閱讀 501評論 3 2
  • 當(dāng)你長期處于一個圈子,即使你想反抗,你的反抗也被局限在這個圈子里。 當(dāng)你的生活被阻塞太多,你本身會失去思考力,失去自我。
    中毒成癮閱讀 227評論 0 0

友情鏈接更多精彩內(nèi)容