趁热打铁,使用内容解析者(ContentResolver)来实现一个图片选择器,大体的功能样式仿照的数字尾巴,( ̄▽ ̄)”只是粗仿……

下面是运行效果(GIF图片超过2MB就不能上传了/(ㄒoㄒ)/~~):

Android 图片选择 三方_内容解析者

Android 图片选择 三方_内容解析者_02

MediaStore这个类是android系统提供的一个多媒体数据库,android中多媒体信息都可以从这里提取。这个MediaStore包括了多媒体数据库的所有信息,包括音频,视频和图像。操作就和使用内容解析这获取其它数据的过程一样,android把所有的多媒体数据库接口进行了封装,所有的数据库不用自己进行创建,直接调用利用ContentResolver去掉用那些封装好的接口就可以进行数据库的操作了。

可以通过查看系统上层源码来查看数据封装的方式,以及在data目录下查看数据库,查看各种格式的文件在数据库中的存储方式,方便对后面的操作的理解。

Android 图片选择 三方_图片选择器_03

下面叙述一下实现的过程:

目录结构:

Android 图片选择 三方_android_04

ImageView的加强,重新自定义了一个可以选择的ImageView

Android 图片选择 三方_内容解析者_05

MyCustomImageView.java

package com.example.imageselector;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ImageView;

/**
 * Created by 春水碧于天 on 2017/1/18.
 */

public class MyCustomImageView extends ImageView {

    private int mViewWidth; //当前ImageView的宽
    private int mViewHeight; //当前ImageView的高
    private Paint mPaint_stroke;
    private Paint mPaint_fill;
    private Paint mPaint_text;
    private String mDrawText; //要绘制的文本
    private Boolean isChoice = false;
    private onCheckedChangeListener mCheckedChangeListener;

    private boolean isCanTouch = true;



    public MyCustomImageView(Context context) {
        super(context);
        InitPaint();
    }


    public MyCustomImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        InitPaint();
    }

    public MyCustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        InitPaint();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        mViewWidth = w;
        mViewHeight = h;



    }

    public void setDrawText(String mDrawText){

        this.mDrawText = mDrawText;
        invalidate(); //重新绘制

    }

    public void setChecked(boolean isChecked){

        isChoice = isChecked;
        invalidate();

    }

    public void setIsCanTouch(boolean isCanTouch){


        this.isCanTouch = isCanTouch;

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        /**
         * 如果没有选中的话,按下选中
         * 如果已经选中的话,按下清除内容
         */

        switch (event.getAction()){

            case MotionEvent.ACTION_DOWN:

                if(!isChoice){ //如果没有选中

                    isChoice = true;
                    invalidate();

                }else if(isChoice){ //如果已经选中了

                    isChoice = false;
                    invalidate();

                }


                //回调
                mCheckedChangeListener.isChecked(isChoice);


                Log.i("tag","手指按下了");
                break;


            case MotionEvent.ACTION_UP:


                Log.i("tag","手指抬起了");
                break;



        }

        if(isCanTouch){
            return true;

        }else{
            return false;
        }



    }

    //初始化画笔
    public void InitPaint(){

        mPaint_stroke = new Paint(Paint.ANTI_ALIAS_FLAG); //设置抗锯齿
        mPaint_stroke.setStyle(Paint.Style.STROKE);//设置填充模式为描边
        mPaint_stroke.setStrokeWidth(8f);//设置画笔的宽度
        mPaint_stroke.setColor(Color.BLUE); //设置画笔的颜色

        mPaint_fill = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint_fill.setStyle(Paint.Style.FILL);//设置填充模式为描边
        mPaint_fill.setColor(Color.BLUE); //设置画笔的颜色

        mPaint_text = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint_text.setTextSize(40f);
        mPaint_text.setColor(Color.rgb(255,255,255));
        mPaint_text.setTextAlign(Paint.Align.CENTER);



    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if(isChoice){

            //绘制矩形边框
            RectF rectF = new RectF(0f,0f,mViewWidth-10,mViewHeight);
            canvas.drawRect(rectF, mPaint_stroke);


            float mCircleX =mViewWidth*(2/3.0f)+(mViewWidth/6.0f);
            float mCircleY =mViewHeight*(1/6.0f);
            float mRadius = (mViewHeight/6.0f)*0.7f;

            //绘制圆
            canvas.drawCircle(mCircleX,mCircleY,mRadius, mPaint_fill);

            //绘制文字
            if(mDrawText!=null) {
                canvas.drawText(mDrawText, mCircleX, mCircleY + 13f, mPaint_text);
            }

        }



    }

    /**
     *设置选中后的回调监听
     */
    public void SetOnCheckedChangeListener(onCheckedChangeListener mCheckedChangeListener){
        this.mCheckedChangeListener = mCheckedChangeListener;
    }
    public interface onCheckedChangeListener{
        void isChecked(boolean isSelected);
    }
}

