Android拍照,上传,预览综合
?
最近需要做手机拍照([1]、调用系统相机【博客最后】;[2]、自己写照相机布局),预览,上传功能。特地研究了下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就行,我做的很简单(参考代码)。
?
附上流程图:import java.io.BufferedOutputStream;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import android.app.Activity;import android.app.AlertDialog;import android.content.DialogInterface;import android.content.Intent;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.PixelFormat;import android.hardware.Camera;import android.hardware.Camera.AutoFocusCallback;import android.hardware.Camera.PictureCallback;import android.hardware.Camera.ShutterCallback;import android.os.Bundle;import android.os.Environment;import android.util.Log;import android.view.KeyEvent;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import com.peacemap.photo.po.Const;/** * 拍照 * @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>?预览功能:
import java.io.File;import java.util.ArrayList;import java.util.List;import android.app.Activity;import android.app.AlertDialog;import android.content.ComponentName;import android.content.DialogInterface;import android.content.Intent;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.drawable.BitmapDrawable;import android.os.Bundle;import android.os.Environment;import android.util.Log;import android.view.Menu;import android.view.MenuItem;import android.view.MotionEvent;import android.view.View;import android.view.View.OnTouchListener;import android.view.animation.AnimationUtils;import android.widget.AdapterView;import android.widget.Gallery;import android.widget.ImageSwitcher;import android.widget.ImageView;import android.widget.LinearLayout.LayoutParams;import android.widget.ViewSwitcher.ViewFactory;import com.peacemap.photo.adapter.ImageAdapter;import com.peacemap.photo.po.Const;/** * 图片浏览 * @author aokunsang * @Date 2011-12-6 */public class PhotoScanAct extends Activity implements ViewFactory{private static String imgPath = Environment.getExternalStorageDirectory().getPath() + "/"+Const.imageDir;private ImageSwitcher imageSwitcher; private Gallery gallery; private List<String> photoList; private int downX,upX; private String newFilePath;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.photoscan);photoList = readFileList();imageSwitcher = (ImageSwitcher)findViewById(R.id.switcher);imageSwitcher.setFactory(this);/* * 淡入淡出效果 */imageSwitcher.setInAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_in)); imageSwitcher.setOutAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_out));imageSwitcher.setOnTouchListener(touchListener);gallery = (Gallery)findViewById(R.id.gallery);gallery.setAdapter(new ImageAdapter(this, photoList));gallery.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {@Overridepublic void onItemSelected(AdapterView<?> arg0, View arg1,int position, long when) {newFilePath = photoList.get(position);Bitmap bm = BitmapFactory.decodeFile(photoList.get(position));BitmapDrawable bd = new BitmapDrawable(bm);imageSwitcher.setImageDrawable(bd);}@Overridepublic void onNothingSelected(AdapterView<?> arg0) {}});}/** * 注册一个触摸事件 */private OnTouchListener touchListener = new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) { if(event.getAction()==MotionEvent.ACTION_DOWN) { downX=(int) event.getX();//取得按下时的坐标 return true; } else if(event.getAction()==MotionEvent.ACTION_UP) { upX=(int) event.getX();//取得松开时的坐标 int index=0; if(upX-downX>100)//从左拖到右,即看前一张 { //如果是第一,则去到尾部 if(gallery.getSelectedItemPosition()==0) index=gallery.getCount()-1; else index=gallery.getSelectedItemPosition()-1; } else if(downX-upX>100)//从右拖到左,即看后一张 { //如果是最后,则去到第一 if(gallery.getSelectedItemPosition()==(gallery.getCount()-1)) index=0; else index=gallery.getSelectedItemPosition()+1; } //改变gallery图片所选,自动触发ImageSwitcher的setOnItemSelectedListener gallery.setSelection(index, true); return true; } return false; }};/** * 获取SD卡中的所有图片路径 * @return */private List<String> readFileList(){List<String> fileList = new ArrayList<String>();File fileDir = new File(imgPath);File[] files = fileDir.listFiles();if(files!=null){for(File file:files){String fileName = file.getName();if (fileName.lastIndexOf(".") > 0 && fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()).equals("jpg")){fileList.add(file.getPath());}}}return fileList;}@Overridepublic View makeView() {ImageView imageView = new ImageView(this);imageView.setScaleType(ImageView.ScaleType.CENTER);imageView.setLayoutParams(new ImageSwitcher.LayoutParams(//自适应图片大小 LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));return imageView;}//声明2个menu键按钮public final static int ITEM_1_ID = Menu.FIRST;public final static int ITEM_2_ID = Menu.FIRST+1;@Overridepublic boolean onCreateOptionsMenu(Menu menu) {super.onCreateOptionsMenu(menu);menu.add(0, ITEM_1_ID, 0, "我要上传");menu.add(0, ITEM_2_ID, 1, "我要拍照");return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {super.onOptionsItemSelected(item);switch(item.getItemId()){case 1:{ //调出系统设置界面Intent intent = new Intent(PhotoScanAct.this,PictureViewAct.class);intent.putExtra("imagePath",newFilePath);startActivity(intent);break;}case 2:{ //拍照startActivity(new Intent(this,TakePhotoAct.class));break;}}return true;}}??ImageAdapter适配器:
import java.util.List;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.Gallery;import android.widget.ImageView;/** * 图片适配器 * @author aokunsang * @Date 2011-12-6 */public class ImageAdapter extends BaseAdapter {private List<String> picList;private Context mContext;public ImageAdapter(Context mContext,List<String> picList){this.mContext = mContext;this.picList = picList;}@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);}/* (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 image = new ImageView(mContext);Bitmap bm = BitmapFactory.decodeFile(picList.get(position));image.setImageBitmap(bm);image.setScaleType(ImageView.ScaleType.CENTER_CROP);image.setLayoutParams(new Gallery.LayoutParams(100, 100));return image;}}?xml:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageSwitcher android:id="@+id/switcher"android:layout_width="match_parent"android:layout_height="match_parent" ></ImageSwitcher> <Gallery android:id="@+id/gallery" android:layout_width="match_parent" android:background="#55000000" android:layout_height="100dip" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:gravity="center_vertical" android:spacing="8dip" /> </RelativeLayout>?上传工具类:
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();}}?
@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); }?