Android仿微信圖片選擇器,支持圖片選擇和拍照

Android圖片選擇器
本文是我的第二篇博客,如果有錯誤的地方,希望大家多多指點,相信大家都知道網上有很多第三方的圖片選擇器,但是在真正的項目中卻遇到例如一個很重要的問題,定制屬于自己的圖品選擇器,很多第三方的無法滿足我們的這一需求,因此我自己寫了一個,代碼簡單,我在下面附帶了源碼,剛興趣的朋友可以下載了自己修改. 本項目沒有依賴人任何第三方的類庫,主要設計思路就是:一個圖片加載類(單例)+利用ContentProvider掃描手機的圖片+GridView顯示圖片 本篇博客的主要內容是仿微信圖片選擇器,主要步驟有如下幾點
① 圖片加載類(單例)
② 獲取手機本地圖片
③ 可以設置圖片選擇個數
下面是在是真機上的效果預覽:

相冊選擇
選擇圖片
圖片預覽

本項目的主要功能:
① 默認顯示圖片最多的文件夾圖片,以及底部顯示圖片總數量;
② 點擊底部旅东,彈出popupWindow,popupWindow包含所有含有圖片的文件夾杈湾,以及顯示每個文件夾中圖片數量睁本;注:此時Activity變暗
③ 選擇任何文件夾,進入該文件夾圖片顯示工育,可以點擊選擇圖片,當然了鹃愤,點擊已選擇的圖片則會取消選擇簇搅;注:選中圖片變暗 當然了,最重要的效果一定流暢软吐,不能動不動OOM~~ 本人測試手機樂視1s瘩将,圖片3802張,和小米紅米,小米2s,未出現OOM異常凹耙,效果也是非常流暢, 不過存在bug在所難免姿现,大家可以留言說下自己發(fā)現的bug;文末會提供源碼下載肖抱。

下面是對項目的一一講解:

第一步:核心方法:圖片加載工具類 ImageLoader:本項目的圖片加載采用Google的Gilde,Glide是一個強大的資源加載工具類备典,支持視頻、圖片意述、GIF等多種格式的資源提佣,功能強大,不知道的同學可以去網上搜一搜荤崇。具體的用法在這里也不做贅述拌屏。

第二步:主界面設計:

主界面布局,主布局的設計思路是,用GridView布局顯示圖片,并為GridView編寫響應的適配器.

<RelativeLayout 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:background="@drawable/set_bg"
    tools:context="${relativePackage}.${activityClass}" >

    <RelativeLayout
        android:id="@+id/top"
        android:layout_width="match_parent"
        android:layout_height="50dip"
        android:background="#0079cd" >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:src="@drawable/return_icon" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:text="圖片"
            android:textColor="#FFFFFF"
            android:textSize="19sp" />
        
        <TextView
            android:id="@+id/select_image"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_alignParentRight="true"
            android:layout_centerInParent="true"
            android:layout_marginBottom="15dp"
            android:layout_marginLeft="15dp"
            android:layout_marginRight="10dp"
            android:background="@drawable/finish_btn"
            android:gravity="center"
            android:text="完成(0/3)"
            android:textColor="#FFFFFF"
            android:textSize="16sp" />

    </RelativeLayout>

    <GridView
        android:id="@+id/id_gridView"
        android:layout_marginTop="3dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/top"
        android:cacheColorHint="@android:color/transparent"
        android:horizontalSpacing="3dip"
        android:numColumns="3"
        android:stretchMode="columnWidth"
        android:verticalSpacing="3dp" >
    </GridView>

    <RelativeLayout
        android:id="@+id/id_bottom_ly"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:background="#88000000"
        android:clickable="true" >

        <TextView
            android:id="@+id/id_dir_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:paddingLeft="10dp"
            android:text="所有圖片"
            android:textColor="#e5e5e5"
            android:textSize="15sp" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="5dip"
            android:layout_toRightOf="@+id/id_dir_name"
            android:src="@drawable/more_select" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginRight="5dip"
            android:layout_toLeftOf="@+id/id_dir_num"
            android:src="@drawable/split_line" />

        <TextView
            android:id="@+id/id_dir_num"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:paddingRight="10dp"
            android:text="預覽"
            android:textSize="15sp"
            android:textColor="@android:color/white" />
    </RelativeLayout>

</RelativeLayout>

第三步:獲取手機上的全部圖片:

該方法會掃描手機上的所有圖片信息,并顯示到主界面上天试,掃描結束槐壳,得到一個所有包含圖片的文件夾信息的List;
對于文件夾信息喜每,我們單獨創(chuàng)建了一個FolderBean:
掃描說及圖片的方法