图片选择器的核心就是对图片的扫描,这面就使用内容解析者,获取安卓系统通过内容提供者暴露进出的数据库的增删改查的接口,进行操数的操作,后面只需要根据自己的需要筛选出需要的文件夹,以及对应格式的图片,下面的查询是对所有图片的查询没,有在查询的时候的筛根据条件进行晒选。

MainActivity.java

package com.example.imageselector;

public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {

    private  ArrayList<FileBean> FileBeans = null;
    private Handler mHandler = null;
    private ListView listView;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        InitUI();


        mHandler = new Handler(){

            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);

                if(msg.what == 1){

                    listView.setAdapter(new myAdapter(MainActivity.this,FileBeans));

                }
            }
        };

        /**
         * 在线程中处理扫描文件的耗时操作
         */
        new Thread(new Runnable() {

            Set<String> DirSet  = null;

            @Override
            public void run() {

                if (Environment.getExternalStorageDirectory() == null){

                    Log.i("tag","外部存储卡为空");
                    return;

                }
                //通过内容解析者获取手机中所有图片的路径
                Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                ContentResolver resolver = MainActivity.this.getContentResolver();
                //查询所有的图片信息,根据修改的时间排序
                Cursor cursor = resolver.query(uri, null, null, null, MediaStore.Images.Media.DATE_MODIFIED);

                DirSet = new HashSet<String>(); //用于记录已经扫描过的文件夹,避免重复扫描
                FileBeans = new ArrayList<FileBean>(); //存放图片文件夹信息
                FileBean fileBean = null;
                while(cursor.moveToNext()){

                    String filePath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));

                    if(filePath.endsWith(".gif") || filePath.endsWith(".GIF")) continue; //过滤掉包含GIF图片的文件夹

                    File Parentfile = new File(filePath).getParentFile(); //获取文件所在的文件夹

                    if(Parentfile==null) continue;

                    if(DirSet.contains(Parentfile.toString())) continue; //如果文件夹里的内容已经遍历过就执行下一次的循环

                    String[] list = Parentfile.list(new FilenameFilter() {
                        @Override
                        public boolean accept(File dir, String name) {

                            if (name.endsWith(".png") || name.endsWith(".jpeg") || name.endsWith(".jpg") ) {

                                return true;

                            }
                            return false;
                        }
                    });

                    DirSet.add(Parentfile.toString());

                    int fileNums = list.length;

                     fileBean = new FileBean();

                     fileBean.setFileList(list); //封装一个文件夹中包含的所有图片

                    fileBean.setFileNums(fileNums); //封装一个文件夹中的图片个数

                    fileBean.setFileParent(Parentfile);

                    fileBean.setFirstImage(filePath); //一个文件夹中对应的第一张图片

                    FileBeans.add(fileBean);

                }


                mHandler.sendEmptyMessage(1);


                cursor.close();
            }
        }).start();

    listView.setOnItemClickListener(this);

    }

    private void InitUI() {

     listView = (ListView) findViewById(R.id.listview);

    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {


        FileBean fileBean = (FileBean) parent.getItemAtPosition(position);

        Intent intent = new Intent(MainActivity.this,ShowImageActivity.class);

        intent.putExtra("fileBean", (Serializable) fileBean);

        startActivity(intent);

    }
}

用于显示文件夹的listview适配器:myAdapter.java

/**
 * Created by 春水碧于天 on 2017/1/17.
 */

public class myAdapter extends BaseAdapter {
    private ArrayList<FileBean> BeanList;
    private Context mContext;

    public myAdapter(Context mContext,ArrayList<FileBean> BeanList) {

        this.BeanList = BeanList;
        this.mContext = mContext;
    }


    @Override
    public int getCount() {
        return BeanList.size();
    }

