android 图片选择控件,单图裁剪,多图选择

最近需要做一个图片选择器,发现Coding里面有一个开源的图片选择控件,于是就厚颜无耻的拿出来简单修改一下自己用了,修改之后即可以实现单图片选择裁剪,也能多图片选择。
废话不多说,没图没真相,
这里写图片描述
这里写图片描述

裁剪调用的是系统的,原生系统的看起来就是简陋啊

调用起来也是相当的简单,写个intent就行了

        //单选裁剪是调用
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, PhotoPickActivity.class);
                intent.putExtra(PhotoPickActivity.EXTRA_MODE, PhotoPickActivity.MODE_SINGLE_CROP);
                startActivityForResult(intent, PhotoPickActivity.REQUEST_PHOTO_CROP);
            }
        });
        //多选时调用
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, PhotoPickActivity.class);
                intent.putExtra(PhotoPickActivity.EXTRA_MODE, PhotoPickActivity.MODE_MUTIL_CROP);
                intent.putExtra(PhotoPickActivity.EXTRA_MAX, 6);
                intent.putExtra(PhotoPickActivity.EXTRA_PICKED,new ArrayList<ImageInfo>());
                startActivityForResult(intent, PhotoPickActivity.REQUEST_PHOTO_LIST);
            }
        });

    //然后在onactivityresult里面获取回调的数据
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(requestCode == PhotoPickActivity.REQUEST_PHOTO_CROP && resultCode == Activity.RESULT_OK){//获取裁剪后的图片路径
            Bundle bundle = data.getExtras();
            String string = bundle.getString(PhotoPickActivity.EXTRA_RESULT_CROP_PHOTO);
            textView.setText(string);
            ImageLoader.getInstance().displayImage(ImageInfo.pathAddPreFix(string), image1, PhotoPickActivity.optionsImage);

        }
        if(requestCode == PhotoPickActivity.REQUEST_PHOTO_LIST && resultCode == Activity.RESULT_OK){
        //图片集合
            ArrayList<ImageInfo> imageInfos = (ArrayList<ImageInfo>) data.getSerializableExtra(PhotoPickActivity.EXTRA_RESULT_PHOTO_LIST);

        }
    }

实现

简单的使用方法说完了,然后说一下它的实现方法.
这里写图片描述

在这里我使用recycleView代替了listview和gridview,为什么,因为我任性。总是看到什么新的东西就想用用它。

首先说PhotoPickActivity,它就是获取手机的所有图片,然后将它分类,在展示出来,因此,先看看他是怎么获取图片的。

  • 首先先获取全部的图片来分析每个文件夹的数量。
private void initDirectory() {
        final String[] needInfos = {
                MediaStore.Images.ImageColumns._ID,
                MediaStore.Images.ImageColumns.DATA,
                MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
        };
        //存储每个类别及他里面图片的数量
        LinkedHashMap<String, Integer> mNames = new LinkedHashMap<>();
        //存储每个类别的第一张图片信息,在目录显示需要
        LinkedHashMap<String, ImageInfo> mData = new LinkedHashMap<>();
        Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, needInfos, "", null, MediaStore.MediaColumns.DATE_ADDED + " DESC");

        while (cursor.moveToNext()) {
            String name = cursor.getString(2);
            if (!mNames.containsKey(name)) {
                mNames.put(name, 1);//每个类别的数目
                ImageInfo imageInfo = new ImageInfo(cursor.getString(1));
                mData.put(name, imageInfo);//每个类别的第一张照片
            } else {
                int newCount = mNames.get(name) + 1;
                mNames.put(name, newCount);
            }
        }

        ArrayList<ImageInfoExtra> mFolderData = new ArrayList<>();//所有图片
        if (cursor.moveToFirst()) {//保存第一个项(所有图片的目录项)
            ImageInfo imageInfo = new ImageInfo(cursor.getString(1));
            int allImagesCount = cursor.getCount();
            mFolderData.add(new ImageInfoExtra(allPhotos, imageInfo, allImagesCount));
        }

        for (String item : mNames.keySet()) {//保存每个项目图片
            ImageInfo info = mData.get(item);
            Integer count = mNames.get(item);
            mFolderData.add(new ImageInfoExtra(item, info, count));
        }
        cursor.close();

        //显示目录列表信息
        mFolderAdapter = new FolderAdapter(mFolderData,this);
        mRecycleTitle.setAdapter(mFolderAdapter);
        mFolderAdapter.setOnitemClickListener(mOnFolderItemClick);

    }

