zoukankan      html  css  js  c++  java
  • Android实现本地图片选择及预览缩放效果仿春雨医生

    在做项目时常常会遇到选择本地图片的需求。曾经都是懒得写直接调用系统方法来选择图片。可是这样并不能实现多选效果。近期又遇到了,所以还是写一个demo好了。以后也方便使用。还是首先来看看效果


    显示的图片使用RecyclerView实现的,利用Glide来载入;以下弹出的图片目录效果是採用PopupWindow实现,这里比採用PopupWindow更方便,弹出显示的左边图片是这个目录里的第一张图片;选中的图片能够进行预览,使用网上一个大神写的来实现的;至于图片的获取是用ContentProvider。


    看看主界面的布局文件。上面一栏是一个返回button和一个跳转预览界面的button。依据是否有选中的图片来设置它的点击和显示状态。中间就是一个用于显示图片的RecyclerView,左下角是显示目录的名字可点击切换。右下角就是确定button。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.cdxsc.imageselect_y.ImageSelecteActivity">
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:background="@android:color/white">
    
            <ImageButton
                android:id="@+id/ib_back"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginLeft="10dp"
                android:background="@mipmap/action_bar_back_normal" />
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginLeft="10dp"
                android:layout_toRightOf="@id/ib_back"
                android:text="选择图片"
                android:textColor="#000"
                android:textSize="16sp" />
    
            <TextView
                android:id="@+id/tv_preview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:layout_marginRight="10dp"
                android:enabled="false"
                android:text="预览"
                android:textColor="#BEBFBF"
                android:textSize="16sp" />
        </RelativeLayout>
    
        <View
            android:layout_width="match_parent"
            android:layout_height="0.5dp"
            android:background="#eeeeee" />
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"></android.support.v7.widget.RecyclerView>
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="50dp">
    
            <TextView
                android:id="@+id/tv_allPic"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_centerVertical="true"
                android:layout_marginLeft="10dp"
                android:clickable="true"
                android:gravity="center_vertical"
                android:text="全部图片"
                android:textColor="@android:color/black"
                android:textSize="16sp" />
    
            <Button
                android:id="@+id/bt_confirm"
                android:layout_width="wrap_content"
                android:layout_height="35dp"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:layout_marginRight="10dp"
                android:background="@drawable/shape_disable"
                android:enabled="false"
                android:text="确定"
                android:textColor="#676767"
                android:textSize="16sp" />
        </RelativeLayout>
    </LinearLayout>
    


    好了。如今看主界面的代码

    public class ImageSelecteActivity extends AppCompatActivity {
    
        private static final String TAG = "lzy";
        @BindView(R.id.ib_back)
        ImageButton mButtonBack;
        @BindView(R.id.tv_preview)
        TextView mTextViewPreview;
        @BindView(R.id.rv)
        RecyclerView mRecyclerView;
        @BindView(R.id.tv_allPic)
        TextView mTextViewAllPic;
        @BindView(R.id.bt_confirm)
        Button mButtonConfirm;
        private GalleryPopupWindow mPopupWindow;
        //存储每一个目录下的图片路径,key是文件名称
        private Map<String, List<String>> mGroupMap = new HashMap<>();
        private List<ImageBean> list = new ArrayList<>();
        //当前目录显示的图片路径
        private List<String> listPath = new ArrayList<>();
        //所选择的图片路径集合
        private ArrayList<String> listSelectedPath = new ArrayList<>();
    
    
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                //扫描完毕后
                getGalleryList();
                listPath.clear();
                listPath.addAll(mGroupMap.get("全部图片"));
                adapter.update(listPath);
                if (mPopupWindow != null)
                    mPopupWindow.notifyDataChanged();
            }
        };
        private ImageSelectAdapter adapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_image_selecte);
            ButterKnife.bind(this);
            init();
        }
    
        private void init() {
            getImages();
            mRecyclerView.setLayoutManager(new GridLayoutManager(ImageSelecteActivity.this, 3));
            adapter = new ImageSelectAdapter(this, listPath);
            mRecyclerView.setAdapter(adapter);
            adapter.setOnCheckedChangedListener(onCheckedChangedListener);
        }
    
        @OnClick({R.id.ib_back, R.id.tv_preview, R.id.tv_allPic, R.id.bt_confirm})
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.ib_back:
                    finish();
                    break;
                case R.id.tv_preview://跳转预览界面
                    Intent intent = new Intent(ImageSelecteActivity.this, ImagePreviewActivity.class);
                    //把选中的图片集合传入预览界面
                    intent.putStringArrayListExtra("pic", listSelectedPath);
                    startActivity(intent);
                    break;
                case R.id.tv_allPic://选择图片目录
                    if (mPopupWindow == null) {
                        //把目录列表的集合传入显示
                        mPopupWindow = new GalleryPopupWindow(this, list);
                        mPopupWindow.setOnItemClickListener(new GalleryPopupWindow.OnItemClickListener() {
                            @Override
                            public void onItemClick(String fileName) {
                                //切换了目录。清除之前的选择的信息
                                setButtonDisable();
                                listPath.clear();
                                listSelectedPath.clear();
                                //把当前选择的目录内图片的路径放入listPath,更新界面
                                listPath.addAll(mGroupMap.get(fileName));
                                adapter.update(listPath);
                                mTextViewAllPic.setText(fileName);
                            }
                        });
                    }
                    mPopupWindow.showAtLocation(mRecyclerView, Gravity.BOTTOM, 0, dp2px(50, ImageSelecteActivity.this));
                    break;
                case R.id.bt_confirm://确定
                    for (int i = 0; i < listSelectedPath.size(); i++) {
                        //这里可通过Glide把它转为Bitmap
                        Glide.with(this).load("file://" + listSelectedPath.get(i)).asBitmap().into(new SimpleTarget<Bitmap>() {
                            @Override
                            public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
                                Log.i(TAG, "onResourceReady: " + resource);
                            }
                        });
                    }
                    break;
            }
        }
    
        /**
         * dp转px
         */
        public static int dp2px(int dp, Context context) {
            return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
                    context.getResources().getDisplayMetrics());
        }
    
        //选择图片变化的监听
        private ImageSelectAdapter.OnCheckedChangedListener onCheckedChangedListener = new ImageSelectAdapter.OnCheckedChangedListener() {
            @Override
            public void onChanged(boolean isChecked, String path, CheckBox cb, int position) {
                if (isChecked) {//选中
                    if (listSelectedPath.size() == 9) {
                        Toast.makeText(ImageSelecteActivity.this, "最多选择9张图片", Toast.LENGTH_SHORT).show();
                        //把点击变为checked的图片变为没有checked
                        cb.setChecked(false);
                        adapter.setCheckedBoxFalse(position);
                        return;
                    }
                    //选中的图片路径加入集合
                    listSelectedPath.add(path);
    
                } else {//取消选中
                    //从集合中移除
                    if (listSelectedPath.contains(path))
                        listSelectedPath.remove(path);
                }
                //假设没有选中的button不可点击
                if (listSelectedPath.size() == 0) {
                    setButtonDisable();
                } else {
                    setButtonEnable();
                }
            }
        };
    
        //选中图片时的button状态
        private void setButtonEnable() {
            mButtonConfirm.setBackgroundResource(R.drawable.selector_bt);
            mButtonConfirm.setTextColor(Color.parseColor("#ffffff"));
            mButtonConfirm.setEnabled(true);
            mTextViewPreview.setEnabled(true);
            mTextViewPreview.setTextColor(getResources().getColor(R.color.colorAccent));
            mButtonConfirm.setText("确定" + listSelectedPath.size() + "/9");
        }
    
        //没有选择时button状态
        private void setButtonDisable() {
            mButtonConfirm.setBackgroundResource(R.drawable.shape_disable);
            mButtonConfirm.setTextColor(Color.parseColor("#676767"));
            mButtonConfirm.setEnabled(false);
            mTextViewPreview.setEnabled(false);
            mTextViewPreview.setTextColor(Color.parseColor("#BEBFBF"));
            mButtonConfirm.setText("确定");
        }
    
        /**
         * 利用ContentProvider扫描手机中的图片。此方法在执行在子线程中
         */
        private void getImages() {
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                    ContentResolver mContentResolver = ImageSelecteActivity.this.getContentResolver();
                    //仅仅查询jpeg和png的图片
    //                Cursor mCursor = mContentResolver.query(mImageUri, null,
    //                        MediaStore.Images.Media.MIME_TYPE + "=? or "
    //                                + MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?",
    //                        new String[]{"image/jpeg", "image/png", "image/jpg"}, MediaStore.Images.Media.DATE_MODIFIED);
                    Cursor mCursor = mContentResolver.query(mImageUri, null, null, null,
                            MediaStore.Images.Media.DATE_MODIFIED);
                    if (mCursor == null) {
                        return;
                    }
                    //存放全部图片的路径
                    List<String> listAllPic = new ArrayList<String>();
                    while (mCursor.moveToNext()) {
                        //获取图片的路径
                        String path = mCursor.getString(mCursor
                                .getColumnIndex(MediaStore.Images.Media.DATA));
    
                        //获取该图片的父路径名
                        String parentName = new File(path).getParentFile().getName();
                        listAllPic.add(path);
    
                        //依据父路径名将图片放入到mGruopMap中
                        if (!mGroupMap.containsKey(parentName)) {
                            List<String> chileList = new ArrayList<String>();
                            chileList.add(path);
                            mGroupMap.put(parentName, chileList);
                        } else {
                            mGroupMap.get(parentName).add(path);
                        }
                    }
                    //加入全部图片
                    mGroupMap.put("全部图片", listAllPic);
                    //通知Handler扫描图片完毕
                    mHandler.sendEmptyMessage(0);
                    mCursor.close();
                }
            }).start();
    
        }
    
        //获取相冊目录列表
        private void getGalleryList() {
            Iterator<Map.Entry<String, List<String>>> iterator = mGroupMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, List<String>> next = iterator.next();
                ImageBean imageBean = new ImageBean();
                imageBean.setFileName(next.getKey());
                imageBean.setFirstPicPath(next.getValue().get(0));
                imageBean.setCount(next.getValue().size());
                if (next.getKey().equals("全部图片"))
                    list.add(0, imageBean);
                else
                    list.add(imageBean);
            }
        }
    }

    ·mGroupMap:这个是以目录名为key,目录内的图片路径集合为value,也就是依照目录来分别存储了全部图片的路径。

    ·listPath:保存的是当前显示在界面上的目录内的图片路径集合

    ·listSelectedPath:保存用户选中的图片路径

    ·list:保存的是ImageBean的集合。ImageBean保存了目录名、里面首张图片的路径以及里面所包括图片的数量,当切换目录时用于显示

    ·getImages():这种方法就是用来扫描手机里图片并保存的,这是在子线程中执行的,显示这可能是一个耗时的任务。通过ContentProvider获取到一个包括全部图片的Cursor,然后遍历这个Cursor把所需的数据就保存在mGroupMap里面,最后利用Handler通知界面更新。
    ·getGalleryList():这种方法就是mGroupMap里面的数据来给list赋值,也就是产生一个现实目录列表所需的数据集合。

    ·GalleryPopupWindow也就是我们用于显示文件列表的,在67--84行就是一些GalleryPopupWindow的设置,调用showAtLocation方法把PopupWindow显示在距离底部50dp的位置,并设置了点击的回调,当切换了一个目录后要做的相关操作就在这里进行。GalleryPopupWindow再待会再详细看看


    接下来再看看中间RecyclerView的Adapter

    public class ImageSelectAdapter extends RecyclerView.Adapter<ImageSelectAdapter.NViewHolder> {
    
        private Context context;
        private List<String> list = new ArrayList<>();
        private OnCheckedChangedListener onCheckedChangedListener;
        private List<Boolean> listChecked = new ArrayList<>();
    
        public ImageSelectAdapter(Context context, List<String> list) {
            this.context = context;
            this.list.addAll(list);
            setListCheched(list);
        }
    
        public void update(List<String> list) {
            this.list.clear();
            this.list.addAll(list);
            setListCheched(list);
            notifyDataSetChanged();
    
        }
    
        /**
         * 设置listChecked的初始值
         *
         * @param list
         */
        private void setListCheched(List<String> list) {
            listChecked.clear();
            for (int i = 0; i < list.size(); i++) {
                listChecked.add(false);
            }
        }
    
        //当点击超过了九张图片,再点击的设置为false
        public void setCheckedBoxFalse(int pos) {
            listChecked.set(pos, false);
        }
    
        public interface OnCheckedChangedListener {
            /**
             * @param isChecked 是否选中
             * @param path      点击的图片路径
             * @param cb        点击的CheckBox
             * @param pos       点击的位置
             */
            void onChanged(boolean isChecked, String path, CheckBox cb, int pos);
        }
    
        public void setOnCheckedChangedListener(OnCheckedChangedListener onCheckedChangedListener) {
            this.onCheckedChangedListener = onCheckedChangedListener;
        }
    
        @Override
        public NViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new NViewHolder(LayoutInflater.from(context).inflate(R.layout.item_image_select, parent, false));
        }
    
        @Override
        public void onBindViewHolder(final NViewHolder holder, final int position) {
            Glide.with(context).load("file://" + list.get(position)).into(holder.iv);
            holder.cb.setChecked(listChecked.get(position));
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    holder.cb.setChecked(!holder.cb.isChecked());
                    if (holder.cb.isChecked()) {
                        listChecked.set(position, true);
                    } else {
                        listChecked.set(position, false);
                    }
                    if (onCheckedChangedListener != null) {
                        onCheckedChangedListener.onChanged(holder.cb.isChecked(), list.get(position), holder.cb, position);
                    }
                }
            });
        }
    
        @Override
        public int getItemCount() {
            return list.size();
        }
    
        public class NViewHolder extends RecyclerView.ViewHolder {
            @BindView(R.id.iv_itemImageSelect)
            ImageView iv;
            @BindView(R.id.cb_itemImageSelect)
            CheckBox cb;
    
            public NViewHolder(View itemView) {
                super(itemView);
                ButterKnife.bind(this, itemView);
            }
        }
    
    }

    这里Item的布局文件就是一个ImageView加一个CheckBox。依据选中状态改变CheckBox的状态,这里就不贴出来了。

    ·listChecked:这个集合是用来存储每一个位置是否Check的,假设在onBindViewHolder里面不设置CheckBox的状态的话,由于复用问题会出问题,所以想出了用一个集合来保存它们状态的方法,不知道大家有没有其它更好的方法。

    ·OnCheckedChangedListener:向外暴露的接口,把点击的位置等參数都传到Activity中去。

    ·update():这种方法用来更新界面的,没有採用直接调notifyDataSetChanged方法是由于,假设数据的数量变化了那么listChecked的数量也要发生变化才行这样才干相应。所以写了这种方法。


    再接着看看GalleryPopupWindow

    /**
     * Created by lzy on 2017/2/8.
     */
    public class GalleryPopupWindow extends PopupWindow {
        private static final String TAG = "lzy";
    
        RecyclerView mRecyclerView;
    
        private Activity activity;
        private GalleryPopupWindow.OnItemClickListener onItemClickListener;
        private List<ImageBean> list;
        private GalleryAdapter adapter;
    
    
        public GalleryPopupWindow(Activity context, List<ImageBean> list) {
            super(context);
            this.activity = context;
            this.list = list;
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View contentView = inflater.inflate(R.layout.popu_gallery, null);
            initView(contentView);
    
            int h = context.getWindowManager().getDefaultDisplay().getHeight();
            int w = context.getWindowManager().getDefaultDisplay().getWidth();
            this.setContentView(contentView);
            this.setWidth(w);
            this.setHeight(ImageSelecteActivity.dp2px(350, context));
            this.setFocusable(false);
            this.setOutsideTouchable(true);
            this.update();
    
            setBackgroundDrawable(new ColorDrawable(000000000));
        }
    
        public void notifyDataChanged() {
            adapter.notifyDataSetChanged();
        }
    
        private void initView(View contentView) {
            mRecyclerView = (RecyclerView) contentView.findViewById(R.id.rv_gallery);
            mRecyclerView.setLayoutManager(new LinearLayoutManager(activity));
            adapter = new GalleryAdapter(list, activity);
            adapter.setOnItemClickListener(new GalleryAdapter.OnItemClickListener() {
                @Override
                public void onItemClick(String fileName) {
                    if (onItemClickListener != null) {
                        onItemClickListener.onItemClick(fileName);
                        dismiss();
                    }
                }
            });
            mRecyclerView.setAdapter(adapter);
    
        }
    
        //暴露点击的接口
        public interface OnItemClickListener {
            /**
             * @param keyValue
             */
            void onItemClick(String keyValue);
        }
    
        public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
            this.onItemClickListener = onItemClickListener;
        }
    }


    这个PopupWindow的布局文件就是一个RecyclerView,所以这里面也没什么。也就是设置RecyclerView。然后向外暴露一个点击的接口,用于Activity接收是点击了哪个目录,所以接口參数也就是目录名,再看看这个PopupWindow的Adapter

    /**
     * Created by lzy on 2017/2/8.
     */
    public class GalleryAdapter extends RecyclerView.Adapter<GalleryAdapter.NViewHolder> {
    
        private Context context;
        private List<ImageBean> list;
        private OnItemClickListener onItemClickListener;
        //用于记录是选中的哪一个目录
        private int selectedPos;
    
        public GalleryAdapter(List<ImageBean> list, Context context) {
            this.list = list;
            this.context = context;
        }
    
        public interface OnItemClickListener {
            void onItemClick(String fileName);
        }
    
        public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
            this.onItemClickListener = onItemClickListener;
        }
    
        @Override
        public NViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new NViewHolder(LayoutInflater.from(context).inflate(R.layout.item_gallery, parent, false));
        }
    
        @Override
        public void onBindViewHolder(NViewHolder holder, final int position) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    selectedPos = position;
                    notifyDataSetChanged();
                    if (onItemClickListener != null) {
                        onItemClickListener.onItemClick(list.get(position).getFileName());
                    }
                }
            });
            if (position == selectedPos) {
                holder.ivCheck.setVisibility(View.VISIBLE);
            } else {
                holder.ivCheck.setVisibility(View.GONE);
            }
            holder.tvCount.setText(list.get(position).getCount() + "张");
            holder.tvName.setText(list.get(position).getFileName());
            Glide.with(context).load("file://" + list.get(position).getFirstPicPath()).into(holder.iv);
        }
    
        @Override
        public int getItemCount() {
            return list.size();
        }
    
        public class NViewHolder extends RecyclerView.ViewHolder {
            @BindView(R.id.iv_itemGallery)
            ImageView iv;
            @BindView(R.id.tv_itemGallery_name)
            TextView tvName;
            @BindView(R.id.tv_itemGallery_count)
            TextView tvCount;
            @BindView(R.id.iv_itemGallery_check)
            ImageView ivCheck;
    
            public NViewHolder(View itemView) {
                super(itemView);
                ButterKnife.bind(this, itemView);
            }
        }
    
    }

    这里有个接口是把点击的文件名称传递给PopupWindow。然后再给Activity。selectedPos是用来记录选择的是哪一个目录。显示相应的CheckBox。


    这里就几乎相同完毕了,感兴趣的能够下载Demo来看看。

    再说一下,这里显示图片都是採用的Glide。使用也非常方便,我们获取的图片路径都是文件路径。假设要转化为Bitmap也能够直接调用Glide的方法就能够轻松实现,例如以下所看到的:

    Glide.with(this).load("file://" + listSelectedPath.get(i)).asBitmap().into(new SimpleTarget<Bitmap>() {
                            @Override
                            public void onResourceReady(Bitmap resource, GlideAnimation<?

    super Bitmap> glideAnimation) { Log.i(TAG, "onResourceReady: " + resource); } });


    当中找寻控件都没有使用findViewById,而是採用的ButterKnife。节约了大量的时间,顺便说说导入的方法

    在app以下的build.gradle中加入以下:

    apply plugin: 'com.neenbedankt.android-apt'
    apt 'com.jakewharton:butterknife-compiler:8.1.0'
        compile 'com.github.bumptech.glide:glide:3.5.2'

    项目以下的build.gradle

     //加入apt插件
            classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

    加入插件

    File->Setting->Plugins  搜索zelezny。例如以下所看到的


    当须要使用的时候。直接在光标移动到布局文件,点击Alt+Insert。选择Generate ButterKnife Injections

    就出现例如以下界面,能够自己主动生成了



    源代码地址:http://download.csdn.net/detail/lylodyf/9768761


  • 相关阅读:
    函数封装总结
    03.深入javascript
    02.JavaScript基础下
    html5权威指南:客户端分区响应图
    html5权威指南:定制input元素
    html5权威指南:表单元素
    html5权威指南:表格元素
    html5权威指南:组织内容、文档分节
    css布局理解
    html5权威指南:标记文字
  • 原文地址:https://www.cnblogs.com/llguanli/p/8407220.html
Copyright © 2011-2022 走看看