    @Override
    public Object getItem(int position) {
        return BeanList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHoder viewHolder = null;

        if(convertView == null){

            viewHolder = new ViewHoder();
            convertView= View.inflate(mContext,R.layout.listview_item,null);

            viewHolder.imageView = (ImageView) convertView.findViewById(R.id.imageView);
            viewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);
            viewHolder.tv_nums = (TextView) convertView.findViewById(R.id.tv_num);

            convertView.setTag(viewHolder);

        }else{

            viewHolder = (ViewHoder) convertView.getTag();


        }

        String []path = BeanList.get(position).getFileParent().toString().split("/");
        int index = path.length-1;
        viewHolder.tv_name.setText(path[index]);
        viewHolder.tv_nums.setText(BeanList.get(position).getFileNums()+"");
        Glide.with(mContext).load(BeanList.get(position).getFirstImage()).into(viewHolder.imageView);


        return convertView;
    }


    static class ViewHoder {

        public TextView tv_name;
        public TextView tv_nums;
        public ImageView imageView;


    }
}
Glide.with(mContext).load(BeanList.get(position).getFirstImage()).into(viewHolder.imageView);

这里的图片加载使用的是谷歌推荐的Glide,惊喜的发现GIF图也是可以加载的,总的来说使用起来还是蛮优雅的,一行代码搞定,Glide和Android的Picasso使用起来api惊人的相似……

ShowImageView.java

public class ShowImageActivity extends AppCompatActivity {