将每个文件夹的名字及里面图片数量保存到一个list里面,因为有个全部图片选项,所以list的第一个item保存所有图片数量并且命名为“全部图片”。

  • 接下来获取每个文件夹的图片数量并显示在主界面上,由于默认显示所有图片,所以首先获取全部图片。

这里使用了LoaderManager异步加载相片数据,

    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        String where ;
        //选的不是第一个项目(指全部图片)
        if(!isAllPhotoMode()){
            String select = ((FolderAdapter) mRecycleTitle.getAdapter()).getSelect();
            where = String.format("%s='%s'",
                    MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
                    select);
        }else{
            where = "";
        }

        return new CursorLoader(PhotoPickActivity.this,MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection,
                where,
                null,
                MediaStore.MediaColumns.DATE_ADDED + " DESC");
    }

    //处理获取到的cursor数据
    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        if(isAllPhotoMode()){
            //全部图片的话,第一个item是照相机,AllPhotoAdapter继承GridPhotoAdapter为了添加照相机item
            photoAdapter = new AllPhotoAdapter(this,data);
        }else {
            photoAdapter = new GridPhotoAdapter(this,data);
        }
        mRecycleItem.setAdapter(photoAdapter);
        photoAdapter.setOnItemClick(mOnPhotoItemClick);
    }

通过获取到的数据更新图片。
上面的where参数是为了判断要拿哪一个文件夹里面的所有图片。它来自于文件夹列表的点击事件

private FolderAdapter.ItemClickListener mOnFolderItemClick = new FolderAdapter.ItemClickListener() {
        @Override
        public void onItemClick(int position,FolderAdapter.FolderHolder holder) {
            //更新当前选择的文件夹名称
            //主界面根据获取的文件夹名字来加载该文件夹下面的图片
            mFolderAdapter.setSelect(position);
            mFoldName.setText(mFolderAdapter.getSelect());
            hideFolderList();

            //每次点击文件夹列表,如果和上次不同的话都获取一次
            if(mFolderId != position){
                getLoaderManager().destroyLoader(mFolderId);
                mFolderId = position;
                getLoaderManager().initLoader(mFolderId, null, PhotoPickActivity.this);
            }
        }
    };
  • 最后就是出来图片的点击了。

    在多图片选择模式下,图片点击可以预览,同时还有个checkbox可以点击。
    由于使用recycleview显示数据,它不能定义item的点击事件,所以我在实例化item view的时候自定义个图片item点击回调和checkbox点击回调。

 public void onBindViewHolder(final GridPhotoAdapter.GridPhotoHolder holder,int position) {
        final String path = ImageInfo.pathAddPreFix(imageInfos.get(position).path);
        final int pos = position;
        ImageLoader.getInstance().displayImage(path, holder.icon,
                PhotoPickActivity.optionsImage);
        if(mActivity.getPhotoMode() == PhotoPickActivity.MODE_SINGLE_CROP){//单图片模式
            holder.check.setVisibility(View.GONE);
            holder.iconFore.setVisibility(View.GONE);
        }else{//多图片模式
            boolean isPick = mActivity.isPicked(path);
            holder.check.setChecked(isPick);
            //图片选中的话添加个蒙版
            holder.iconFore.setVisibility(holder.check.isChecked() ? View.VISIBLE : View.GONE);
            holder.check.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                     //设置个checkbox选择回调
                    itemPhotoClick.onCheckBoxClick(holder.check.isChecked(), path, holder.check);
                    holder.iconFore.setVisibility(holder.check.isChecked() ? View.VISIBLE : View.GONE);
                }
            });
        }
        holder.icon.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(itemPhotoClick != null){
                    Log.d("unlock", pos + "setOnClickListener" + path);
                    itemPhotoClick.onItemClick(pos,path);
                }
            }
        });
    }

