高仿微信图片选择

很久之前项目中用到的一个图片选择功能,于是当时做一个简单的demo。测试了一下,支持图片的选择,预览,删除等基本操作,支持图片的倒序显示,拍照添加图片,以及过滤较小图片等。

部分截图,如下
图片选择

相册列表

图片预览

功能描述:
1. 点击添加图片按钮进入图片选择页面,选择的图片显示在主界面,并且可以删除。
2. 在图片选择页面可以点击chechbox选择图片,点击图片进行预览,可靠图片有个数限制。
3. 点击左下方按钮进入相册选择列表,可以浏览不同相册。
4. 点击拍照按钮可以进行拍照并且将拍照图片直接显示在主界面。
5. 预览界面可以进行图片选择,点击完成所选图片显示在主界面,若无已选图片,则预览图片默认被选择。

主要代码:
最重要的代码就是对手机图片的扫描,这里是利用ContentProvider,然后将获取到的图片按照名称为key保存到一个map,测试中发现微信是将文件夹名称相同的图片列为同一相册,而qq是根据不同的id列为同一相册。同时自定义了一个全部图片的相册,用来保存所有的图片并且过滤掉了较小的图片(微信的默认列表就对图片进行了过滤),这样就避免了有些应用产生的垃圾图片显示在主界面(如一堆空白图)。详细代码:

public class AlbumHelper extends AsyncTask<Object, Object, Object>{

    final String TAG = getClass().getSimpleName();
    //过滤掉非常小的图片的参数(微信的默认图片列表貌似就是过滤掉了较小的图片)
    public static int mMinImageSize = 5000;//5000b
    Context context;
    ContentResolver cr;
    // 专辑列表
    LinkedHashMap<String, PhotoImageBucket> bucketList = new LinkedHashMap<>();//保证相册的顺序与输入相同
    private GetAlbumList getAlbumList;

    private String allPhotosBucketId = "-11111111111";//自定义全部图片的bucketId


    /**
     * 初始化
     * @param context
     */
    public void init(Context context) {
        if (this.context == null) {
            this.context = context;
            cr = context.getContentResolver();
        }
    }


    /**
     * 是否创建了图片集
     */
    boolean hasBuildImagesBucketList = false;
    /**
     * 得到图片集
     */
    private void buildImagesBucketList() {
        // 构造相册索引
        String columns[] = new String[] { Media._ID, Media.BUCKET_ID,
                Media.PICASA_ID, Media.DATA, Media.DISPLAY_NAME, Media.TITLE,
                Media.SIZE, Media.BUCKET_DISPLAY_NAME };

        //按时间倒序
        String sortOrder = Media.DATE_ADDED + " DESC";

        // 得到一个游标
        Cursor cur = cr.query(Media.EXTERNAL_CONTENT_URI, columns, null, null,
                sortOrder);
        if (cur == null) {
            LogUtils.logd(TAG, "call: " + "Empty images");
        }else
        if (cur.moveToFirst()) {
            //设置存放所有图片的相册,第一个入存放相册的map
            PhotoImageBucket allBucket = new PhotoImageBucket();
            allBucket.setImageList(new ArrayList<PhotoImageItem>());
            allBucket.setBucketName("所有图片");
            bucketList.put(allPhotosBucketId, allBucket);

            // 获取指定列的索引
            int photoIDIndex = cur.getColumnIndexOrThrow(Media._ID);
            int photoPathIndex = cur.getColumnIndexOrThrow(Media.DATA);
            int bucketDisplayNameIndex = cur.getColumnIndexOrThrow(Media.BUCKET_DISPLAY_NAME);
            int photoSizeIndex = cur.getColumnIndexOrThrow(Media.SIZE);
            int bucketIdIndex = cur.getColumnIndexOrThrow(Media.BUCKET_ID);

            do {
                //过滤掉名字为空的图片,如.jpg,.png等
                if (cur.getString(photoPathIndex).substring(
                        cur.getString(photoPathIndex).lastIndexOf("/")+1,
                        cur.getString(photoPathIndex).lastIndexOf("."))
                        .replaceAll(" ", "").length()<=0)
                {
                    LogUtils.logd(TAG, "出现了异常图片的地址:cur.getString(photoPathIndex)="+cur.getString(photoPathIndex));
                }else {
                    String _id = cur.getString(photoIDIndex);
                    String path = cur.getString(photoPathIndex);
                    String bucketName = cur.getString(bucketDisplayNameIndex);
                    int photoSize = cur.getInt(photoSizeIndex);
                    //以相册句为key将相册名相册的图片放入同一个相册,如果想不同相册分开显示可以使用bucketId为key
                    //String bucketId = cur.getString(bucketIdIndex);
                    //PhotoImageBucket bucket = bucketList.get(bucketId);
                    PhotoImageBucket bucket = bucketList.get(bucketName);
                    //这里完成图片归并到相应的相册里去
                    if (bucket == null) {
                        bucket = new PhotoImageBucket();
                        bucketList.put(bucketName, bucket);//使用bucketName进行合并,将名字相同的保存到同一相册
                        bucket.setImageList(new ArrayList<PhotoImageItem>());
                        bucket.setBucketName(bucketName);
                    }
                    bucket.setCount(bucket.getCount() + 1);
                    PhotoImageItem imageItem = new PhotoImageItem();
                    imageItem.setImageId(_id);
                    imageItem.setImagePath(path);
                    bucket.getImageList().add(imageItem);
                    //将每一张图片都存入所有图片的相册里
                    LogUtils.loge(TAG, path + "");
                    if(photoSize > mMinImageSize){//所有图片里过滤掉较小图片,避免显示过多无效图片(空图)---测试微信中进行了过滤,不知道过滤规则
                        allBucket.setCount(allBucket.getCount() + 1);
                        allBucket.getImageList().add(imageItem);
                    }
                }
            } while (cur.moveToNext());
        }
        cur.close();
        hasBuildImagesBucketList = true;
    }

