RecyclerView的功能扩展(Android图片选择器)

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

效果图片
RecyclerView的功能扩展(Android图片选择器)_第1张图片


RecyclerView的功能扩展(Android图片选择器)_第2张图片
工程逻辑简述

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

//Glide
compile 'com.github.bumptech.glide:glide:3.7.0'
//RecyclerView
compile '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)

你可能感兴趣的:(Android)