最近需要做手机拍照【两种方式:调用系统相机(博客最后);自己写照相机布局】,预览,上传功能。特地研究了下android的手机拍照。

参考地址:

http://blog.csdn.net/cfwdl/article/details/5746708

http://mjbb.iteye.com/blog/1018006

http://blog.csdn.net/hellogv/article/details/5962494

1、上传文件功能网上很多讲的,只要细心点,按照格式来写发送的数据,都是没有问题的。

2、预览使用Gallery和ImageSwitcher就行,我做的很简单(参考代码)。

----------------------------------------------------------------------------------------------------------------------

修改内容

1、照相功能使用系统自带照相机(自己写的照相机属性设置根据不同照相机会有问题,所以舍弃)

2、预览功能不再使用Gallery+ImageSwitcher;实用性不强,并且显示慢且卡。改用异步加载

3、上传图片时,对图片进行压缩,增加上传速度。

4、长按gridView进入编辑模式,批量删除图片。参考http://aokunsang.iteye.com/blog/1668902;

5、今天又做修改,之前写的压缩图片方法的竟然会变形(没有测试大的图片),修改后不会变形了。

以此,希望能做到最好的用户体验。

附上流程图:
Android拍照,上传,预览综合【修改】
拍照功能: 【预览尺寸有知道的朋友留言告知。】
 * 拍照 * @author Administrator */public class TakePhotoAct extends Activity implements SurfaceHolder.Callback{private static String imgPath = Environment.getExternalStorageDirectory().getPath() + "/"+Const.imageDir;private SurfaceView surfaceView;   //相机画布private SurfaceHolder surfaceHolder; private Button takePicView,exitView;private Camera mCamera;    //照相机@Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);//这里我在AndroidManifest.xml的activity中添加了android:theme="@android:style/Theme.NoTitleBar.Fullscreen"        /**         * 隐藏状态栏和标题栏                 this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);        requestWindowFeature(Window.FEATURE_NO_TITLE);        */        //setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);  //横屏        /**         * 获取Button并且设置事件监听         */        takePicView = (Button)this.findViewById(R.id.takepic);        takePicView.setOnClickListener(TakePicListener);        exitView = (Button)this.findViewById(R.id.exit);        exitView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {finish();}});        surfaceView = (SurfaceView)this.findViewById(R.id.surface_camera);        surfaceHolder = surfaceView.getHolder();        surfaceHolder.addCallback(this);        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);                checkSoftStage();  //首先检测SD卡是否存在    }/** * 检测手机是否存在SD卡,网络连接是否打开 */private void checkSoftStage(){if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){  //判断是否存在SD卡//String rootPath = Environment.getExternalStorageDirectory().getPath();  //获取SD卡的根目录File file = new File(imgPath);if(!file.exists()){file.mkdir();}}else{new AlertDialog.Builder(this).setMessage("检测到手机没有存储卡!请插入手机存储卡再开启本应用。").setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {finish();}}).show();}}/** * 点击拍照按钮,启动拍照 */private final OnClickListener TakePicListener = new OnClickListener(){@Overridepublic void onClick(View v) {mCamera.autoFocus(new AutoFoucus());  //自动对焦}};/** * 自动对焦后拍照 * @author aokunsang * @Date 2011-12-5 */private final class AutoFoucus implements AutoFocusCallback{@Overridepublic void onAutoFocus(boolean success, Camera camera) {if(success && mCamera!=null){mCamera.takePicture(mShutterCallback, null, mPictureCallback);}}}/** * 重点对象、 此处实例化了一个本界面的PictureCallback * 当用户拍完一张照片的时候触发,这时候对图片处理并保存操作。 *  */private final PictureCallback mPictureCallback = new PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera camera) {try {String fileName = System.currentTimeMillis()+".jpg";File file = new File(imgPath,fileName);Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length);BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));bm.compress(Bitmap.CompressFormat.JPEG, 60, bos);bos.flush();bos.close();Intent intent = new Intent(TakePhotoAct.this,PictureViewAct.class);intent.putExtra("imagePath", file.getPath());startActivity(intent);} catch (Exception e) {e.printStackTrace();}}};/** * 在相机快门关闭时候的回调接口,通过这个接口来通知用户快门关闭的事件, * 普通相机在快门关闭的时候都会发出响声,根据需要可以在该回调接口中定义各种动作, 例如:使设备震动 */  private final ShutterCallback mShutterCallback = new ShutterCallback() {  public void onShutter() {  Log.d("ShutterCallback", "...onShutter...");  }  };@Override/** * 初始化相机参数,比如相机的参数: 像素, 大小,格式 */public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {Camera.Parameters param = mCamera.getParameters();/** * 设置拍照图片格式 */param.setPictureFormat(PixelFormat.JPEG);/** * 设置预览尺寸【这里需要注意:预览尺寸有些数字正确,有些会报错,不清楚为啥】 *///param.setPreviewSize(320, 240);/** * 设置图片大小 */param.setPictureSize(Const.width, Const.height);mCamera.setParameters(param);/** * 开始预览 */mCamera.startPreview();}@Override/** * 打开相机,设置预览 */public void surfaceCreated(SurfaceHolder holder) {try {mCamera = Camera.open();  //打开摄像头mCamera.setPreviewDisplay(holder);} catch (IOException e) {mCamera.release();mCamera = null;}}@Override/** * 预览界面被关闭时,或者停止相机拍摄;释放相机资源 */public void surfaceDestroyed(SurfaceHolder holder) {mCamera.stopPreview();if(mCamera!=null) mCamera.release();mCamera = null;}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {if(keyCode == KeyEvent.KEYCODE_CAMERA){   //按下相机实体按键,启动本程序照相功能mCamera.autoFocus(new AutoFoucus());  //自动对焦return true;}else{return false;}}}
xml:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    ><SurfaceView android:id="@+id/surface_camera"android:layout_width="fill_parent"android:layout_height="fill_parent"android:layout_weight="1"/><LinearLayout android:orientation="horizontal"android:layout_width="fill_parent"android:layout_height="wrap_content"><Button android:text="拍照"android:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/takepic"/><Button android:text="退出"android:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/exit"/></LinearLayout></LinearLayout>
预览功能: /**
 * 图片预览 * @author: aokunsang * @date: 2012-8-1 */public class PictureScanAct extends Activity {private GridView gridView;private ImageAdapter imgAdapter;private List<String> fileNameList = new ArrayList<String>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.picturescan);gridView = (GridView)findViewById(R.id.picture_grid);imgAdapter = new ImageAdapter(this);gridView.setAdapter(imgAdapter);gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {String fileName = fileNameList.get(position);startActivity(new Intent(PictureScanAct.this, PictureViewAct.class).putExtra("flag","upload").putExtra("imagePath",fileName));}});//setProgressBarIndeterminateVisibility(true);Toast.makeText(this, "加载图片中....", Toast.LENGTH_LONG).show();    new AsyncLoadedImage().execute();}/** * 向适配器添加图片 * @param bitmap */private void addImage(Bitmap... loadImages){for(Bitmap loadImage:loadImages){imgAdapter.addPhoto(loadImage);}}/** * 释放内存 */protected void onDestroy() {        super.onDestroy();        final GridView grid = gridView;        final int count = grid.getChildCount();        ImageView v = null;        for (int i = 0; i < count; i++) {            v = (ImageView) grid.getChildAt(i);            ((BitmapDrawable) v.getDrawable()).setCallback(null);        }    }/** * 异步加载图片展示 * @author: aokunsang * @date: 2012-8-1 */ class AsyncLoadedImage extends AsyncTask<Object, Bitmap, Boolean> {@Overrideprotected Boolean doInBackground(Object... params) {File fileDir = new File(Const.imgPath);File[] files = fileDir.listFiles();boolean result = false;if(files!=null){for(File file:files){String fileName = file.getName();if (fileName.lastIndexOf(".") > 0  && fileName.substring(fileName.lastIndexOf(".") + 1,  fileName.length()).equals("jpg")){Bitmap bitmap;                Bitmap newBitmap; try {                        BitmapFactory.Options options = new BitmapFactory.Options();                        options.inSampleSize = 10;                        bitmap = BitmapFactory.decodeFile(file.getPath(), options);                        newBitmap = ThumbnailUtils.extractThumbnail(bitmap, 100, 100);                        bitmap.recycle();                        if (newBitmap != null) {                        fileNameList.add(file.getPath());                            publishProgress(newBitmap);                            result = true;                        }                    } catch (Exception e) {                        e.printStackTrace();                    }}}}return result;}@Override        public void onProgressUpdate(Bitmap... value) {addImage(value);        }@Override        protected void onPostExecute(Boolean result) {if(!result){showDialog(1);}        } } @Overrideprotected Dialog onCreateDialog(int id) { AlertDialog dialog = new AlertDialog.Builder(PictureScanAct.this).setTitle("温馨提示").setMessage("暂时还没有照片,请先采集照片!").setPositiveButton("确定", new DialogInterface.OnClickListener(){@Overridepublic void onClick(DialogInterface dialog, int which) {startActivity(new Intent(PictureScanAct.this,TakePhotoAct.class));}}).setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {finish();}}).show(); return dialog;}}

