欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android实现拍照、选择图片并裁剪图片功能

程序员文章站 2024-03-01 09:01:28
一、 实现拍照、选择图片并裁剪图片效果 按照之前博客的风格,首先看下实现效果。      二、 ucrop项目应用 想起之前看...

一、 实现拍照、选择图片并裁剪图片效果

按照之前博客的风格,首先看下实现效果。

   Android实现拍照、选择图片并裁剪图片功能 

Android实现拍照、选择图片并裁剪图片功能

二、 ucrop项目应用

想起之前看到的yalantis/ucrop效果比较绚,但是研究源码之后发现在定制界面方面还是有一点的限制,于是在它的基础上做了修改android-crop,把定制界面独立出来,让用户去*设置。下图为使用android-crop实现的模仿微信选择图片并裁剪demo。

    Android实现拍照、选择图片并裁剪图片功能

三、 实现思路

比较简单的选择设备图片裁剪,并将裁剪后的图片保存到指定路径;

调用系统拍照,将拍照图片保存在sd卡,然后裁剪图片并将裁剪后的图片保存在指定路径。
流程图如下所示:

    Android实现拍照、选择图片并裁剪图片功能
 

四、 选择框实现
这里通过popupwindow来实现,当然也可以根据需求采用其他方式实现。实现效果如下图所示:

    Android实现拍照、选择图片并裁剪图片功能

1. xml布局

<?xml version="1.0" encoding="utf-8"?> 
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:layout_width="fill_parent" 
 android:layout_height="wrap_content" 
 android:gravity="center_horizontal" 
 android:orientation="vertical"> 
 
 <linearlayout 
 android:id="@+id/pop_layout" 
 android:layout_width="match_parent" 
 android:layout_height="wrap_content" 
 android:layout_alignparentbottom="true" 
 android:background="#444" 
 android:gravity="center_horizontal" 
 android:orientation="vertical"> 
 
 <button 
 android:id="@+id/picture_selector_take_photo_btn" 
 android:layout_width="match_parent" 
 android:layout_height="wrap_content" 
 android:layout_marginleft="10dip" 
 android:layout_marginright="10dip" 
 android:layout_margintop="10dp" 
 android:background="#4d69ff" 
 android:padding="10dp" 
 android:text="拍照" 
 android:textcolor="#cec9e7" 
 android:textsize="18sp" 
 android:textstyle="bold" /> 
 
 <button 
 android:id="@+id/picture_selector_pick_picture_btn" 
 android:layout_width="match_parent" 
 android:layout_height="wrap_content" 
 android:layout_marginleft="10dip" 
 android:layout_marginright="10dip" 
 android:layout_margintop="5dp" 
 android:background="#4d69ff" 
 android:padding="10dp" 
 android:text="从相册选择" 
 android:textcolor="#cec9e7" 
 android:textsize="18sp" 
 android:textstyle="bold" /> 
 
 <button 
 android:id="@+id/picture_selector_cancel_btn" 
 android:layout_width="match_parent" 
 android:layout_height="wrap_content" 
 android:layout_marginbottom="15dip" 
 android:layout_marginleft="10dip" 
 android:layout_marginright="10dip" 
 android:layout_margintop="20dp" 
 android:background="@android:color/white" 
 android:padding="10dp" 
 android:text="取消" 
 android:textcolor="#373447" 
 android:textsize="18sp" 
 android:textstyle="bold" /> 
 </linearlayout> 
 
</relativelayout> 

2. 代码编写

public selectpicturepopupwindow(context context) { 
 super(context); 
 layoutinflater inflater = (layoutinflater) context.getsystemservice(context.layout_inflater_service); 
 mmenuview = inflater.inflate(r.layout.layout_picture_selector, null); 
 takephotobtn = (button) mmenuview.findviewbyid(r.id.picture_selector_take_photo_btn); 
 pickpicturebtn = (button) mmenuview.findviewbyid(r.id.picture_selector_pick_picture_btn); 
 cancelbtn = (button) mmenuview.findviewbyid(r.id.picture_selector_cancel_btn); 
 // 设置按钮监听 
 takephotobtn.setonclicklistener(this); 
 pickpicturebtn.setonclicklistener(this); 
 cancelbtn.setonclicklistener(this); 
} 

创建selectpicturepopupwindow的时候设置按钮的监听。这里编写一个选择监听接口:

/** 
 * 选择监听接口 
 */ 
public interface onselectedlistener { 
 void onselected(view v, int position); 
} 

回调的参数为点击的按钮view以及当前按钮的索引,那么只要在选择监听里面返回接口的回调就可以啦。

