Aide学

图片选择器

2018-08-13  本文已影响22人  不会敲代码的好代码

一 、设计目标

在进行开发的时候有现成的图片选择器,通过内容提供器可以直接打开系统相册进行选择,得到要选择的图片,界面比较简陋,但可以实现简单的图片选择功能。要设计一个自己的图片选择器,要明确设计的目标:

  1. 访问手机的媒体库,获得有图片的文件夹
  2. 为文件夹列表设置一个下拉选择框,下拉选择要访问的文件夹
  3. 选择目标文件夹,遍历文件夹所有图片,利用Glide进行图片加载最后在RecyclerView中进行显示
  4. 设置一个ImageView对要选择的图片进行预览,点击预览图进入图片编辑页面

以上大概就是我要实现的图片选择器的设计目标,对设计目标进行分析后确定使用的工具有

  1. MediaStore媒体库
  2. Handler异步加载
  3. Glide图片加载
  4. RecyclerView列表显示
  5. Spinner下拉选择框

二、 相关内容学习

1. MediaStore

MediaStore这个类是android系统提供的一个多媒体数据库,android 中多媒体信息都可以从这里提取。这个MediaStore包括了多媒体数据库的所有信息,包括音频,视频和图像,android把所有的多媒体数据库接口 进行了封装,所有的数据库不用自己进行创建,直接调用利用ContentResolver去掉用那些封装好的接口就可以进行数据库的操作了。今天我就介绍 一些这些接口的用法。在进行图片选择的时候,就是通过MediaStore进行查找图片路径等操作。

类结构图
MediaStore详细字段参考链接:https://blog.csdn.net/lemon_blue/article/details/52353851

2. Handler异步加载

Handler主要用于异步消息的处理: 有点类似辅助类,封装了消息投递、消息处理等接口。当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。

3. Glide图片加载

一行代码

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

4. RecyclerView

参考之前的内容

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 {

    /**
     * 图片选择界面的预览图
     */
    private ImageView iv_selectedImage;

    /**
     * 显示当前文件夹的图片列表
     */
    private RecyclerView recyclerView;

    /**
     * 图片列表适配器
     */
    private CreateAdapter createAdapter;

    /**
     * 列表网格布局管理器
     */
    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<> ();

    /**
     * 当前预览图片路径
     */
    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);
        //预览图点击事件
        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 ();

    }

    /**
     * 设置下拉选择栏的选择事件
     */
    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设置适配器,进行显示
     */
    private void setRecyclerView() {
        //布局管理器一定要设置
        gridLayoutManager = new GridLayoutManager (getActivity (), 4, GridLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager (gridLayoutManager);
        createAdapter = new CreateAdapter (getActivity (), selectedImageList);
        recyclerView.setAdapter (createAdapter);
        /**
         * 为RecyclerView的item设置点击事件
         */
        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;
    }

    /**
     * 文件比较类,实现Comparator接口,重写compare方法
     * 传入File泛型
     * public int compare(Object o1, Object o2) 返回一个基本类型的整型
     * 如果要按照升序排序,则o1< o2,返回-1(负数),相等返回0,01大于02返回1(正数)
     * 如果要按照降序排序,则o1< o2,返回1(正数),相等返回0,01大于02返回-1(负数)
     */
    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提供一个监听点击事件的方法,实现自定义的接口
     *
     * @param onItemClickListener
     */
    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }
}

四、 成果展示

image.png
image.png
image.png
image.png
上一篇 下一篇

猜你喜欢

热点阅读