/**
     * 利用ContentProvider掃描手機的圖片
     */
    private void initDatas() {
        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            Toast.makeText(context, "當前存儲卡不可用", Toast.LENGTH_SHORT).show();
            return;
        }
        mProgress = ProgressDialog.show(context, null, "正在加載...");
        new Thread() {
            @Override
            public void run() {
                FolderBean folderBean1 = new FolderBean();
                folderBean1.setCount(0);
                folderBean1.setName("所有圖片");
                folderBean1.setDir("/所有圖片");
                folderBean1.setFirstImgPath("/所有圖片");
                mFolderBeans.add(folderBean1);
                Uri mImgUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                ContentResolver cr = context.getContentResolver();
                // 獲取手機上的所有的圖片
                Cursor cursor = cr.query(mImgUri, null,
                        MediaStore.Images.Media.MIME_TYPE + " = ? or " + MediaStore.Images.Media.MIME_TYPE + " = ? ",
                        new String[] { "image/jpeg", "image/png" }, MediaStore.Images.Media.DATE_MODIFIED);
                Log.e("圖片選擇器", "4");
                Set<String> mDirPaths = new HashSet<String>();
                Log.e("圖片選擇器", "5");
                while (cursor.moveToNext()) {
                    final String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
                    File parentFile = new File(path).getParentFile();
                    if (parentFile == null) {
                        continue;
                    }
                    String dirPath = parentFile.getAbsolutePath();
                    FolderBean folderBean = null;
                    if (mDirPaths.contains(dirPath)) {
                        continue;
                    } else {
                        mDirPaths.add(dirPath);
                        folderBean = new FolderBean();
                        folderBean.setDir(dirPath);
                        folderBean.setFirstImgPath(path);
                    }

                    if (parentFile.list() == null)
                        continue;
                    int picSize = parentFile.list(new FilenameFilter() {
                        //對文件進行過濾,獲取圖片文件
                        @Override
                        public boolean accept(File dir, String filename) {
                            if (filename.endsWith(".jpg") || filename.endsWith(".jpeg") || filename.endsWith(".png")) {
                                return true;
                            }
                            return false;
                        }
                    }).length;
                    folderBean.setCount(picSize);
                    mFolderBeans.add(folderBean);
                    if (picSize > mMaxCount) {
                        mMaxCount = picSize;
                        mCurrentDir = parentFile;
                    }
                }
                Log.e("圖片選擇器", "6");
                cursor.close();
                // 通知handler掃描完成
                mHandler.sendEmptyMessage(DATA_LOADED);
            }
        }.start();

    }

FolderBean

public class FolderBean {

    private String dir;
    private String name;
    private String firstImgPath;
    private int count;

    public String getDir() {
        return dir;
    }