@override 
public void onclick(view v) { 
 switch (v.getid()) { 
 case r.id.picture_selector_take_photo_btn: 
 if(null != monselectedlistener) { 
 monselectedlistener.onselected(v, 0); 
 } 
 break; 
 case r.id.picture_selector_pick_picture_btn: 
 if(null != monselectedlistener) { 
 monselectedlistener.onselected(v, 1); 
 } 
 break; 
 case r.id.picture_selector_cancel_btn: 
 if(null != monselectedlistener) { 
 monselectedlistener.onselected(v, 2); 
 } 
 break; 
 } 
} 

popupwindow的初始化创建、监听设置好之后,只要提供显示与隐藏两个方法就可以了。

/** 
 * 把一个view控件添加到popupwindow上并且显示 
 * 
 * @param activity 
 */ 
public void showpopupwindow(activity activity) { 
 popupwindow = new popupwindow(mmenuview, // 添加到popupwindow 
 viewgroup.layoutparams.match_parent, viewgroup.layoutparams.wrap_content); 
 popupwindow.setbackgrounddrawable(new colordrawable(color.transparent)); 
 popupwindow.showatlocation(activity.getwindow().getdecorview(), gravity.center | gravity.bottom, 0, 0); 
 popupwindow.setanimationstyle(android.r.style.animation_inputmethod); // 设置窗口显示的动画效果 
 popupwindow.setfocusable(false); // 点击其他地方隐藏键盘 popupwindow 
 popupwindow.update(); 
} 
/** 
 * 移除popupwindow 
 */ 
public void dismisspopupwindow() { 
 if (popupwindow != null && popupwindow.isshowing()) { 
 popupwindow.dismiss(); 
 popupwindow = null; 
 } 
} 

ok,到这里选择框的实现就完成了。

五、使用选择框

通过我们上面对选择框的封装,使用起来就比较简单了,只需要初始化及设置选择的监听就可以啦。

1.初始化选择框

mselectpicturepopupwindow = new selectpicturepopupwindow(mcontext); 
mselectpicturepopupwindow.setonselectedlistener(this); 

2.设置选择框监听

@override 
public void onselected(view v, int position) { 
 switch (position) { 
 case 0: 
 // todo: "拍照"按钮被点击了 
 break; 
 case 1: 
 // todo: "从相册选择"按钮被点击了 
 break; 
 case 2: 
 // todo: "取消"按钮被点击了 
 break; 
 } 
} 

然后在fragment上进行封装,我们取名为pictureselectfragment。

六、拍照并保存图片

调用系统的拍照,并把拍摄的图片保存到指定位置。

@override 
public void onselected(view v, int position) { 
 switch (position) { 
 case 0: 
 // "拍照"按钮被点击了 
 mselectpicturepopupwindow.dismisspopupwindow(); 
 intent takeintent = new intent(mediastore.action_image_capture); 
 //下面这句指定调用相机拍照后的照片存储的路径 
 takeintent.putextra(mediastore.extra_output, uri.fromfile(new file(mtempphotopath))); 
 startactivityforresult(takeintent, camera_request_code); 
 break; 
 case 1: 
 // todo: "从相册选择"按钮被点击了 
 break; 
 case 2: 
 // todo: "取消"按钮被点击了 
 break; 
 } 
} 

这里的指定位置为sd卡本目录下
mtempphotopath = environment.getexternalstoragedirectory() + file.separator + "photo.jpeg"; 

当拍摄照片完成时会回调到onactivityresult,我们在这里处理图片的裁剪就可以了。

@override 
public void onactivityresult(int requestcode, int resultcode, intent data) { 
 if (resultcode == mactivity.result_ok) { 
 switch (requestcode) { 
 case camera_request_code: 
 // todo: 调用相机拍照 
 break; 
 } 
 } 
 super.onactivityresult(requestcode, resultcode, data); 
} 

七、相册选择图片

调用系统的选择图片

@override 
public void onselected(view v, int position) { 
 switch (position) { 
 case 0: 
 // "拍照"按钮被点击了 
 mselectpicturepopupwindow.dismisspopupwindow(); 
 intent takeintent = new intent(mediastore.action_image_capture); 
 // 下面这句指定调用相机拍照后的照片存储的路径 
 takeintent.putextra(mediastore.extra_output, uri.fromfile(new file(mtempphotopath))); 
 startactivityforresult(takeintent, camera_request_code); 
 break; 
 case 1: 
 // "从相册选择"按钮被点击了 
 mselectpicturepopupwindow.dismisspopupwindow(); 
 intent pickintent = new intent(intent.action_pick, null); 
 // 如果限制上传到服务器的图片类型时可以直接写如:"image/jpeg 、 image/png等的类型" 
 pickintent.setdataandtype(mediastore.images.media.external_content_uri, "image/*"); 
 startactivityforresult(pickintent, gallery_request_code); 
 break; 
 case 2: 
 // todo: "取消"按钮被点击了 
 break; 
 } 
} 

当拍选择图片完成时会回调到onactivityresult,在这里处理选择的返回结果。