    /**
     * 得到图片集
     * @param refresh
     * @return tmpList
     */
    private List<PhotoImageBucket> getImagesBucketList(boolean refresh) {
        if (refresh || (!refresh && !hasBuildImagesBucketList)) {
            buildImagesBucketList();
        }
        List<PhotoImageBucket> tmpList = new ArrayList<>();
        Iterator<Entry<String, PhotoImageBucket>> itr = bucketList.entrySet().iterator();
        //将Hash转化为List
        while (itr.hasNext()) {
            Map.Entry<String, PhotoImageBucket> entry = itr
                    .next();
            tmpList.add(entry.getValue());
        }
        return tmpList;
    }

    public void setGetAlbumList(GetAlbumList getAlbumList) {
        this.getAlbumList = getAlbumList;
    }
    //回调接口
    public interface GetAlbumList{
        void getAlbumList(List<PhotoImageBucket> list);
    }

    @Override
    protected Object doInBackground(Object... params) {
        return getImagesBucketList((Boolean)(params[0]));
    }
    @SuppressWarnings("unchecked")
    @Override
    protected void onPostExecute(Object result) {
        super.onPostExecute(result);
        getAlbumList.getAlbumList((List<PhotoImageBucket>) result);
    }

}

以上代码中出现的PhotoImageBucket ,与PhotoImageItem 是创建用来存放相册信息与图片信息的bean,这里就不贴代码了,结尾会附上全部代码。

图片选择界面是一个gridView用来显示图片以及listView用来显示相册列表(可用popupWindow个人喜好),在gridView中的图片列表上添加了一个遮罩层,避免图片与checkbos颜色相同相互影响,另外当选中图片后将图片置暗。gridView的adapter中添加了两个回调监听,分别是点击图片预览与点击checkbox选择,代码如下:

public class AlbumGridViewAdapter extends BaseAdapter{
    final String TAG = getClass().getSimpleName();
    private LayoutInflater inflater;
    private ArrayList<PhotoImageItem> dataList;
    private ArrayList<PhotoImageItem> selectedDataList;
    private BitmapUtils mBitmapUtils;

    public AlbumGridViewAdapter(Context context, ArrayList<PhotoImageItem> dataList,
                                ArrayList<PhotoImageItem> selectedDataList) {
        inflater = LayoutInflater.from(context);
        this.dataList = dataList;
        this.selectedDataList = selectedDataList;

        mBitmapUtils = new BitmapUtils(context);
        mBitmapUtils.configDefaultLoadingImage(R.mipmap.album_default_loading_pic);//默认背景图片
        mBitmapUtils.configDefaultLoadFailedImage(R.mipmap.album_default_loading_pic);//加载失败图片
        mBitmapUtils.configDefaultBitmapConfig(Bitmap.Config.RGB_565);//设置图片压缩类型

    }

    public int getCount() {
        return dataList.size();
    }