GridView适配器: public class ImageAdapter extends BaseAdapter {
private List<Bitmap> picList = new ArrayList<Bitmap>();private Context mContext;public ImageAdapter(Context mContext){this.mContext = mContext;}@Overridepublic int getCount() {return picList.size();}/* (non-Javadoc) * @see android.widget.Adapter#getItem(int) */@Overridepublic Object getItem(int position) {// TODO Auto-generated method stubreturn picList.get(position);}/** * 添加图片 * @param bitmap */public void addPhoto(Bitmap loadImage){picList.add(loadImage);notifyDataSetChanged();}/* (non-Javadoc) * @see android.widget.Adapter#getItemId(int) */@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ImageView imageView = null;if(convertView == null){imageView = new ImageView(mContext);imageView.setLayoutParams(new GridView.LayoutParams(110, 110));  imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);  imageView.setPadding(5,5,5,5);}else{imageView = (ImageView)convertView;}imageView.setImageBitmap(picList.get(position));return imageView;}}

图片预览界面: <?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/picture_grid"    android:layout_width="match_parent"    android:layout_height="match_parent" android:numColumns="4"android:verticalSpacing="5dip"android:horizontalSpacing="5dip"android:stretchMode="columnWidth"android:columnWidth="120dip"android:gravity="center"        ></GridView>

预览图片:


Android拍照,上传,预览综合【修改】
Android拍照,上传,预览综合【修改】

上传工具类:
import java.io.BufferedReader;import java.io.DataOutputStream;import java.io.FileInputStream;import java.io.InputStream;import java.io.InputStreamReader;import java.net.HttpURLConnection;import java.net.URL;import java.util.Map;import java.util.Map.Entry;import android.util.Log;import com.peacemap.photo.po.FileInfo;/** * POST上传文件 * @author aokunsang * @Date 2011-12-6 */public class PostFile {private static PostFile postFile = new PostFile();private final static String LINEND = "\r\n";private final static String BOUNDARY = "---------------------------7da2137580612"; //数据分隔线private final static String PREFIX = "--";private final static String MUTIPART_FORMDATA = "multipart/form-data";private final static String CHARSET = "utf-8";private final static String CONTENTTYPE = "application/octet-stream";private PostFile(){}public static PostFile getInstance(){return postFile;}/** * HTTP上传文件 * @param actionUrl  请求服务器的路径 * @param params     传递的表单内容 * @param files      多个文件信息 * @return */public String post(String actionUrl,Map<String,String> params,FileInfo[] files){try {URL url = new URL(actionUrl);HttpURLConnection urlConn = (HttpURLConnection)url.openConnection();urlConn.setDoOutput(true);   //允许输出urlConn.setDoInput(true);   //允许输入urlConn.setUseCaches(false);urlConn.setRequestMethod("POST");urlConn.setRequestProperty("connection", "Keep-Alive");urlConn.setRequestProperty("Charset", CHARSET);urlConn.setRequestProperty("Content-Type", MUTIPART_FORMDATA+";boundary="+BOUNDARY);DataOutputStream dos = new DataOutputStream(urlConn.getOutputStream());//构建表单数据String entryText = bulidFormText(params);Log.i("-------描述信息---------------", entryText);dos.write(entryText.getBytes());StringBuffer sb = new StringBuffer("");for(FileInfo file : files){sb.append(PREFIX).append(BOUNDARY).append(LINEND);sb.append("Content-Disposition: form-data; name=\""+file.getFileTextName()+"\"; filename=\""+file.getFile().getAbsolutePath()+"\""+LINEND);sb.append("Content-Type:"+CONTENTTYPE+";charset="+CHARSET+LINEND);sb.append(LINEND);dos.write(sb.toString().getBytes());InputStream is = new FileInputStream(file.getFile());byte[] buffer = new byte[1024];                int len = 0;                while ((len = is.read(buffer)) != -1) {                dos.write(buffer, 0, len);                }                is.close();                dos.write(LINEND.getBytes());}//请求的结束标志byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes();dos.write(end_data);dos.flush();//-----------------------------------  发送请求数据结束  ----------------------------    //----------------------------------   接收返回信息 ------------------------int code = urlConn.getResponseCode();if(code!=200){urlConn.disconnect();return "";}else{BufferedReader br = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));String result = "";String line = null;while((line = br.readLine())!=null){result += line;}br.close();urlConn.disconnect();return result;}} catch (Exception e) {Log.e("--------上传图片错误--------", e.getMessage());return null;}}/** * HTTP上传单个文件 * @param actionUrl  请求服务器的路径 * @param params     传递的表单内容 * @param files      单个文件信息 * @return */public String post(String actionUrl,Map<String,String> params,FileInfo fileInfo){return post(actionUrl, params, new FileInfo[]{fileInfo});}/** * 封装表单文本数据 * @param paramText * @return */private String bulidFormText(Map<String,String> paramText){if(paramText==null || paramText.isEmpty()) return "";StringBuffer sb = new StringBuffer("");for(Entry<String,String> entry : paramText.entrySet()){ sb.append(PREFIX).append(BOUNDARY).append(LINEND);sb.append("Content-Disposition:form-data;name=\""                    + entry.getKey() + "\"" + LINEND);//sb.append("Content-Type:text/plain;charset=" + CHARSET + LINEND);sb.append(LINEND);sb.append(entry.getValue());sb.append(LINEND);}return sb.toString();}/** * 封装文件文本数据 * @param files * @return */private String buildFromFile(FileInfo[] files){StringBuffer sb = new StringBuffer();for(FileInfo file : files){sb.append(PREFIX).append(BOUNDARY).append(LINEND);sb.append("Content-Disposition: form-data; name=\""+file.getFileTextName()+"\"; filename=\""+file.getFile().getAbsolutePath()+"\""+LINEND);sb.append("Content-Type:"+CONTENTTYPE+";charset="+CHARSET+LINEND);sb.append(LINEND);}return sb.toString();}}
上传图片时对图片进行压缩处理(压缩处理程序): /**
 * 压缩图片上传 * @param picPath * @return */private synchronized File compressPicture(String picPath){    int maxWidth = 640,maxHeight=480;  //设置新图片的大小            String fileName = picPath.substring(picPath.lastIndexOf("/"));    BitmapFactory.Options options = new BitmapFactory.Options();      options.inJustDecodeBounds = true;      Bitmap image = BitmapFactory.decodeFile(picPath, options);      double ratio = 1D;      if (maxWidth > 0 && maxHeight <= 0) {          // 限定宽度,高度不做限制          ratio = Math.ceil(options.outWidth / maxWidth);      } else if (maxHeight > 0 && maxWidth <= 0) {          // 限定高度,不限制宽度          ratio = Math.ceil(options.outHeight / maxHeight);      } else if (maxWidth > 0 && maxHeight > 0) {          // 高度和宽度都做了限制,这时候我们计算在这个限制内能容纳的最大的图片尺寸,不会使图片变形          double _widthRatio = Math.ceil(options.outWidth / maxWidth);          double _heightRatio = (double) Math.ceil(options.outHeight / maxHeight);          ratio = _widthRatio > _heightRatio ? _widthRatio : _heightRatio;      }      if (ratio > 1)        options.inSampleSize = (int) ratio;    options.inJustDecodeBounds = false;      options.inPreferredConfig = Bitmap.Config.RGB_565;      image = BitmapFactory.decodeFile(picPath, options);             //保存入sdCard             File file = new File(Const.thumbnailPath+fileName);             try {              FileOutputStream out = new FileOutputStream(file);              if(image.compress(Bitmap.CompressFormat.JPEG, 100, out)){                  out.flush();                  out.close();              }        } catch (Exception e) {             e.printStackTrace();            return new File(picPath);        }finally{        if(image!=null && !image.isRecycled()){        image.recycle();        }        }return file;}

-------------------------------------我是个华丽的分割线,哇哈哈------------------------- ----------- ----------- 做完这个拍照后,感觉功能太简单,比如:设置图片大小,白天夜晚照相等等一些系统照相机带的功能都没有,因此用在项目中感觉不炫。 然后就用了简单点的,直接调用系统照相机了。本来想着简单呢,后来也遇到点问题。 (1)根据Camera Activity返回的时候,会带一个名为data的 Bitmap对象,照片的缩略图(这个地方可以做各种修改,我没用到不说了),上代码
 @Override     public void onCreate(Bundle savedInstanceState) {   super.onCreate(savedInstanceState);   checkSoftStage();      try {   Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);   startActivityForResult(intent, TAKE_PICTURE);    } catch (Exception e) {   e.printStackTrace();   }} /** * 检测手机是否存在SD卡,网络连接是否打开 */private void checkSoftStage(){if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){  //判断是否存在SD卡File file = new File(imgPath);if(!file.exists()){file.mkdir();}}else{new AlertDialog.Builder(this).setMessage("检测到手机没有存储卡!请插入手机存储卡再开启本应用。").setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {finish();}}).show();}} protected void onActivityResult(int requestCode, int resultCode, Intent data)  {             if (resultCode == TAKE_PICTURE)      {              //  拍照Activity保存图像数据的key是data,返回的数据类型是Bitmap对象              Bitmap cameraBitmap = (Bitmap) data.getExtras().get("/sdcard/rtest.jpg");         //  在ImageView组件中显示拍摄的照片             image.setImageBitmap(cameraBitmap);                 // 做自己的业务操作。。。。    }            super.onActivityResult(requestCode, resultCode, data);          } 
(2) 以上代码在我的小米手机上测试时,出现问题了。 返回的name为data的Bitmap对象是个Null,我发现小米照完相片之后,他会先跳到一个预览的界面(系统自带的页面),所以得不到Bitmap对象了。 因此我就先保存照片以及其路径,然后在 onActivityResult中获取图片,做业务操作,代码如下:
   public void onCreate(Bundle savedInstanceState) {   super.onCreate(savedInstanceState);   checkSoftStage();      try {   Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);   startActivityForResult(intent, TAKE_PICTURE);    } catch (Exception e) {   e.printStackTrace();   }      try {   Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);   String fileName = System.currentTimeMillis()+".jpg";   newImgPath = imgPath + "/" + fileName;   Uri uri = Uri.fromFile(new File(imgPath,fileName));   intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);   startActivityForResult(intent, TAKE_PICTURE);   } catch (Exception e) {  e.printStackTrace();  }    } @Override    protected void onActivityResult(int requestCode,    int resultCode, Intent data) {       Log.i("--------图片路径---------", "------"+newImgPath+"---------");  //.....做一些业务操作} catch (Exception e) {e.printStackTrace();}super.onActivityResult(requestCode, resultCode, data);   }

更多相关文章

  1. Android记事本NotePad应用功能拓展(三)
  2. android 使用Activity类布局时怎样让图片居中
  3. android上传图片和参数(属性)到服务器
  4. Android多点触控---Matrix图片随意的放大缩小,拖动
  5. Android图片轮播控件---com.youth.banner.Banner
  6. Android上传图片到服务器,android-async-http上传图片到服务器

随机推荐

  1. Android(安卓)屏蔽蓝牙连接时的首次配对
  2. 图解Android应用程序构建原理
  3. activity 的属性android:taskAffinity和a
  4. Android可循环显示图像的Android(安卓)Ga
  5. android基于XMPP的消息推送机制
  6. 基于Android的 拍照上传 程序开发
  7. Android中Handler,Looper,MessageQueue和Th
  8. Android与电脑局域网共享之:Samba Server
  9. Android软件如何进行推广?十种推广方式可
  10. [Android] 缓存机制