Android 应用程序开发中总会遇到从本地选择照片的操作,本文描述得是一个简单得图片选择器
支持设置图片张数、可以设置屏蔽得图片格式、图片预览。更多的功能正在开发中,项目已放到:码云git上 我也在随时更新这个项目。下面就简单地介绍下这个内容。

效果图片




工程逻辑简述

实现原理则是,使用Android提供的媒体数据库,将图片资源的地址(存在本地的绝对地址(路径))提取出来,再使用Glide图片加载框架,将图片加载到RecyclerView列表中。
对于Glide框架和RecyclerView可以添加如下依赖(若遇到依赖版本问题就自行解决咯)

//Glidecompile 'com.github.bumptech.glide:glide:3.7.0'//RecyclerViewcompile 'com.android.support:recyclerview-v7:26.0.0-alpha1'

以上便是这个工程的基本思路

获取图片地址(路径)

package com.util.photopicker.util;import android.content.Context;import android.database.Cursor;import android.provider.MediaStore;import com.util.photopicker.bean.ImageBean;import java.util.ArrayList;import java.util.List;/** * Created by eric on 2017/12/3. */public class ImageFinder {    public static final String NONE = "none";    public static final String TYPE_GIF = "image/gif";    public static final String TYPE_JPEG = "image/jpeg";    public static final String TYPE_jpg = "image/jpg";    public static final String TYPE_PNG = "image/png";    //第二个参数是配置需要屏蔽的图片格式    public static List getImages(Context context, String typeShield){        String shield = typeShield;        List list = new ArrayList<>();        Cursor cursor = context.getContentResolver().query(                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null);        while (cursor.moveToNext()){            String name = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));            String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));            String type = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE));            if (type.equals(shield)) continue;            list.add(0,new ImageBean(path,name));        }        return list;    }}

若是以上这个类看的头大,那我们只用知道一下这几行核心代码就OK了

 Cursor cursor = context.getContentResolver().query(                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null);        while (cursor.moveToNext()){            String name = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));            String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));            String type = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE));        }

以上便是通过Android为我们提供的媒体数据库来查询图片的name,path,type。当然还有更多的图片属性可以获取,例如文件大小等信息,但我们这儿暂时只需要这些就够了。除此之外,还得有个Bean类来存放我们得到的单个数据,然后放在一个List里,再返回这个list;Bean类如下

package com.util.photopicker.bean;/** * Created by eric on 2017/12/3. */public class ImageBean {    private String imagePath;    private String imageName;    private boolean isChoose = false;    public ImageBean(String path, String name){        this.imagePath = path;        this.imageName = name;    }    public String getImagePath() {        return imagePath;    }    public void setImagePath(String imagePath) {        this.imagePath = imagePath;    }    public String getImageName() {        return imageName;    }    public void setImageName(String imageName) {        this.imageName = imageName;    }    public boolean isChoose() {        return isChoose;    }    public void setChoose(boolean choose) {        isChoose = choose;    }}

当我们拿到了这个list之后就可以用列表的形式展示出来了,这里我采用的是RecyclerView,当然可以用其他的控件来代替,这里就自行考虑了。

RecyclerView
我都知道,RecyclerView简单易用,但是这个还是没法满足我们的需求,以下是对RecyclerView 的 adapter的一点小小的扩展。

package com.util.photopicker.component;import android.support.v7.widget.RecyclerView;import android.view.View;/** * Created by eric on 2017/12/4. */public abstract class BaseViewHolder extends RecyclerView.ViewHolder {    public BaseViewHolder(View itemView) {        super(itemView);        findViewById(itemView);    }    /**     * 转发传入的OnItemChooseCallback与位置     * @param chooseCallback 点击回调     * @param position 位置     */    public void setChooseCallback(OnItemChooseCallback chooseCallback,int position) {        if (chooseCallback != null){            intOnItemChooseCallback(chooseCallback,position);        }    }    /**     * 传入Item的点击事件的监听器     * @param listener     */    public void setOnItemClickListener(OnRecyclerViewItemClickListener listener, int position){        if (listener != null){            initOnItemClickListener(listener, position);        }    }    /**     * 传入Item的长按事件的监听器     * @param longClickListener     */    public void setOnItemLongClickListener(OnRecyclerViewItemLongClickListener longClickListener, int position){        if (longClickListener != null){            iniOnItemLongClickListener(longClickListener,position);        }    }    /**     * 初始化点击事件(开发者自行实现)     * @param chooseCallback 单项选择回调     * @param position 当前点击的位置     */    abstract public void intOnItemChooseCallback(OnItemChooseCallback chooseCallback, int position);    /**     * 初始化Item的点击事件(开发者自行实现)     * @param listener 监听器     */    abstract public void initOnItemClickListener(OnRecyclerViewItemClickListener listener, int position);    /**     * 初始化Item的长按事件(开发者自行实现)     * @param longClickListener 监听器     */    abstract public void iniOnItemLongClickListener(OnRecyclerViewItemLongClickListener longClickListener, int position);    /**     * 通过id匹配控件(开发者自行实现)     * @param itemView 父布局     */    abstract protected void findViewById(View itemView);    /**     * 用于装载数据(开发者自行实现)     * @param position 当前位置     */    abstract public void onBind(int position);}