@override 
public void onactivityresult(int requestcode, int resultcode, intent data) { 
 if (resultcode == mactivity.result_ok) { 
 switch (requestcode) { 
 case camera_request_code: 
 // todo: 调用相机拍照 
 break; 
 case gallery_request_code: 
 // todo: 直接从相册获取 
 break; 
 } 
 } 
 super.onactivityresult(requestcode, resultcode, data); 
} 

八、使用crop裁剪图片

裁剪图片,这里设置宽高比为1:1,最大尺寸为512*512,当然可以根据自己的需求来设置。

/** 
 * 裁剪图片方法实现 
 * 
 * @param uri 
 */ 
public void startcropactivity(uri uri) { 
 ucrop.of(uri, mdestinationuri) 
 .withaspectratio(1, 1) 
 .withmaxresultsize(512, 512) 
 .withtargetactivity(cropactivity.class) 
 .start(mactivity, this); 
} 

cropactiivty裁剪完成时会回调到onactivityresult,在这里处理选择的返回结果。

@override 
public void onactivityresult(int requestcode, int resultcode, intent data) { 
 if (resultcode == mactivity.result_ok) { 
 switch (requestcode) { 
 case camera_request_code: // 调用相机拍照 
 file temp = new file(mtempphotopath); 
 startcropactivity(uri.fromfile(temp)); 
 break; 
 case gallery_request_code: // 直接从相册获取 
 startcropactivity(data.getdata()); 
 break; 
 case ucrop.request_crop: 
 // todo: 裁剪图片结果 
 break; 
 case ucrop.result_error: 
 // todo: 裁剪图片错误 
 break; 
 } 
 } 
 super.onactivityresult(requestcode, resultcode, data); 
} 

cropactivity的界面如下所示:

Android实现拍照、选择图片并裁剪图片功能

当然也可以轻松设计成如下两图:

Android实现拍照、选择图片并裁剪图片功能

Android实现拍照、选择图片并裁剪图片功能

1. xml布局

<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" 
 xmlns:fab="http://schemas.android.com/apk/res-auto" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:cliptopadding="true" 
 android:fitssystemwindows="true"> 
 
 <include layout="@layout/toolbar_layout" /> 
 
 <framelayout 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:layout_below="@+id/toolbar" 
 android:background="#000"> 
 
 <com.kevin.crop.view.ucropview 
 android:id="@+id/weixin_act_ucrop" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:visibility="invisible" /> 
 
 </framelayout> 
 
 <android.support.design.widget.coordinatorlayout 
 android:layout_width="match_parent" 
 android:layout_height="match_parent"> 
 
 <android.support.design.widget.floatingactionbutton 
 android:id="@+id/crop_act_save_fab" 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:layout_gravity="bottom|right" 
 android:layout_margin="@dimen/fab_margin" 
 android:src="@mipmap/ic_done_white" 
 fab:fabsize="normal" /> 
 </android.support.design.widget.coordinatorlayout> 
 
 
</relativelayout> 

可以发现非常简单,只有一个主要的cropview,这就是ucrop框架为我们提供的。

2. 代码编写

@override 
protected void initviews() { 
 inittoolbar(); 
 
 mgesturecropimageview = mucropview.getcropimageview(); 
 moverlayview = mucropview.getoverlayview(); 
 
 // 设置允许缩放 
 mgesturecropimageview.setscaleenabled(true); 
 // 设置禁止旋转 
 mgesturecropimageview.setrotateenabled(false); 
 // 设置外部阴影颜色 
 moverlayview.setdimmedcolor(color.parsecolor("#aa000000")); 
 // 设置周围阴影是否为椭圆(如果false则为矩形) 
 moverlayview.setovaldimmedlayer(false); 
 // 设置显示裁剪边框 
 moverlayview.setshowcropframe(true); 
 // 设置不显示裁剪网格 
 moverlayview.setshowcropgrid(false); 
 
 final intent intent = getintent(); 
 setimagedata(intent); 
} 
private void setimagedata(intent intent) { 
 uri inputuri = intent.getparcelableextra(ucrop.extra_input_uri); 
 moutputuri = intent.getparcelableextra(ucrop.extra_output_uri); 
 
 if (inputuri != null && moutputuri != null) { 
 try { 
 mgesturecropimageview.setimageuri(inputuri); 
 } catch (exception e) { 
 setresultexception(e); 
 finish(); 
 } 
 } else { 
 setresultexception(new nullpointerexception("both input and output uri must be specified")); 
 finish(); 
 } 
 
 // 设置裁剪宽高比 
 if (intent.getbooleanextra(ucrop.extra_aspect_ratio_set, false)) { 
 float aspectratiox = intent.getfloatextra(ucrop.extra_aspect_ratio_x, 0); 
 float aspectratioy = intent.getfloatextra(ucrop.extra_aspect_ratio_y, 0); 
 
 if (aspectratiox > 0 && aspectratioy > 0) { 
 mgesturecropimageview.settargetaspectratio(aspectratiox / aspectratioy); 
 } else { 
 mgesturecropimageview.settargetaspectratio(cropimageview.source_image_aspect_ratio); 
 } 
 } 
 
 // 设置裁剪的最大宽高 
 if (intent.getbooleanextra(ucrop.extra_max_size_set, false)) { 
 int maxsizex = intent.getintextra(ucrop.extra_max_size_x, 0); 
 int maxsizey = intent.getintextra(ucrop.extra_max_size_y, 0); 
 
 if (maxsizex > 0 && maxsizey > 0) { 
 mgesturecropimageview.setmaxresultimagesizex(maxsizex); 
 mgesturecropimageview.setmaxresultimagesizey(maxsizey); 
 } else { 
 log.w(tag, "extra_max_size_x and extra_max_size_y must be greater than 0"); 
 } 
 } 
} 