    public void setDir(String dir) {
        this.dir = dir;
        int lastIndexOf = this.dir.lastIndexOf("/") + 1;
        this.name = this.dir.substring(lastIndexOf);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getFirstImgPath() {
        return firstImgPath;
    }

    public void setFirstImgPath(String firstImgPath) {
        this.firstImgPath = firstImgPath;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    @Override
    public String toString() {
        return dir;
    }

}

第四步:把圖片信息顯示到布局:

public class ImageAdapter extends BaseAdapter {
    private static Set<String> mSelectImg = new HashSet<String>();
    private Context context;
    private List<String> mImgPahts;
    private String mDirPath;
    private LayoutInflater mInflater;
    private Handler handler;

    public ImageAdapter(Context context, List<String> mDatas, String dirPath, Handler handler) {
        this.context = context;
        this.mImgPahts = mDatas;
        this.mDirPath = dirPath;
        mInflater = LayoutInflater.from(context);
        this.handler = handler;
    }

    @Override
    public int getCount() {
        return mImgPahts.size();
    }

    @Override
    public Object getItem(int position) {
        return mImgPahts.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        final ViewHolder viewHolder;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.item_griadview, null);
            viewHolder = new ViewHolder();
            viewHolder.mImg = (ImageView) convertView.findViewById(R.id.id_item_image);
            viewHolder.mSelect = (ImageButton) convertView.findViewById(R.id.id_item_select);

            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        if (mImgPahts.get(position).equals("null###")) {
            viewHolder.mImg.setImageResource(R.drawable.photograph_icon);
            viewHolder.mImg.setScaleType(ScaleType.CENTER);
            viewHolder.mSelect.setVisibility(View.GONE);
            viewHolder.mImg.setColorFilter(null);
            viewHolder.mImg.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    handler.sendEmptyMessage(4);
                }
            });
        } else {
            viewHolder.mImg.setImageResource(R.drawable.not_loaded_icon);
            viewHolder.mImg.setScaleType(ScaleType.FIT_XY);
            viewHolder.mSelect.setVisibility(View.VISIBLE);
            viewHolder.mSelect.setImageResource(R.drawable.login_chexbox);
            viewHolder.mImg.setColorFilter(null);
            if (mDirPath.equals("")) {
                ImageLoader.getInstance(3, com.awang.imageloader.util.ImageLoader.Type.FIFO)
                        .LoadImage(mImgPahts.get(position), viewHolder.mImg);
            } else {
                ImageLoader.getInstance(3, com.awang.imageloader.util.ImageLoader.Type.FIFO)
                        .LoadImage(mDirPath + "/" + mImgPahts.get(position), viewHolder.mImg);
            }
            final String filePath = mDirPath.equals("") ? mImgPahts.get(position)
                    : mDirPath + "/" + mImgPahts.get(position);
            viewHolder.mImg.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    ImgaeSelectActivity.getCount();
                    if (ImgaeSelectActivity.contains(filePath)) {
                        ImgaeSelectActivity.removeSelectImg(filePath);
                        handler.sendEmptyMessage(2);
                        viewHolder.mSelect.setImageResource(R.drawable.login_chexbox);
                        viewHolder.mImg.setColorFilter(null);
                    } else {
                        if (ImgaeSelectActivity.getCount() >= 3) {
                            handler.sendEmptyMessage(1);
                        } else {
                            ImgaeSelectActivity.addSelectImg(filePath);
                            handler.sendEmptyMessage(1);
                            viewHolder.mSelect.setImageResource(R.drawable.login_chexbox_s);
                            viewHolder.mImg.setColorFilter(Color.parseColor("#77000000"));
                        }
                    }
                    // notifyDataSetChanged();
                }
            });
            if (ImgaeSelectActivity.contains(filePath)) {
                viewHolder.mSelect.setImageResource(R.drawable.login_chexbox_s);
                viewHolder.mImg.setColorFilter(Color.parseColor("#77000000"));
            }
        }
        return convertView;
    }

    public class ViewHolder {

        ImageView mImg;
        ImageButton mSelect;
    }

}

第五步:展現文件夾的PopupWindow:
要實現的效果是,點擊底部的布局彈出我們的文件夾選擇框雳攘,并且我們彈出框后面的Activity要變暗带兜;

public class ListImageDirPopupWindow extends PopupWindow {

    private int width;
    private int heightl;
    private View mContentView;
    private ListView mListView;
    private List<FolderBean> mDatas;
    private ListDirAdapter mAdapter;
    public interface setOnDirSelectListerener {
        void onSelected(FolderBean folderBean, int position);
    }

    private static int currentSelect = 0;
    public setOnDirSelectListerener dirSelectListerener;

    public ListImageDirPopupWindow(Context context, List<FolderBean> mDatas) {
        calWidthAndHeight(context);
        mContentView = LayoutInflater.from(context).inflate(R.layout.popuwid, null);

        this.mDatas = mDatas;
        setContentView(mContentView);
        setWidth(width);
        setHeight(heightl);
        setFocusable(true);
        setTouchable(true);
        setOutsideTouchable(true);
        setBackgroundDrawable(new BitmapDrawable());
        setTouchInterceptor(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
                    dismiss();
                    return true;
                }
                return false;
            }
        });
        initViews(context);
        initEvent();
    }

    private void initViews(Context context) {
        mListView = (ListView) mContentView.findViewById(R.id.id_list_dir);
        mAdapter = new ListDirAdapter(context, mDatas);
        mListView.setAdapter(mAdapter);
    }

    public void initEvent() {
        mListView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (dirSelectListerener != null) {
                    dirSelectListerener.onSelected(mDatas.get(position), position);
                    currentSelect = position;
                    mAdapter.notifyDataSetChanged();
                }

            }
        });
    }

    public void setDirSelectListerener(setOnDirSelectListerener dirSelectListerener) {
        this.dirSelectListerener = dirSelectListerener;
    }

    /**
     * 計算popupWindow的寬度和高度
     */
    private void calWidthAndHeight(Context context) {
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(outMetrics);
        width = outMetrics.widthPixels;
        heightl = (int) (outMetrics.heightPixels * 0.7);
    }

    private class ListDirAdapter extends ArrayAdapter<FolderBean> {

        private LayoutInflater mInflater;
        private List<FolderBean> mDatas;

        public ListDirAdapter(Context context, List<FolderBean> mDatas) {
            super(context, 0, mDatas);
            mInflater = LayoutInflater.from(context);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder = null;
            if (convertView == null) {
                viewHolder = new ViewHolder();
                convertView = mInflater.inflate(R.layout.item_popuwid, parent, false);
                viewHolder.mImg = (ImageView) convertView.findViewById(R.id.id_dir_item_image);
                viewHolder.setSel = (ImageView) convertView.findViewById(R.id.set_sel);
                viewHolder.mDirName = (TextView) convertView.findViewById(R.id.id_dir_item_name);
                viewHolder.mDirCount = (TextView) convertView.findViewById(R.id.id_dir_item_count);
                convertView.setTag(viewHolder);

            } else {

                viewHolder = (ViewHolder) convertView.getTag();
            }

            FolderBean bean = getItem(position);

            // 重置
            viewHolder.mImg.setImageResource(R.drawable.not_loaded_icon);
            ImageLoader.getInstance().LoadImage(bean.getFirstImgPath(), viewHolder.mImg);
            viewHolder.mDirName.setText(bean.getName());
            if (bean.getCount() == 0) {
                viewHolder.mDirCount.setText("");
            } else {

                viewHolder.mDirCount.setText("" + bean.getCount());
            }
            if (position == currentSelect) {
                viewHolder.setSel.setVisibility(View.VISIBLE);
            } else {
                viewHolder.setSel.setVisibility(View.GONE);
            }

            return convertView;
        }

        private class ViewHolder {
            ImageView setSel;
            ImageView mImg;
            TextView mDirName;
            TextView mDirCount;
        }

    }
    public static void setCurrent(int position){
        
    }

}

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    android:orientation="vertical" >
    
    <ListView 
        android:paddingLeft="10dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/id_list_dir"
        android:divider="#eee39d"
        android:dividerHeight="1px"></ListView>

