Android上傳圖片之調用系統拍照和從相簿選擇圖片

來源:互聯網
上載者:User

Android上傳圖片之調用系統拍照和從相簿選擇圖片
Android上傳圖片之調用系統拍照和從相簿選擇圖片

前言:
萬丈高樓平底起,萬事起於微末。不知不覺距離上篇博文已近四個月,2015年12月17日下午發了第一篇博文,現在是2016年4月6日。時間間隔長的過分啊,我自己都看不下去了。原因呢?當然是自己的原因,其實是有很多時間來些部落格的,但是這些時間都花在DOTA上了(還是太年輕啊)。請原諒我的過錯…….
一、概述:
現在幾乎應用都會用到上傳圖片的功能,而要上傳圖片,首先得選擇圖片,本文不針對如何上傳圖片到伺服器(每個項目與伺服器互動的方式不同,因此不寫上傳圖片到伺服器相關代碼),只是對選擇圖片做簡單的介紹,沒有涉及到對圖片的圓角處理與剪裁。本文主要涉及以下幾個簡單的知識點:

簡單的調用系統拍照和系統相簿選擇圖片 通過GridView實現動態添加圖片的效果 Adapter使用的小技巧 Fragment中調用系統拍照該怎麼擷取資料(介面回調)

二、實現:
我們先來看項目目錄:

一個Adapter、兩個Activity,一個Fragment、一個工具類,一目瞭然。有人在這裡有疑問了,為什麼是兩個Activity?不是三個嗎?沒錯,理論上ChooseActivityChooseFragmentActivityBaseActivity加起來是三個,不過在這裡BaseActivity是類比實際項目抽離Activity中公用的代碼,不做為視圖,所以我不把BaseActivity算進去。
ChooseActivity是類比Activity中調用系統拍照和系統相簿選擇圖片,ChooseFragmentActivity中放入ChooseFragment類比Fragment中調用系統拍照和系統相簿選擇圖片(在這裡我定死了一個Fragment類比項目實際情況,實際情況一個Activity中會有多個Fragment),ImageUtils做一些簡單的圖片處理。SelectPicPopupWindow一個簡單的PopupWindow,UploadImageAdapter動態選擇圖片上傳的適配器。
先來點吧:


圖中展示的效果:點擊預設圖片彈出PopupWindow讓使用者選擇拍照還是從相簿選擇圖片(模擬器中不便使用拍照功能,本人在幾台手機上試過沒有問題,請到真機上測試),選擇好圖片後已選擇好的圖片可長按刪除,這裡控制了最多選擇6張圖片。

簡單的調用系統拍照和系統相簿選擇圖片
我們先來看是怎麼調用系統拍照和從相簿選擇圖片的:
申明組件與變數:

/**     * 選擇圖片的返回碼     */    public final static int SELECT_IMAGE_RESULT_CODE = 200;     /**     * 當前選擇的圖片的路徑     */    public String mImagePath;    /**     * 自訂的PopupWindow     */    private SelectPicPopupWindow menuWindow;