以上为cropview的配置,更多配置请参考项目源码。

最重要的,裁剪保存图片:

private void cropandsaveimage() { 
 outputstream outputstream = null; 
 try { 
 final bitmap croppedbitmap = mgesturecropimageview.cropimage(); 
 if (croppedbitmap != null) { 
 outputstream = getcontentresolver().openoutputstream(moutputuri); 
 croppedbitmap.compress(bitmap.compressformat.jpeg, 85, outputstream); 
 croppedbitmap.recycle(); 
 
 setresulturi(moutputuri, mgesturecropimageview.gettargetaspectratio()); 
 finish(); 
 } else { 
 setresultexception(new nullpointerexception("cropimageview.cropimage() returned null.")); 
 } 
 } catch (exception e) { 
 setresultexception(e); 
 finish(); 
 } finally { 
 bitmaploadutils.close(outputstream); 
 } 
} 

pictureselectfragment处理裁剪成功的返回值

/** 
 * 处理剪切成功的返回值 
 * 
 * @param result 
 */ 
private void handlecropresult(intent result) { 
 deletetempphotofile(); 
 final uri resulturi = ucrop.getoutput(result); 
 if (null != resulturi && null != monpictureselectedlistener) { 
 bitmap bitmap = null; 
 try { 
 bitmap = mediastore.images.media.getbitmap(mactivity.getcontentresolver(), resulturi); 
 } catch (filenotfoundexception e) { 
 e.printstacktrace(); 
 } catch (ioexception e) { 
 e.printstacktrace(); 
 } 
 monpictureselectedlistener.onpictureselected(resulturi, bitmap); 
 } else { 
 toast.maketext(mcontext, "无法剪切选择图片", toast.length_short).show(); 
 } 
} 

处理裁剪失败的返回值

/** 
 * 处理剪切失败的返回值 
 * 
 * @param result 
 */ 
private void handlecroperror(intent result) { 
 deletetempphotofile(); 
 final throwable croperror = ucrop.geterror(result); 
 if (croperror != null) { 
 log.e(tag, "handlecroperror: ", croperror); 
 toast.maketext(mcontext, croperror.getmessage(), toast.length_long).show(); 
 } else { 
 toast.maketext(mcontext, "无法剪切选择图片", toast.length_short).show(); 
 } 
} 

这里设置了选择的回调接口,便于封装抽取。

/** 
 * 图片选择的回调接口 
 */ 
public interface onpictureselectedlistener { 
 /** 
 * 图片选择的监听回调 
 * 
 * @param fileuri 
 * @param bitmap 
 */ 
 void onpictureselected(uri fileuri, bitmap bitmap); 
} 

经过五、六、七步骤,我们的pictureselectfragment就搞定了,在使用的时候只要继承它,几行代码就搞定了。

九、pictureselectfragment使用

// 设置图片点击监听 
mpictureiv.setonclicklistener(new view.onclicklistener() { 
 @override 
 public void onclick(view v) { 
 selectpicture(); 
 } 
}); 
// 设置裁剪图片结果监听 
setonpictureselectedlistener(new onpictureselectedlistener() { 
 @override 
 public void onpictureselected(uri fileuri, bitmap bitmap) { 
 mpictureiv.setimagebitmap(bitmap); 
 
 string filepath = fileuri.getencodedpath(); 
 string imagepath = uri.decode(filepath); 
 toast.maketext(mcontext, "图片已经保存到:" + imagepath, toast.length_long).show(); 
 } 
}); 

ok,经过我们上面的封装及基类抽取,在使用的时候还是非常简单的。

十、下载

源码及示例
用到的android-crop库

更多内容大家可以参考专题《android图片处理》进行学习。

以上就是本文的全部内容,希望对大家学习android软件编程有所帮助。