    private GridView gridView;
    private TextView tv_cancle;
    private TextView tv_complete;
    private HashMap<Integer, Integer> mChoicePosition = new HashMap<Integer, Integer>(); //记录选择的图片对应的下标数(GridView复用的问题)
    private HashMap<Integer, ImageView> mChoiceImage = new HashMap<Integer, ImageView>(); //记录选择的图片
    private FileBean fileBean;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_show_image);
        InitUI();

        fileBean = (FileBean) getIntent().getSerializableExtra("fileBean");


        gridView.setAdapter(new MyGridAdapter(fileBean, this));


        //点击完成执行的相应操作
        tv_complete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String AllChoiceImagePath[] =  getChoiceImage(); //获取选择的图片存放在数组中


            }
        });

    }

    private String[] getChoiceImage() {

        Iterator<Map.Entry<Integer, Integer>> iterator = mChoicePosition.entrySet().iterator();
        int index[] = new int[mChoicePosition.size()];
        int i=0;
        while(iterator.hasNext()){

            Map.Entry entry = (Map.Entry)iterator.next();
            index[i++] = (int) entry.getKey(); //根据key(position)获取图片
        }

        Arrays.sort(index); //排序

        String ImagePath[] = new String[mChoicePosition.size()]; //存放选择的图片的路径

        for (int num:index) {

            ImagePath[num] = fileBean.getFileParent().toString()+fileBean.getFileList()[num]; //根据选择的下标获取fileBean对象中存储的图片的路径,存放在数组中
        }


        for(String imagePath:ImagePath){

            Log.i("tag","ImagePath:"+imagePath);

        }

        return ImagePath;


    }

    private void InitUI() {

        gridView = (GridView) findViewById(R.id.gridView);
        tv_cancle = (TextView) findViewById(R.id.tv_cancle);
        tv_complete = (TextView) findViewById(R.id.tv_complete);

    }


    /**
     * GridView的适配器
     */
    public class MyGridAdapter extends BaseAdapter {

        private FileBean fileBean;
        private Context mContext;
        private int CurrentNum = 1;

        public MyGridAdapter(FileBean fileBean, Context mContext) {

            this.fileBean = fileBean;
            this.mContext = mContext;

        }

        @Override
        public int getCount() {

            return fileBean.getFileList().length;
        }

        @Override
        public Object getItem(int position) {
            return fileBean.getFileParent() + fileBean.getFileList()[position];
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder = null;

            if (convertView == null) {

                convertView = View.inflate(mContext, R.layout.grid_item, null);

                viewHolder = new ViewHolder();

                viewHolder.imageViewGrid = (MyCustomImageView) convertView.findViewById(R.id.imageViewGrid);

                convertView.setTag(viewHolder);

            } else {

                viewHolder = (ViewHolder) convertView.getTag();


            }

            final File file = new File(fileBean.getFileParent(), fileBean.getFileList()[position]);


            Glide.with(mContext).load(file).into(viewHolder.imageViewGrid);


            final ViewHolder ViewHolder = viewHolder;


            viewHolder.imageViewGrid.SetOnCheckedChangeListener(new MyCustomImageView.onCheckedChangeListener() {
                @Override
                public void isChecked(boolean isSelected) {

                    //点击图片进行选择的时候调用

                    if (CurrentNum < 10) {
                        if (isSelected) {
                            //将选中的图片记录到map集合中
                            mChoicePosition.put(position, CurrentNum);
                            mChoiceImage.put(position, ViewHolder.imageViewGrid);


                            ViewHolder.imageViewGrid.setDrawText(CurrentNum + "");
                            CurrentNum++;
                        } else {
                            Log.i("tag", "CurrentNum--:" + CurrentNum);
                            Log.i("tag", "Position==>" + position);


                            ChangeIndex(position);//根据选择动态改变图片对应的下标数


                        }

                    } else {

                        if (isSelected) { //如果是未选中状态就将其状态置为未选中状态

                            ViewHolder.imageViewGrid.setChecked(false);
                        } else {

                            ChangeIndex(position);



                        }
                        if (CurrentNum == 10) {

                            Toast.makeText(ShowImageActivity.this, "最多选择9张图片", Toast.LENGTH_SHORT).show();
                        }

                    }


                    if (CurrentNum == 0) CurrentNum = 1;


                    int num = mChoicePosition.size();
                    if(num==0){
                        tv_complete.setText("完成");
                    }
                    else{
                        tv_complete.setText("完成 "+num+"/9");
                    }
                }

                /**
                 * 根据选择动态改变图片对应的下标数
                 */
                private void ChangeIndex(int position) {

                    if (mChoicePosition.containsKey(position)) {

                        int num = mChoicePosition.get(position);

                        Iterator iter = mChoicePosition.entrySet().iterator();
                        while (iter.hasNext()) {
                            Map.Entry entry = (Map.Entry) iter.next();
                            int key = (int) entry.getKey();
                            int val = (int) entry.getValue();

                            Log.i("tag", "key: " + key);
                            Log.i("tag", "value: " + val);

                            //将比当前点击的下标大的数进行减一操作
                            if (val > num) {

                                val = val - 1;

                                mChoicePosition.put(key, val);

                                MyCustomImageView iv = (MyCustomImageView) mChoiceImage.get(key);

                                iv.setDrawText(val + "");
                            }

                        }

                        mChoicePosition.remove(position);
                        mChoiceImage.remove(position);
                        ViewHolder.imageViewGrid.setDrawText(CurrentNum + "");
                        CurrentNum--;


                    }

                }
            });


            if (mChoicePosition.get(position) != null) {

                viewHolder.imageViewGrid.setChecked(true);
                viewHolder.imageViewGrid.setDrawText(String.valueOf(mChoicePosition.get(position)));

            } else {


                viewHolder.imageViewGrid.setChecked(false);

            }

            return convertView;
        }


        class ViewHolder {

            public MyCustomImageView imageViewGrid;

        }

    }

}

由于GridView的复用,这里采用Map集合的方式将选中的图片的位置的下标记录到Map集合中,然后再根据map集合中的数据将选择的那个图片对应的Index设置给ImageView,防止ImageView复用造成的界面的错乱。

FileBean.java

/**
 * Created by 春水碧于天 on 2017/1/17.
 */

public class FileBean implements Serializable{

    private String firstImage;//第一张图片的文件名
    private File fileParent; //文件的父路径
    private int fileNums; //文件的数量
    private String[] fileList;
    private int ChoiceImage = -1; //设置选择的图片的下标数




    public String getFirstImage() {
        return firstImage;
    }

    public void setFirstImage(String firstImage) {
        this.firstImage = firstImage;
    }

    public File getFileParent() {
        return fileParent;
    }

    public void setFileParent(File fileParent) {
        this.fileParent = fileParent;
    }

    public String[] getFileList() {
        return fileList;
    }

    public void setFileList(String[] fileList) {
        this.fileList = fileList;
    }

    public int getFileNums() {
        return fileNums;
    }

    public void setFileNums(int fileNums) {
        this.fileNums = fileNums;
    }
}

布局就不朝上面贴了,反正挺简单的。
(被有些逻辑恶心到了……期待医生的群英传,更上一层楼!!)