彈出PopupWindow:

    /**     * 拍照或從圖庫選擇圖片(PopupWindow形式)     */    public void showPicturePopupWindow(){        menuWindow = new SelectPicPopupWindow(this, new OnClickListener() {            @Override            public void onClick(View v) {                // 隱藏快顯視窗                menuWindow.dismiss();                switch (v.getId()) {                case R.id.takePhotoBtn:// 拍照                    takePhoto();                    break;                case R.id.pickPhotoBtn:// 相簿選擇圖片                    pickPhoto();                    break;                case R.id.cancelBtn:// 取消                    break;                default:                    break;                }            }        });          menuWindow.showAtLocation(findViewById(R.id.choose_layout), Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL, 0, 0);    }   

其中最重要的就是拍照相關的takephoto方法了了,部分機型拍完照後沒有資料返回,只能通過指定拍完照獲得圖片的儲存路徑來解決這個問題了。注釋寫的很詳細,這裡不再多解釋了。但是注意一點指定路徑的時候可能會出現拍完照後無法點確定返回,有的手機甚至會點擊後掛掉,這個時候會報不是有效路徑的錯誤。我遇到錯誤是在擷取到的與應用相關聯的路徑後面再建立一個檔案/xxxx,至於為什麼不行,我也不知道原理。

private void takePhoto() {        // 執行拍照前,應該先判斷SD卡是否存在        String SDState = Environment.getExternalStorageState();        if (SDState.equals(Environment.MEDIA_MOUNTED)) {            /**             * 通過指定圖片儲存路徑,解決部分機型onActivityResult回調 data返回為null的情況             */            //擷取與應用相關聯的路徑            String imageFilePath = getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath();            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA);             //根據目前時間產生圖片的名稱            String timestamp = "/"+formatter.format(new Date())+".jpg";             File imageFile = new File(imageFilePath,timestamp);// 通過路徑建立儲存檔案            mImagePath = imageFile.getAbsolutePath();            Uri imageFileUri = Uri.fromFile(imageFile);// 擷取檔案的Uri            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);            intent.putExtra(MediaStore.EXTRA_OUTPUT,imageFileUri);// 告訴相機拍攝完畢輸出圖片到指定的Uri            startActivityForResult(intent, SELECT_IMAGE_RESULT_CODE);        } else {            Toast.makeText(this, "記憶卡不存在!", Toast.LENGTH_LONG).show();        }    }

通過GridView實現動態添加圖片的效果
其實你們更關心GridView動態增加item,item刪除等效果:

申明組件和變數:

/**     * 需要上傳的圖片路徑  控制預設圖片在最後面需要用LinkedList     */    private LinkedList dataList = new LinkedList();    /**     * 圖片上傳GridView     */    private GridView uploadGridView;    /**     * 圖片上傳Adapter     */    private UploadImageAdapter adapter;

初始化GridView和Adapter:

    uploadGridView = (GridView) findViewById(R.id.grid_upload_pictures);        dataList.addLast(null);// 初始化第一個添加按鈕資料        adapter = new UploadImageAdapter(this, dataList);        uploadGridView.setAdapter(adapter);        uploadGridView.setOnItemClickListener(mItemClick);        uploadGridView.setOnItemLongClickListener(mItemLongClick);

GridView的item點擊監聽和長按監聽:

/**     * 上傳圖片GridView Item單擊監聽     */    private OnItemClickListener mItemClick = new OnItemClickListener(){        @Override        public void onItemClick(AdapterView parent, View view, int position,                long id) {            if(parent.getItemAtPosition(position) == null){ // 添加圖片                //showPictureDailog();//Dialog形式                showPicturePopupWindow();//PopupWindow形式            }        }    };    /**     * 上傳圖片GridView Item長按監聽     */    private OnItemLongClickListener mItemLongClick = new OnItemLongClickListener(){        @Override        public boolean onItemLongClick(AdapterView parent, View view,                int position, long id) {            if(parent.getItemAtPosition(position) != null){ // 長按刪除                dataList.remove(parent.getItemAtPosition(position));                adapter.update(dataList); // 重新整理圖片            }            return true;        }    };

對於onActivityResult的回調如下:

@Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        super.onActivityResult(requestCode, resultCode, data);        if(requestCode == SELECT_IMAGE_RESULT_CODE && resultCode == RESULT_OK){            String imagePath = "";            if(data != null && data.getData() != null){//有資料返回直接使用返回的圖片地址                imagePath = ImageUtils.getFilePathByFileUri(this, data.getData());            }else{//無資料使用指定的圖片路徑                imagePath = mImagePath;            }            dataList.addFirst(imagePath);//每次資料放到首位            adapter.update(dataList); // 重新整理圖片        }    }

Adapter使用的小技巧
我們可以看到GirdView點擊監聽和長按監聽都用到了

if(parent.getItemAtPosition(position) != null){            //相關邏輯}

判斷語句,為什麼用parent.getItemAtPosition(position) 而不用dataList .get(position)呢?個人認為使用適配器最好將資料來源隔離出來,即除了在Adapter傳入資料或者Adapter更新資料,其他情況不再使用資料來源,避免資料不同步造成一些問題。我們再來看一下Adapter的代碼:

/** * 多圖上傳,動態添加圖片適配器 */public class UploadImageAdapter extends BaseAdapter {    private LinkedList imagePathList;    private Context context;    private boolean isAddData = true;    /**     * 控制最多上傳的圖片數量     */    private int imageNumber = 6;    public UploadImageAdapter(Context context, LinkedList imagePath) {        this.context = context;        this.imagePathList = imagePath;    }    public void update(LinkedList imagePathList){        this.imagePathList = imagePathList;        //這裡控制選擇的圖片放到前面,預設的圖片放到最後面,        if(isAddData){            //集合中的總數量等於上傳圖片的數量加上預設的圖片不能大於imageNumber + 1            if(imagePathList.size() == imageNumber + 1){                //移除預設的圖片                imagePathList.removeLast();                isAddData = false;            }        }else{            //添加預設的圖片            imagePathList.addLast(null);            isAddData = true;        }        notifyDataSetChanged();    }    @Override    public int getCount() {        return imagePathList == null ? 0 : imagePathList.size();    }    @Override    public Object getItem(int position) {        return imagePathList == null ? null : imagePathList.get(position);    }    @Override    public long getItemId(int position) {        return  position;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        ImageView iv_image;        if (convertView == null) {//建立ImageView            iv_image = new ImageView(context);            iv_image.setLayoutParams(new AbsListView.LayoutParams(ImageUtils.getWidth(context) / 3 - 5, ImageUtils.getWidth(context) / 3 - 5) );              iv_image.setScaleType(ImageButton.ScaleType.CENTER_CROP);            convertView = iv_image;        }else{            iv_image = (ImageView) convertView;        }        if(getItem(position) == null ){//圖片地址為空白時設定預設圖片            iv_image.setImageResource(R.drawable.upload);        }else{            //擷取圖片縮圖,避免OOM            Bitmap bitmap = ImageUtils.getImageThumbnail((String)getItem(position), ImageUtils.getWidth(context) / 3 - 5, ImageUtils.getWidth(context) / 3 - 5);            iv_image.setImageBitmap(bitmap);        }        return convertView;    }

在這裡我對getCount()、getItem()方法都做了非空的判斷,個人認為能避免null 指標異常就要避免,當然這樣做也是為了在getView中直接使用getItem(position)方法,而不是取用dataList.get(position)擷取當前item的對應的資料,原因在GridView點擊和長按事件中有提到過。邏輯比較簡單,不做過多的介紹。

Fragment與Activity之間通過介面傳遞資料
我覺得最重要的就是Fragment與Activity之間怎麼傳遞資料,在這裡我採取了介面回調來實現資料傳遞。
首先在BaseActivity中定義一個介面:

/**     * 選擇圖片的返回碼     */    public final static int SELECT_IMAGE_RESULT_CODE = 200;     /**     * 當前選擇的圖片的路徑     */    public String mImagePath;    /**     * 自訂的PopupWindow     */    private SelectPicPopupWindow menuWindow;    /**     * Fragment回調介面     */    public OnFragmentResult mOnFragmentResult;    public void setOnFragmentResult(OnFragmentResult onFragmentResult){        mOnFragmentResult = onFragmentResult;    }    /**     * 回調資料給Fragment的介面     */    public interface OnFragmentResult{        void onResult(String mImagePath);    }

然後我們來看看是怎麼使用的吧:
因為ChooseFragmentActivity繼承自BaseActivity,所以直接mOnFragmentResult

@Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        super.onActivityResult(requestCode, resultCode, data);        String imagePath = "";        if(requestCode == SELECT_IMAGE_RESULT_CODE && resultCode== RESULT_OK){            if(data != null && data.getData() != null){                imagePath = ImageUtils.getFilePathByFileUri(this, data.getData());            }else{                imagePath = mImagePath;            }            mOnFragmentResult.onResult(imagePath);        }    }

Fragment中:

//設定監聽      ((BaseActivity)getActivity()).setOnFragmentResult(mOnFragmentResult);private OnFragmentResult mOnFragmentResult = new OnFragmentResult() {        @Override        public void onResult(String mImagePath) {            dataList.addFirst(mImagePath);            adapter.update(dataList); // 重新整理圖片        }    };      

而在Fragment中對GridView點擊、長按事件操作與Activity中大同小異,主要是Context的擷取。

/**     * 上傳圖片GridView Item單擊監聽     */    private OnItemClickListener mItemClick = new OnItemClickListener(){        @Override        public void onItemClick(AdapterView parent, View view, int position,                long id) {            if(parent.getItemAtPosition(position) == null){ // 添加圖片                //((BaseActivity)getActivity()).showPictureDailog();//Dialog形式                ((BaseActivity)getActivity()).showPicturePopupWindow();//PopupWindow形式            }        }    };

最關鍵的地方就是(BaseActivity)getActivity()這步操作,這樣能在Fragment中拿到BaseActivity中的方法和屬性。這種操作在很多情景使用會帶來很大的便利。

好了,本片文章就進入尾聲了……

Think great thoughts and you will be great.

剛試了,上傳不了資源,等能上傳了再奉上源碼~

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.