    public Object getItem(int position) {
        return dataList.get(position);
    }

    public long getItemId(int position) {
        return 0;
    }



    private static class ViewHolder {
        public SquareImageView imageView;
        public CheckBox checkBox;
        public View maskView;
        public LinearLayout ll;
    }

    public View getView(final int position, View convertView, ViewGroup parent) {
        final ViewHolder viewHolder;
        if (convertView == null) {
            viewHolder = new ViewHolder();
            convertView = inflater.inflate(R.layout.album_pic_item, parent, false);
            viewHolder.imageView = (SquareImageView)convertView.findViewById(R.id.item_selected_image);
            viewHolder.maskView = convertView.findViewById(R.id.image_mask);
            viewHolder.checkBox = (CheckBox)convertView.findViewById(R.id.album_checkbox);
            viewHolder.ll = (LinearLayout)convertView.findViewById(R.id.album_item_ll);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        if(selectedDataList.contains(dataList.get(position))){
            viewHolder.checkBox.setChecked(true);
            viewHolder.maskView.setBackgroundResource(R.color.image_select_mask);
        }else{
            viewHolder.checkBox.setChecked(false);
            viewHolder.maskView.setBackgroundResource(R.color.image_default_mask);
        }

        viewHolder.imageView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (dataList != null && mOnPhotoClickListener != null
                        && position < dataList.size()) {
                    mOnPhotoClickListener.onItemClick(position);
                }
            }
        });



        viewHolder.ll.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (dataList != null && mOnCheckBoxClickListener != null
                        && position < dataList.size()) {
                    mOnCheckBoxClickListener.onItemClick(viewHolder.maskView, position, viewHolder.checkBox);
                }
            }
        });

        mBitmapUtils.display(viewHolder.imageView, dataList.get(position).getImagePath());

        return convertView;
    }


    private OnPhotoClickListener mOnPhotoClickListener;

    public void setOnPhotoClickListener(OnPhotoClickListener l){
        mOnPhotoClickListener = l;
    }

    //预览图片的回调监听
    public interface OnPhotoClickListener{
        void onItemClick(int position);
    }

    private OnCheckBoxClickListener mOnCheckBoxClickListener;

    public void setOnCheckBoxClickListener(OnCheckBoxClickListener l){
        mOnCheckBoxClickListener = l;
    }

    //选择图片的预览监听
    public interface OnCheckBoxClickListener{
        void onItemClick(View maskView, int position, CheckBox checkBox);
    }

}

关于拍照功能的实现代码如下:

public void photo() {
    if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
            //照片的路径
            filePath = Environment.getExternalStorageDirectory()
                    + "/albumtest/" + "testPIC/";
            //照片名
            fileName = "albumtestpic." + String.valueOf(System.currentTimeMillis()) + ".jpg";
            File file = new File(filePath);
            if (!file.exists()) {
                file.mkdirs();
            }
            photoUri = Uri.fromFile(new File(filePath, fileName));
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
            startActivityForResult(intent, TAKE_PICTURE);
        } else {
            Toast.makeText(AlbumActivity.this, "内存卡不可用,不能拍照", Toast.LENGTH_LONG).show();
        }
    }

这里通过intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);修改了图片的保存路径,然后在onActivityResult进行处理。这里有一点需要注意,当拍照完成之后一定要对该照片进行扫描,否则你将在图片列表中找不到你所拍摄的照片(测试微信等应该也无法显示该照片),代码如下:

MediaScannerConnection.scanFile(this,
        new String[]{filePath + fileName}, null,
        new MediaScannerConnection.OnScanCompletedListener() {
            public void onScanCompleted(String path, Uri uri) {

            }
        });

相册列表的实现比较简单这里就不再赘述。图片预览界面就是一个viewPager也比较简单,只是在PagerAdapter的instantiateItem中进行加载图片,避免一次性添加很多图片引起oom。另外关于图片选择个数的限制,以及点击完成图片显示在主界面等功能在这里也不再赘述,大家可以在全部代码中查看。

项目结构:
这里写图片描述

由于本例只是个demo,并没有进行测试,肯定会存在一些bug,例如图片加载的oom,在这里有很多框架例如Android-Universal-Image-LoaderxUtils等,当然你也可以自己去设置图片的
inSampleSize,缓存以及存储路径。demo当时使用的是xUtils较低版本。另外拍照功能的实现在有些手机上也会出现异常,在此demo上并没有进行测试。

具体内容,请查看源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值