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.

刚试了,上传不了资源,等能上传了再奉上源码~

时间: 2024-12-28 15:38:00

Android上传图片之调用系统拍照和从相册选择图片的相关文章

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

Android上传图片之调用系统拍照和从相冊选择图片 本篇文章已授权微信公众号 guolin_blog (郭霖)独家公布 前言: 万丈高楼平底起,万事起于微末.不知不觉距离上篇博文已近四个月,2015年12月17日下午发了第一篇博文.如今是2016年4月6日.时间间隔长的过分啊,我自己都看不下去了. 原因呢?当然是自己的原因.事实上是有非常多时间来些博客的,可是这些时间都花在DOTA上了(还是太年轻啊).请原谅我的过错--. 一.概述: 如今差点儿应用都会用到上传图片的功能,而要上传图片,首先得

ng-cordova 手机拍照或从相册选择图片

1.需求描述 实现一个调用摄像头拍照,或者直接打开本地图库选择照片,然后替换App中图片的功能 2.准备 1) 安装ng-cordova 进入到ionic工程目录,使用bower工具安装, bower install ngCordova 然后将ng-cordova.js 或者 ng-cordova.min.js 添加到index.html 中的 cordova.js 引用之前 ... <script src="lib/ngCordova/dist/ng-cordova.js"&g

Android拍照,相册选择图片以及Android6.0权限管理

概述 在android开发过程中,拍照或者从相册中选择图片是很常见的功能.下面要说得这个案例比较简单,用户点击按钮选择拍照或者打开相册选择图片,然后将选中的图片显示在手机上.android6.0后,推出了动态权限管理.以往我们将涉及到的权限全部写在清单文件中,只要用户安装了该程序,程序在运行过程中都会获得相应权限.android6.0后,对于一些特别敏感的权限,开发者必须在程序中进行声明.拍照和从相册选择图片都是涉及到用户隐私的敏感权限,必须在程序中进行声明. 大概的流程 创建布局文件,这里不多

HTML5 Plus 拍照或者相册选择图片上传

利用HTML Plus的Camera.GalleryIO.Storage和Uploader来实现手机APP拍照或者从相册选择图片上传.Camera模块管理设备的摄像头,可用于拍照.摄像操作,通过plus.camera获取摄像头管理对象.Gallery模块管理系统相册,支持从相册中选择图片或视频文件.保存图片或视频文件到相册等功能.通过plus.gallery获取相册管理对象.IO模块管理本地文件系统,用于对文件系统的目录浏览.文件的读取.文件的写入等操作.通过plus.io可获取文件系统管理对象

Android 实例讲解添加本地图片和调用系统拍照图片

在项目的开发过程我们离不开图片,而有时候需要调用本地的图片,有时候需要调用拍照图片.同时实现拍照的方法有两种,一种是调用系统拍照功能,另一种是自定义拍照功能.而本博文目前只讲解第一种方法,第二种方法后期在加以讲解. 添加本地图片和调用系统拍照图片主要是通过调用acitivity跳转startActivityForResult(Intent intent, int requestCode)方法和activity返回结果onActivityResult(int requestCode, int re

android调用系统拍照那些事

小时候都知道每天写日记是个好习惯,慢慢发现自己忘记了这些习惯,好久没有给这干枯的博客添加一点新意了,这回也冒出个小芽来刷新一下博客. 今天写的一点也不复杂,就是回顾一些老的知识而已,也算是记一个笔记,好了闲话不多说了,开始今天的主题吧. 关于拍照,这里不是自己实现拍照,是调用系统拍照,很简单的,可是有些时候我也遇到一个问题,就是我没有主动压缩,系统却自动帮我压缩了,可是我需要这些高清的图片,解决方式网上也有说,但是我做的是自己的笔记,所以也不在乎赘余,最起码我是经过验证后,才写我笔记的. 下面是

[Android Pro] 调用系统相机和图库,裁剪图片

private static final int PHOTO_REQUEST_TAKEPHOTO = 1;// 拍照 private static final int PHOTO_REQUEST_GALLERY = 2;// 从相册中选择 private static final int PHOTO_REQUEST_CUT = 3;// 结果 private File tempFile = new File(Environment.getExternalStorageDirectory(), g

Android拍照或从图库选择图片并裁剪

今天看<第一行代码>上面关于拍照和从相册选取图片那一部分,发现始终出不来效果,所以搜索其他资料学习一下相关知识,写一个简单的Demo. 一. 拍照选择图片 1.使用隐式Intent启动相机 //构建隐式Intent Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //调用系统相机 startActivityForResult(intent, 1); 2.处理相机拍照返回的结果 //用户点击了取消 if(data == n

Android 拍照或者从相册获取图片的实现

我们常常会用到上传头像,或者发帖子的时候选择本地图片上传的功能.这个很常见 今天因为app的需求我研究了下.现在分享下. 其实不论是通过拍照还是从相册选取都会用到Intent 这是系统提供给我们用来调用系统方法的好用工具! 首先,需要设计下我们想怎么调用系统的拍照或者选取图片的方法 我们可以点击头像或者一个按钮然后弹出一个对话框,让用户自己 选择是拍照还是选择图片(如下图) . 那这个对话框怎么写呢.通过AlertDialog来实现(我们就给这个方法起名叫dialog): //对头像操作 pri