对于一个列表来说,我们必定会用到adapter,RecyclerView也不列外,会用到 adapter 和 ViewHolder。
看上面这个类的名字变知道BaseViewHolder ,是自己写的一个ViewHolder的基类,然后我们的ViewHolder继承这个类便可以得到它的属性和功能。当然只有这个类是不够的,我们都知道 RecyclerView 显示列表,是adapter 和 ViewHolder的结合。可能你已经猜到了,还有一个BaseRecyclerAdapter的基类。

package com.util.photopicker.component;import android.support.v7.widget.RecyclerView;/** * Created by eric on 2017/12/4. */public abstract class BaseRecyclerAdapter extends RecyclerView.Adapter {    private OnItemChooseCallback chooseCallback;    private OnRecyclerViewItemClickListener onRecyclerViewItemClickListener;    private OnRecyclerViewItemLongClickListener onRecyclerViewItemLongClickListener;    @Override    public void onBindViewHolder(BaseViewHolder holder, int position) {        holder.setChooseCallback(chooseCallback,position);        holder.setOnItemClickListener(onRecyclerViewItemClickListener,position);        holder.setOnItemLongClickListener(onRecyclerViewItemLongClickListener,position);        holder.onBind(position);    }    public void setChooseCallback(OnItemChooseCallback chooseCallback) {        this.chooseCallback = chooseCallback;    }    public void setOnRecyclerViewItemClickListener(OnRecyclerViewItemClickListener onRecyclerViewItemClickListener) {        this.onRecyclerViewItemClickListener = onRecyclerViewItemClickListener;    }    public void setOnRecyclerViewItemLongClickListener(OnRecyclerViewItemLongClickListener onRecyclerViewItemLongClickListener) {        this.onRecyclerViewItemLongClickListener = onRecyclerViewItemLongClickListener;    }}

以上便是BaseRecyclerAdapter,其中很有灵性的是这几个方法

public void setChooseCallback(OnItemChooseCallback chooseCallback) {        this.chooseCallback = chooseCallback;    }    public void setOnRecyclerViewItemClickListener(OnRecyclerViewItemClickListener onRecyclerViewItemClickListener) {        this.onRecyclerViewItemClickListener = onRecyclerViewItemClickListener;    }    public void setOnRecyclerViewItemLongClickListener(OnRecyclerViewItemLongClickListener onRecyclerViewItemLongClickListener) {        this.onRecyclerViewItemLongClickListener = onRecyclerViewItemLongClickListener;    }

可以看出,这个便是设置事件的监听,OnItemChooseCallback,OnRecyclerViewItemClickListener ,OnRecyclerViewItemLongClickListener 相信看名字就知道是什么意思了

1、 OnItemChooseCallback

package com.util.photopicker.component;/** * Created by eric  on 2017/12/4. */public interface OnItemChooseCallback {    /**     * 点击单项时     * @param position 位置     * @param isChosen 是否选中     */    void chooseState(int position, boolean isChosen);    /**     * 现在的值     * @param countNow     */    void countNow(int countNow);    /**     * 警告不能再选了     * @param count     */    void countWarning(int count);}

2、 OnRecyclerViewItemClickListener

package com.util.photopicker.component;/** * Created by eric  on 2017/12/5. */public interface OnRecyclerViewItemClickListener {    void onItemClick(int position);}

3、OnRecyclerViewItemLongClickListener

package com.util.photopicker.component;/** * Created by 肖庆鸿 on 2017/12/5. */public interface OnRecyclerViewItemLongClickListener {    void onItemLongClick(int position);}

以上这样写的好处再哪儿呢,下面就可以看出来

ImagesListAdapter