上面的checkbox点击监听没有使用setOnCheckedChangeListener,因为使用它的时候,进入图片详情界面(PhotoPickDetailActivity)选择图片再回来的时候会发现多选了几个图片,原因怎么都找不到,果断偷懒。

图片的点击处理如下

    //图片的点击
    //如果是裁剪模式直接调到裁剪页面
    //如果是多选模式,则开启viewpager显示当前页面的图片
    GridPhotoAdapter.ItemPhotoClick mOnPhotoItemClick = new GridPhotoAdapter.ItemPhotoClick() {
        @Override
        public void onItemClick(int position,String path) {
            if(mPhotoMode == MODE_SINGLE_CROP){//点击图片调用系统裁剪功能
                Uri uri = Uri.parse(path);
                fileCropUri = CameraPhotoUtil.getOutputMediaFileUri();
                cropImageUri(uri, fileCropUri,640,640,REQUEST_RESULT_PHOTO_CROP);
            }else{
                //进入图片详情界面
                Intent intent = new Intent(PhotoPickActivity.this, PhotoPickDetailActivity.class);
                intent.putExtra(PhotoPickDetailActivity.PICK_DATA, mPickData);
                intent.putExtra(PhotoPickDetailActivity.EXTRA_MAX, mMaxPick);
                String folderParam = "";
                intent.putExtra(PhotoPickDetailActivity.PHOTO_BEGIN, position);
                folderParam = mFolderAdapter.getSelect();
                if(folderParam.equals(allPhotos))
                    folderParam = "";
                intent.putExtra(PhotoPickDetailActivity.FOLDER_NAME, folderParam);
                startActivityForResult(intent, REQUEST_PHOTO_DETAIL);
            }
        }

        //点击拍照
        @Override
        public void onCameraClick() {
            if(mPickData.size() >= mMaxPick){
                Toast.makeText(PhotoPickActivity.this,String.format("最多只能选择%s张",mMaxPick),Toast.LENGTH_SHORT).show();
                return;
            }
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            fileUri = CameraPhotoUtil.getOutputMediaFileUri();
            intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
            startActivityForResult(intent, REQUEST_RESULT_PHOTO);
        }
        //点击checkbox
        @Override
        public void onCheckBoxClick(boolean isCheck, String path,View view) {
            if (isCheck) {
                if(mPickData.size()>=mMaxPick){
                    Toast.makeText(PhotoPickActivity.this,String.format("最多只能选择%s张",mMaxPick),Toast.LENGTH_SHORT).show();
                    ((CheckBox)view).setChecked(false);
                    return ;
                }
                //选中图片的list中没有该图片的话,添加它
                if (!isPicked(path) && mPickData.size() < mMaxPick) {
                    ImageInfo imageInfo = new ImageInfo(path);
                    mPickData.add(imageInfo);
                }
            } else {//取消checkbox
                for (int i = 0; i < mPickData.size(); i++) {
                    if (mPickData.get(i).path.equals(path)) {
                        mPickData.remove(i);
                    }
                }
            }
            updatePickCount();
        }
    };

点击图片item进入详情页就不多说了,也就是在viewpager显示传过去的图片list,并将处理的list通过setResult放回到PhotoPickActivity,PhotoPickActivity最终也是将数据setResult返回给调用它的activity.

WrapContentLinearLayoutManager:recycleview显示的list设置高度wrap_content时失效,上stackoverflow查了一下,大神们重写了LinearLayoutManager,就可以设置它wrap_content了。

大致介绍就到这里了,大家感兴趣的可以下载源码看看

源码点击下载

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值