当前位置: 代码迷 >> Android >> Android上传图片之调用系统拍照跟从相册选择图片
  详细解决方案

Android上传图片之调用系统拍照跟从相册选择图片

热度:520   发布时间:2016-04-24 11:13:22.0
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<String> dataList = new LinkedList<String>();    /**     * 图片上传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<String> imagePathList;    private Context context;    private boolean isAddData = true;    /**     * 控制最多上传的图片数量     */    private int imageNumber = 6;    public UploadImageAdapter(Context context, LinkedList<String> imagePath) {        this.context = context;        this.imagePathList = imagePath;    }    public void update(LinkedList<String> 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()方法都做了非空的判断,个人认为能避免空指针异常就要避免,当然这样做也是为了在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.

上源码

8楼Lucy_cc昨天 17:12
空空干巴跌
Re: u0100655929小时前
回复Lucy_ccn我七,么么哒
7楼u010065592昨天 17:03
天空没想到你是这种人!居然孜孜不倦的提高技术,我等却还在荒废时光。
6楼kkkkkqqqqq55555昨天 17:03
老衲学习了
5楼u013165570昨天 17:01
收到红包才过来的
4楼ieee802x昨天 17:01
博主加油,未来是你们的!
3楼dgs960825昨天 16:57
七七,爱你么么哒~
2楼u013466454昨天 16:53
天空没想到你是这种人!居然孜孜不倦的提高技术
Re: u010065592昨天 16:54
回复u013466454 高叔,没想到你也是这种人。
1楼dgs960825昨天 16:49
大侠,请留下姓名!
  相关解决方案