package com.util.photopicker.adapter;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import com.bumptech.glide.Glide;import com.util.photopicker.R;import com.util.photopicker.bean.ImageBean;import com.util.photopicker.component.BaseRecyclerAdapter;import com.util.photopicker.component.BaseViewHolder;import com.util.photopicker.component.OnItemChooseCallback;import com.util.photopicker.component.OnRecyclerViewItemClickListener;import com.util.photopicker.component.OnRecyclerViewItemLongClickListener;import java.util.List;/** * Created by eric on 2017/12/3. */public class ImagesListAdapter extends BaseRecyclerAdapter {    private int count = 0;    private int maxNum = 1;    private Context context;    private List list;    public ImagesListAdapter(Context context, List list){        this.context = context;        this.list = list;    }    @Override    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_picker,null);        return new MyViewHolder(view);    }    @Override    public int getItemCount() {        return list.size();    }    public void setMaxNum(int maxNum) {        if (maxNum < 1) return;        this.maxNum = maxNum;    }    private class MyViewHolder extends BaseViewHolder {        private ImageView mImageSrc;        private ImageView mImageChoose;        private MyViewHolder(View itemView) {            super(itemView);        }        @Override        public void intOnItemChooseCallback(final OnItemChooseCallback chooseCallback, final int position) {           mImageChoose.setOnClickListener(new View.OnClickListener() {               @Override               public void onClick(View view) {                   if (count < maxNum){                       if (!list.get(position).isChoose()){                           mImageChoose.setImageResource(R.drawable.checked_yes);                           list.get(position).setChoose(true);                           chooseCallback.chooseState(position,true);                           count ++;                       } else {                           mImageChoose.setImageResource(R.drawable.checked_null);                           list.get(position).setChoose(false);                           chooseCallback.chooseState(position,false);                           count--;                       }                   } else { //count >= maxNum                       if (!list.get(position).isChoose()){                           chooseCallback.countWarning(count);                       } else {                           mImageChoose.setImageResource(R.drawable.checked_null);                           list.get(position).setChoose(false);                           chooseCallback.chooseState(position,false);                           count--;                       }                   }                   chooseCallback.countNow(count);               }           });        }        @Override        public void initOnItemClickListener(final OnRecyclerViewItemClickListener listener, final int position) {            itemView.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View v) {                    listener.onItemClick(position);                }            });        }        @Override        public void iniOnItemLongClickListener(OnRecyclerViewItemLongClickListener longClickListener, int position) {        }        @Override        protected void findViewById(View itemView) {            mImageSrc = itemView.findViewById(R.id.image_src);            mImageChoose = itemView.findViewById(R.id.image_choose);        }        @Override        public void onBind(int position) {            if (list.get(position).isChoose()){                mImageChoose.setImageResource(R.drawable.checked_yes);            } else {                mImageChoose.setImageResource(R.drawable.checked_null);            }            Glide.with(context)                    .load(list.get(position).getImagePath())                    .into(mImageSrc);        }    }}

adapter里面我们只需要重写两个方法就OK了

 @Override    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_picker,null);        return new MyViewHolder(view);    }    @Override    public int getItemCount() {        return list.size();    }

这个相信已经不用解释了,然后再viewholder中处理逻辑就很清晰了

private MyViewHolder(View itemView) {            //这里super不可省略            super(itemView);        }        @Override        public void intOnItemChooseCallback(final OnItemChooseCallback chooseCallback, final int position) {              //初始化选择事件的callback        }        @Override        public void initOnItemClickListener(final OnRecyclerViewItemClickListener listener, final int position) {            //初始化单击事件        }        @Override        public void iniOnItemLongClickListener(OnRecyclerViewItemLongClickListener longClickListener, int position) {            //初始化长按事件        }        @Override        protected void findViewById(View itemView) {            //通过id来初始化itemView内的控件        }        @Override        public void onBind(int position) {            //装配数据        }    }

这个类里是重写继承自抽象类BaseViewHolder的一些方法,看名字就知道这个方法里该写什么。这里放心,若是没有调用adapter对象的set方法这些初始化方法是不会调用的。以上便是图片选择器中对RecyclerView 的 adapter的扩展。

ImagesPickActivity
再下来就是展示图片的ImagesPickActivity了,这里的一系列操作都是些 固定的方式,没什么可说的

package com.util.photopicker.activities;import android.app.Activity;import android.content.Intent;import android.content.pm.PackageManager;import android.net.Uri;import android.os.Build;import android.provider.MediaStore;import android.support.annotation.NonNull;import android.support.v4.app.ActivityCompat;import android.support.v4.content.FileProvider;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.support.v7.widget.GridLayoutManager;import android.support.v7.widget.RecyclerView;import android.support.v7.widget.Toolbar;import android.view.View;import android.widget.Button;import android.widget.ImageButton;import android.widget.TextView;import android.widget.Toast;import com.util.photopicker.R;import com.util.photopicker.bean.ImageBean;import com.util.photopicker.adapter.ImagesListAdapter;import com.util.photopicker.component.OnItemChooseCallback;import com.util.photopicker.component.OnRecyclerViewItemClickListener;import com.util.photopicker.util.ImageFinder;import java.io.File;import java.io.IOException;import java.util.ArrayList;import java.util.List;public class ImagesPickActivity extends AppCompatActivity implements View.OnClickListener{    public final static String RESULT_IMAGES_LIST = "imagesPath";    private final static int REQUEST_CAMERA = 200;    private final static String EXTRA_COUNT = "max";    private static final int REQUEST_EXTERNAL_STORAGE = 1;    private static String[] PERMISSIONS_STORAGE = {            "android.permission.READ_EXTERNAL_STORAGE",            "android.permission.WRITE_EXTERNAL_STORAGE" };    private Toolbar mTbPickActivity;    private TextView mTvCount;    private ImageButton mImageBtnPickerBack;    private Button mBtnSure;    private RecyclerView mRecyclerViewPickActivity;    private List list;    private Uri cameraImageUri;    private int maxCount;    private ArrayList chosenList = new ArrayList<>();    /**     * 提供启动活动的方法     * @param activity 起点活动     * @param max 最大图片数     * @param requestCode 请求值     */    public static void startPicker(Activity activity, int max, int requestCode){        Intent intent = new Intent(activity,ImagesPickActivity.class);        intent.putExtra(EXTRA_COUNT,max);        activity.startActivityForResult(intent,requestCode);    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_pick);        Intent intent = getIntent();        maxCount = intent.getIntExtra(EXTRA_COUNT,9);        initView();        requestStoragePermission();    }    /**     * 初始化控件资源     */    private void initView() {        mTbPickActivity = (Toolbar) findViewById(R.id.tb_pick_activity);        mTvCount = (TextView) findViewById(R.id.tv_count);        mImageBtnPickerBack = (ImageButton) findViewById(R.id.imageBtn_picker_back);        mBtnSure = (Button) findViewById(R.id.btn_sure);        mRecyclerViewPickActivity = (RecyclerView) findViewById(R.id.recyclerView_pick_activity);        mTvCount.setText(0+"/"+maxCount);        mImageBtnPickerBack.setOnClickListener(this);        mBtnSure.setOnClickListener(this);    }    /**     * 初始化图片列表     */    private void initRecyclerView(){        list = ImageFinder.getImages(this,ImageFinder.TYPE_GIF);        MyChooseCallback callback = new MyChooseCallback();        MyOnItemClickListener listener = new MyOnItemClickListener();        ImagesListAdapter adapter = new ImagesListAdapter(this,list);        RecyclerView.LayoutManager layoutManager = new GridLayoutManager(this,3);        adapter.setMaxNum(maxCount);        adapter.setChooseCallback(callback);        adapter.setOnRecyclerViewItemClickListener(listener);        mRecyclerViewPickActivity.setLayoutManager(layoutManager);        mRecyclerViewPickActivity.setAdapter(adapter);    }    /**     * 照相机     */    private void takePhoto(){        //文件io流存储图片        File outputImage = new File(getExternalCacheDir(),"outputImage.jpg");        try {            if (outputImage.exists()){                outputImage.delete();            }            outputImage.createNewFile(); //创建新的对象        } catch (IOException e) {            e.printStackTrace();        }        if (Build.VERSION.SDK_INT >= 24){            cameraImageUri = FileProvider.getUriForFile(this,"com.eric.photopicker.camera",outputImage);        } else {            cameraImageUri = Uri.fromFile(outputImage);        }        //启动相机        Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");        intent.putExtra(MediaStore.EXTRA_OUTPUT,cameraImageUri);        startActivityForResult(intent,REQUEST_CAMERA);    }    /**     * 请求读写权限     */    private void requestStoragePermission(){        int permission = ActivityCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE");        if (permission != PackageManager.PERMISSION_GRANTED) {            ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);        } else {            initRecyclerView();        }    }    /**     * 启动活动返回值处     * @param requestCode 请求码     * @param resultCode 结果码     * @param data 数据     */    @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        switch (requestCode){            case REQUEST_CAMERA:                if (resultCode == RESULT_OK){                    Toast.makeText(this,"图片获取成功",Toast.LENGTH_SHORT).show();                }                break;            default:                break;        }    }    /**     * 权限请求结果处理     * @param requestCode 请求码     * @param permissions 权限数组     * @param grantResults 结果     */    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {        switch (requestCode){            case REQUEST_EXTERNAL_STORAGE:                if (grantResults.length > 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED){                    initRecyclerView();                } else {                    Toast.makeText(this,"您未开启读取储存权限", Toast.LENGTH_SHORT).show();                }                break;            default:                break;        }    }    /**     * 系统view点击回调     * @param v view     */    @Override    public void onClick(View v) {        switch (v.getId()){            case R.id.imageBtn_picker_back:                finish();                break;            case R.id.btn_sure:                onPickedDone();                break;            default:                break;        }    }    /**     * 返回一组图片path     */    private void onPickedDone(){        ArrayList images = new ArrayList<>();        for (int i : chosenList){            images.add(list.get(i).getImagePath());        }        onResult(images);    }    /**     * 返回结果     * @param images     */    private void onResult(ArrayList images){        Intent intent = new Intent();        intent.putStringArrayListExtra(RESULT_IMAGES_LIST,images);        setResult(RESULT_OK,intent);        finish();    }    /**     * Item点击事件的监听类     */    private class MyOnItemClickListener implements OnRecyclerViewItemClickListener{        @Override        public void onItemClick(int position) {            ImagesPreviewActivity.startPreView(ImagesPickActivity.this,list.get(position).getImagePath());        }    }    /**     * Item选则事件的监听类     */    private class MyChooseCallback implements OnItemChooseCallback {        @Override        public void chooseState(int position, boolean isChosen) {            if (isChosen){                chosenList.add(position);            } else {                int index = 0;                for (int i : chosenList){                    if (i == position){                        chosenList.remove(index);                    }                    index ++;                }            }        }        @Override        public void countNow(int countNow) {            mTvCount.setText(countNow +"/"+ maxCount);        }        @Override        public void countWarning(int count) {            Toast.makeText(ImagesPickActivity.this,"最多选择"+count+"张图片",Toast.LENGTH_SHORT).show();        }    }}

当我们单击图片时,想要查看图片

ImagesPreviewActivity

package com.util.photopicker.activities;import android.app.Activity;import android.content.Intent;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.ImageButton;import android.widget.ImageView;import com.bumptech.glide.Glide;import com.util.photopicker.R;public class ImagesPreviewActivity extends AppCompatActivity {    private final static String EXTRA_PREVIEW = "imagePath";    private ImageView mImagePreviewShow;    private String imagePath;    private ImageButton mImgBtnPreviewBack;    public static void startPreView(Activity activity, String imagePath){        Intent intent = new Intent(activity,ImagesPreviewActivity.class);        intent.putExtra(EXTRA_PREVIEW,imagePath);        activity.startActivity(intent);    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_images_preview);        Intent intent = getIntent();        imagePath = intent.getStringExtra(EXTRA_PREVIEW);        mImagePreviewShow = (ImageView) findViewById(R.id.image_preview_show);        mImgBtnPreviewBack = (ImageButton) findViewById(R.id.imgBtn_preview_back);        mImgBtnPreviewBack.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                finish();            }        });        Glide.with(this)                .load(imagePath)                .into(mImagePreviewShow);    }}

以上便是一个简单的图片选择器工程,工程源码地址点击:码云(git)

更多相关文章

  1. Android中保存图片的两种方式
  2. 【整理】Android中使用XML自定义组件各种状态下的背景图片
  3. Android对View进行截图并保存到本地相册
  4. Android事件分发机制以及滑动冲突处理
  5. Android(安卓)studio 百度地图开发(6)Marker绑定事件、计算两点距
  6. Android实现再图标右上角显示数字
  7. android之图片截取
  8. Android显示从网络下载图片偏小的问题
  9. Android(安卓)gesture 原理

随机推荐

  1. ActivitiyThread的建立
  2. Android JSBridge的原理与实现
  3. android 初探
  4. Android核心分析之GUI框架的原理
  5. 基于feng streaming server 搭建Android
  6. Android源码-Android系统启动源码分析
  7. 【Android】学习笔记(9)――SQLite简单使
  8. js与android之间的简单传值
  9. android 使用SharedPreferences保存对象
  10. android 开机动画和关机动画修改与制作