最近需要做手机拍照【两种方式:调用系统相机(博客最后);自己写照相机布局】,预览,上传功能。特地研究了下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、今天又做修改,之前写的压缩图片方法的竟然会变形(没有测试大的图片),修改后不会变形了。
以此,希望能做到最好的用户体验。
![](http://dl.iteye.com/upload/attachment/601534/dd3ee1af-b97b-31bb-b14d-0c06c6830f29.jpg)
- * 拍照
- * @author Administrator
- */
- ublic 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() {
- @Override
- public 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() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- finish();
- }
- }).show();
- }
- }
- /**
- * 点击拍照按钮,启动拍照
- */
- private final OnClickListener TakePicListener = new OnClickListener(){
- @Override
- public void onClick(View v) {
- mCamera.autoFocus(new AutoFoucus()); //自动对焦
- }
- };
- /**
- * 自动对焦后拍照
- * @author aokunsang
- * @Date 2011-12-5
- */
- private final class AutoFoucus implements AutoFocusCallback{
- @Override
- public void onAutoFocus(boolean success, Camera camera) {
- if(success && mCamera!=null){
- mCamera.takePicture(mShutterCallback, null, mPictureCallback);
- }
- }
- }
- /**
- * 重点对象、 此处实例化了一个本界面的PictureCallback
- * 当用户拍完一张照片的时候触发,这时候对图片处理并保存操作。
- *
- */
- private final PictureCallback mPictureCallback = new PictureCallback() {
- @Override
- public 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;
- }
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if(keyCode == KeyEvent.KEYCODE_CAMERA){ //按下相机实体按键,启动本程序照相功能
- mCamera.autoFocus(new AutoFoucus()); //自动对焦
- return true;
- }else{
- return false;
- }
- }
- <?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>();
- @Override
- protected 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() {
- @Override
- public 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> {
- @Override
- protected 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);
- }
- }
- }
- @Override
- protected Dialog onCreateDialog(int id) {
- AlertDialog dialog = new AlertDialog.Builder(PictureScanAct.this).setTitle("温馨提示").setMessage("暂时还没有照片,请先采集照片!")
- .setPositiveButton("确定", new DialogInterface.OnClickListener(){
- @Override
- public void onClick(DialogInterface dialog, int which) {
- startActivity(new Intent(PictureScanAct.this,TakePhotoAct.class));
- }
- }).setNegativeButton("取消", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- finish();
- }
- }).show();
- return dialog;
- }
- }
- private List<Bitmap> picList = new ArrayList<Bitmap>();
- private Context mContext;
- public ImageAdapter(Context mContext){
- this.mContext = mContext;
- }
- @Override
- public int getCount() {
- return picList.size();
- }
- /* (non-Javadoc)
- * @see android.widget.Adapter#getItem(int)
- */
- @Override
- public Object getItem(int position) {
- // TODO Auto-generated method stub
- return picList.get(position);
- }
- /**
- * 添加图片
- * @param bitmap
- */
- public void addPhoto(Bitmap loadImage){
- picList.add(loadImage);
- notifyDataSetChanged();
- }
- /* (non-Javadoc)
- * @see android.widget.Adapter#getItemId(int)
- */
- @Override
- public long getItemId(int position) {
- // TODO Auto-generated method stub
- return position;
- }
- @Override
- public 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;
- }
- <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>
- 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;
- }
- @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() {
- @Override
- public 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);
- }
- 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);
- }