</LinearLayout>

主要的代碼就這么多,

總結:本項目的主要代碼基本已經貼出來了,感興趣的朋友也可以去下載本人的項目,下載地址也已經貼了出來,最后,如有不對的地方,請多多指教.

項目下載地址:

http://download.csdn.net/download/waa_0618/9748835
[1]: http://math.stackexchange.com/
[2]: https://github.com/jmcmanus/pagedown-extra "Pagedown Extra"
[3]: http://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference
[4]: http://bramp.github.io/js-sequence-diagrams/
[5]: http://adrai.github.io/flowchart.js/
[6]: https://github.com/benweet/stackedit

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末吨灭,一起剝皮案震驚了整個濱河市刚照,隨后出現的幾起案子,更是在濱河造成了極大的恐慌喧兄,老刑警劉巖无畔,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異吠冤,居然都是意外死亡浑彰,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門拯辙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來郭变,“玉大人颜价,你說我怎么就攤上這事∷弑簦” “怎么了周伦?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長未荒。 經常有香客問我专挪,道長,這世上最難降的妖魔是什么片排? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任寨腔,我火速辦了婚禮,結果婚禮上划纽,老公的妹妹穿的比我還像新娘脆侮。我一直安慰自己,他們只是感情好勇劣,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布靖避。 她就那樣靜靜地躺著,像睡著了一般比默。 火紅的嫁衣襯著肌膚如雪幻捏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天命咐,我揣著相機與錄音篡九,去河邊找鬼。 笑死醋奠,一個胖子當著我的面吹牛榛臼,可吹牛的內容都是我干的。 我是一名探鬼主播窜司,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼沛善,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了塞祈?” 一聲冷哼從身側響起金刁,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎议薪,沒想到半個月后尤蛮,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡斯议,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年产捞,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捅位。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡轧葛,死狀恐怖搂抒,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情尿扯,我是刑警寧澤求晶,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站衷笋,受9級特大地震影響芳杏,放射性物質發(fā)生泄漏。R本人自食惡果不足惜辟宗,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一爵赵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泊脐,春花似錦空幻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至缩挑,卻和暖如春但两,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背供置。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工谨湘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芥丧。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓紧阔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親续担。 傳聞我的和親對象是個殘疾皇子寓辱,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,504評論 25 707
  • 接上一篇:Android仿微信圖片選擇器(一) 上一篇介紹了發(fā)表界面的編寫及數據的處理,這一篇主要介紹圖片選擇界面...
    逸先森閱讀 1,091評論 0 1
  • ¥開啟¥ 【iAPP實現進入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程赤拒,因...
    小菜c閱讀 6,358評論 0 17
  • 誰家玉面少年郎, 仗劍天涯意氣揚诱鞠。 陌路紅塵君一笑挎挖, 輕風不語美人香。 少年醒來的時候航夺,首先看到的是茅屋房梁上一對...
    酉時七若閱讀 399評論 0 2
  • SQL>shutdown immediate;cmd > rman nocatalog RMAN > connec...
    王滕輝閱讀 401評論 0 0