Android仿微信QQ设置图形头像裁剪功能
最近在做毕业设计,想有一个功能和qq一样可以裁剪头像并设置圆形头像,额,这是设计狮的一种潮流。
而纵观现在主流的app,只要有用户系统这个功能,这个需求一般都是在(bu)劫(de)难(bu)逃(xue)!
图片裁剪实现方式有两种,一种是利用系统自带的裁剪工具,一种是使用开源工具cropper。本节就为大家带来如何使用系统自带的裁剪工具进行图片裁剪~
还是先来个简单的运行图。
额,简单说下,我待会会把代码写成小demo分享给大家,在文章末尾会附上github链接,需要的可以自行下载~
下面来简单分析一下实现思路,我们首先照片肯定可以通过拍照和从相册选取,这个都可以向系统发送特定的intent,响应对应的系统程序,然后在onactivityresult里面,获取我们的数据即可。而在onactivityresult里面,我们可以获取到两种形式的数据,bitmap and uri。一般情况下我们是不会选择bitmap的,因为大家都知道我们的手机里面的照片都太大了~强行使用bitmap,我只能说你,屌屌屌,sorry,我说的不是666,是傻屌的意思!
哈哈哈,让我爆粗口,我原本是拒绝的~只是希望警醒在看文章的你,那么就用uri吧~
那么然后呢?当然是对它做裁剪,完成后把这个裁剪后的bitmap对象设置给imageview,保存起来,上传到服务器即可。
大致了解了流程,那么我们直接看代码吧~
先看看我们的圆形image吧,我这个有点乱,因为考虑了很多我毕设的逻辑,所以做了一些修正,这个圆形image相信网上会很多。
package com.example.nanchen.cropimagetest; import android.content.context; import android.content.res.typedarray; import android.graphics.bitmap; import android.graphics.bitmap.config; import android.graphics.canvas; import android.graphics.color; import android.graphics.colormatrix; import android.graphics.colormatrixcolorfilter; import android.graphics.paint; import android.graphics.porterduff.mode; import android.graphics.porterduffxfermode; import android.graphics.rect; import android.graphics.rectf; import android.util.attributeset; import android.view.motionevent; import android.view.view; import android.widget.imageview; /** * @author nanchen * @filename cropimagetest * @packagename com.example.nanchen.cropimagetest * @date 2016/10/13 15:09 */ public class roundimageview extends imageview { /** * 圆形imageview,可设置最多两个宽度不同且颜色不同的圆形边框。 * * @author alan */ private static class imageview_level { public final static int level0 = 0; public final static int level1 = 1; public final static int level2 = 2; public final static int level3 = 3; public final static int level4 = 4; } private context mcontext; private int circlecolor = color.white; private int circlewidth = 0; private int mlevel = imageview_level.level1; public void setlevel(int level) { mlevel = level; } public roundimageview(context context) { super(context); mcontext = context; } public roundimageview(context context, attributeset attrs) { super(context, attrs); mcontext = context; setcustomattributes(attrs); } public roundimageview(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); mcontext = context; setcustomattributes(attrs); } private void setcustomattributes(attributeset attrs) { typedarray a = mcontext.obtainstyledattributes(attrs, r.styleable.roundedimageview); int width = a.getdimensionpixelsize(r.styleable.roundedimageview_border_thickness, 0); setpadding(width, width, width, width); mlevel = a.getint(r.styleable.roundedimageview_image_mode, imageview_level.level1); circlecolor = a.getcolor(r.styleable.roundedimageview_border_color, circlecolor); } @override public void setimagebitmap(bitmap bm) { switch (this.mlevel) { case imageview_level.level1 : bm = roundbitmap(bm); case imageview_level.level2 : if ((getpaddingleft() == getpaddingright()) && (getpaddingleft() == getpaddingbottom()) && (getpaddingleft() == getpaddingtop())) { this.circlewidth = getpaddingleft(); bm = roundbitmap(bm); } break; case imageview_level.level3 : bm = chamferbitmap(bm); break; case imageview_level.level4: if ((getpaddingleft() == getpaddingright()) && (getpaddingleft() == getpaddingbottom()) && (getpaddingleft() == getpaddingtop())) { this.circlewidth = getpaddingleft(); bm = roundbitmap(bm); } break; default : break; } super.setimagebitmap(bm); } @override protected void ondraw(canvas canvas) { switch (this.mlevel) { case imageview_level.level2: if (circlewidth > 0) { drawcircleborder(canvas, (getwidth() - this.circlewidth*2 + circlewidth) / 2, this.circlecolor, getwidth(), getheight(), this.circlewidth); } break; case imageview_level.level4: if (circlewidth > 0){ int paddingwidth = circlewidth; drawcircleborder(canvas, (getwidth()-paddingwidth*2 +circlewidth /2) / 2, this.circlecolor, getwidth(), getheight(), this.circlewidth /2,color.dkgray); int tempwidth = circlewidth /2; drawcircleborder(canvas, (getwidth()-paddingwidth*2 +tempwidth) / 2, this.circlecolor, getwidth(), getheight(), tempwidth,color.dkgray); } break; default: break; } super.ondraw(canvas); } /** * bitmap切成圆形 * * @param bitmap 传入bitmap对象 * @return */ private bitmap roundbitmap(bitmap bitmap) { bitmap resultbitmap = null; canvas canvas = null; int width = bitmap.getwidth(); int height = bitmap.getheight(); float roundpx; float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom; if (width <= height) { roundpx = width / 2; top = 0; bottom = width; left = 0; right = width; height = width; dst_left = 0; dst_top = 0; dst_right = width; dst_bottom = width; } else { roundpx = height / 2; float clip = (width - height) / 2; left = clip; right = width - clip; top = 0; bottom = height; width = height; dst_left = 0; dst_top = 0; dst_right = height; dst_bottom = height; } if (width <= 0) { width = 1; } if (height <= 0) { height = 1; } try { resultbitmap = bitmap.createbitmap(width, height, config.argb_4444); } catch (throwable e) { e.printstacktrace(); } try { canvas = new canvas(resultbitmap); } catch (throwable e) { e.printstacktrace(); } final int color = color.red; final paint paint = new paint(); final rect src = new rect((int) left, (int) top, (int) right, (int) bottom); final rect dst = new rect((int) dst_left, (int) dst_top, (int) dst_right, (int) dst_bottom); final rectf rectf = new rectf(dst); paint.setantialias(true); canvas.drawargb(0, 0, 0, 0); // paint.setcolor(color); canvas.drawroundrect(rectf, roundpx, roundpx, paint); paint.setxfermode(new porterduffxfermode(mode.src_in)); canvas.drawbitmap(bitmap, src, dst, paint); return resultbitmap; } /** * bitmap倒角 * * @param bitmap 传入bitmap对象 * @return */ private bitmap chamferbitmap(bitmap bitmap) { bitmap resultbitmap = null; canvas canvas = null; int width = bitmap.getwidth(); int height = bitmap.getheight(); float roundpx; float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom; if (width <= height) { roundpx = dip2px(this.mcontext, 4); // 8像素倒角 4是dp值 top = 0; bottom = width; left = 0; right = width; height = width; dst_left = 0; dst_top = 0; dst_right = width; dst_bottom = width; } else { roundpx = dip2px(this.mcontext, 4); // 8像素倒角 4是dp值 float clip = (width - height) / 2; left = clip; right = width - clip; top = 0; bottom = height; width = height; dst_left = 0; dst_top = 0; dst_right = height; dst_bottom = height; } if (width <= 0) { width = 1; } if (height <= 0) { height = 1; } try { resultbitmap = bitmap.createbitmap(width, height, config.argb_4444); } catch (throwable e) { e.printstacktrace(); } try { canvas = new canvas(resultbitmap); } catch (throwable e) { e.printstacktrace(); } final int color = color.red; final paint paint = new paint(); final rect src = new rect((int) left, (int) top, (int) right, (int) bottom); final rect dst = new rect((int) dst_left, (int) dst_top, (int) dst_right, (int) dst_bottom); final rectf rectf = new rectf(dst); paint.setantialias(true); canvas.drawargb(0, 0, 0, 0); // paint.setcolor(color); canvas.drawroundrect(rectf, roundpx, roundpx, paint); paint.setxfermode(new porterduffxfermode(mode.src_in)); canvas.drawbitmap(bitmap, src, dst, paint); return resultbitmap; } /** * 画布画圆 */ private void drawcircleborder(canvas canvas, int radius, int color, int width, int height, int circlewidth) { paint paint = new paint(); /* 去锯齿 */ paint.setantialias(true); paint.setfilterbitmap(true); paint.setdither(true); paint.setcolor(color); /* 设置paint的 style 为stroke:空心 */ paint.setstyle(paint.style.stroke); /* 设置paint的外框宽度 */ paint.setstrokewidth(circlewidth); canvas.drawcircle(width / 2, height / 2, radius, paint); } private void drawcircleborder(canvas canvas, int radius, int color, int width, int height, int circlewidth,int shadowcolor){ canvas.save(); //保存画布当前状态 canvas.rotate(45,width / 2, height / 2); //右下角45度阴影投射 paint paint = new paint(); paint.setcolor(0x09ffffff & shadowcolor ); //设置alpha值 for(int i=0;i<circlewidth*2;i++) //向下角角偏移投射多少次阴影层 { canvas.drawcircle(width/2+i, height / 2, radius+2, paint); } canvas.restore(); paint = new paint(); /* 去锯齿 */ paint.setantialias(true); paint.setfilterbitmap(true); paint.setdither(true); paint.setcolor(color); /* 设置paint的 style 为stroke:空心 */ paint.setstyle(paint.style.stroke); /* 设置paint的外框宽度 */ paint.setstrokewidth(circlewidth); //二分之一实体 canvas.drawcircle(width / 2, height / 2, radius, paint); } public void setcirclewidth(int padding) { setpadding(padding, padding, padding, padding); } public int getcirclecolor() { return circlecolor; } public void setcirclecolor(int circlecolor) { this.circlecolor = circlecolor; } // 执行完setimagebitmap后才能获得; public int getcirclewidth() { return this.circlewidth; } public ontouchlistener ontouchlistener = new ontouchlistener() { @override public boolean ontouch(view view, motionevent event) { switch (event.getaction()) { case motionevent.action_up: changelight((imageview) view, 0); // onclick break; case motionevent.action_down: changelight((imageview) view, -60); break; case motionevent.action_move: // changelight((imageview) view, 0); break; case motionevent.action_cancel: changelight((imageview) view, 0); break; default: break; } return false; } }; public void setcolorfilter(boolean value){ if(value){ setontouchlistener(ontouchlistener); }else{ setontouchlistener(null); } } private void changelight(imageview imageview, int brightness) { colormatrix matrix = new colormatrix(); matrix.set(new float[] { 1, 0, 0, 0, brightness, 0, 1, 0, 0, brightness, 0, 0, 1, 0, brightness, 0, 0, 0, 1, 0 }); imageview.setcolorfilter(new colormatrixcolorfilter(matrix)); } /** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) */ private int dip2px(context context, float dpvalue) { final float scale = context.getapplicationcontext().getresources().getdisplaymetrics().density; return (int) (dpvalue * scale + 0.5f); } }
自定义一个仿ios的弹出框
package com.example.nanchen.cropimagetest; import android.app.activity; import android.app.dialog; import android.os.bundle; import android.text.textutils; import android.view.layoutinflater; import android.view.view; import android.view.view.onclicklistener; import android.view.viewgroup; import android.view.viewgroup.layoutparams; import android.view.window; import android.view.windowmanager; import android.widget.adapterview; import android.widget.adapterview.onitemclicklistener; import android.widget.baseadapter; import android.widget.button; import android.widget.listview; import android.widget.textview; import java.util.list; /** * dialog * */ public class selectdialog extends dialog implements onclicklistener,onitemclicklistener { private selectdialoglistener mlistener; private activity mactivity; private button mmbtn_cancel; private textview mtv_title; private list<string> mname; private string mtitle; private boolean musecustomcolor = false; private int mfirstitemcolor; private int motheritemcolor; public interface selectdialoglistener { public void onitemclick(adapterview<?> parent, view view, int position, long id); } private selectdialogcancellistener mcancellistener; public interface selectdialogcancellistener { public void oncancelclick(view v); } public selectdialog(activity activity, int theme, selectdialoglistener listener,list<string> names) { super(activity, theme); mactivity = activity; mlistener = listener; this.mname=names; // 设置是否点击外围解散 setcanceledontouchoutside(true); } /** * @param activity 调用弹出菜单的activity * @param theme 主题 * @param listener 菜单项单击事件 * @param cancellistener 取消事件 * @param names 菜单项名称 * */ public selectdialog(activity activity, int theme,selectdialoglistener listener,selectdialogcancellistener cancellistener ,list<string> names) { super(activity, theme); mactivity = activity; mlistener = listener; mcancellistener = cancellistener; this.mname=names; // 设置是否点击外围不解散 setcanceledontouchoutside(false); } /** * @param activity 调用弹出菜单的activity * @param theme 主题 * @param listener 菜单项单击事件 * @param names 菜单项名称 * @param title 菜单标题文字 * */ public selectdialog(activity activity, int theme,selectdialoglistener listener,list<string> names,string title) { super(activity, theme); mactivity = activity; mlistener = listener; this.mname=names; mtitle = title; // 设置是否点击外围可解散 setcanceledontouchoutside(true); } public selectdialog(activity activity, int theme,selectdialoglistener listener,selectdialogcancellistener cancellistener,list<string> names,string title) { super(activity, theme); mactivity = activity; mlistener = listener; mcancellistener = cancellistener; this.mname=names; mtitle = title; // 设置是否点击外围可解散 setcanceledontouchoutside(true); } @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); view view = getlayoutinflater().inflate(r.layout.view_dialog_select, null); setcontentview(view, new layoutparams(layoutparams.fill_parent, layoutparams.wrap_content)); window window = getwindow(); // 设置显示动画 window.setwindowanimations(r.style.main_menu_animstyle); windowmanager.layoutparams wl = window.getattributes(); wl.x = 0; wl.y = mactivity.getwindowmanager().getdefaultdisplay().getheight(); // 以下这两句是为了保证按钮可以水平满屏 wl.width = layoutparams.match_parent; wl.height = layoutparams.wrap_content; // 设置显示位置 onwindowattributeschanged(wl); //setcanceledontouchoutside(false); initviews(); } private void initviews() { dialogadapter dialogadapter=new dialogadapter(mname); listview dialoglist=(listview) findviewbyid(r.id.dialog_list); dialoglist.setonitemclicklistener(this); dialoglist.setadapter(dialogadapter); mmbtn_cancel = (button) findviewbyid(r.id.mbtn_cancel); mtv_title = (textview) findviewbyid(r.id.mtv_title); //mmbtn_cancel.setonclicklistener(this); mmbtn_cancel.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { // todo auto-generated method stub if(mcancellistener != null){ mcancellistener.oncancelclick(v); } dismiss(); } }); if(!textutils.isempty(mtitle) && mtv_title != null){ mtv_title.setvisibility(view.visible); mtv_title.settext(mtitle); }else{ mtv_title.setvisibility(view.gone); } } @override public void onclick(view v) { dismiss(); } @override public void onitemclick(adapterview<?> parent, view view, int position, long id) { mlistener.onitemclick(parent, view, position, id); dismiss(); } private class dialogadapter extends baseadapter { private list<string> mstrings; private viewholder viewholder; private layoutinflater layoutinflater; public dialogadapter(list<string> strings) { this.mstrings = strings; this.layoutinflater=mactivity.getlayoutinflater(); } @override public int getcount() { // todo auto-generated method stub return mstrings.size(); } @override public object getitem(int position) { // todo auto-generated method stub return mstrings.get(position); } @override public long getitemid(int position) { // todo auto-generated method stub return position; } @override public view getview(int position, view convertview, viewgroup parent) { if (null == convertview) { viewholder=new viewholder(); convertview=layoutinflater.inflate(r.layout.view_dialog_item, null); viewholder.dialogitembutton=(textview) convertview.findviewbyid(r.id.dialog_item_bt); convertview.settag(viewholder); }else{ viewholder=(viewholder) convertview.gettag(); } viewholder.dialogitembutton.settext(mstrings.get(position)); if (!musecustomcolor) { mfirstitemcolor = mactivity.getresources().getcolor(r.color.dialog_blue); motheritemcolor = mactivity.getresources().getcolor(r.color.dialog_blue); } if (1 == mstrings.size()) { viewholder.dialogitembutton.settextcolor(mfirstitemcolor); viewholder.dialogitembutton.setbackgroundresource(r.drawable.dialog_item_bg_only); } else if (position == 0) { viewholder.dialogitembutton.settextcolor(mfirstitemcolor); viewholder.dialogitembutton.setbackgroundresource(r.drawable.dialog_item_bg_top); } else if (position == mstrings.size() - 1) { viewholder.dialogitembutton.settextcolor(motheritemcolor); viewholder.dialogitembutton.setbackgroundresource(r.drawable.dialog_item_bg_buttom); } else { viewholder.dialogitembutton.settextcolor(motheritemcolor); viewholder.dialogitembutton.setbackgroundresource(r.drawable.dialog_item_bg_center); } return convertview; } } public static class viewholder { public textview dialogitembutton; } /** * 设置列表项的文本颜色 */ public void setitemcolor(int firstitemcolor, int otheritemcolor) { mfirstitemcolor = firstitemcolor; motheritemcolor = otheritemcolor; musecustomcolor = true; } }
由于图片文件一定在相册中,有可能你也会在文件系统中其他地方选择,这里我采用之前写的万能适配器!
由于楼主时间的确比较紧,所以代码都是直接copy上来的,有些地方没做完全优化还望大家见谅!
再看看activity的代码和布局
package com.example.nanchen.cropimagetest; import android.content.componentname; import android.content.dialoginterface; import android.content.dialoginterface.oncancellistener; import android.content.intent; import android.content.pm.resolveinfo; import android.graphics.bitmap; import android.graphics.bitmap.compressformat; import android.net.uri; import android.os.bundle; import android.os.environment; import android.provider.mediastore; import android.provider.mediastore.images.media; import android.support.v7.app.alertdialog; import android.support.v7.app.alertdialog.builder; import android.support.v7.app.appcompatactivity; import android.view.view; import android.widget.adapterview; import com.example.nanchen.cropimagetest.selectdialog.selectdialoglistener; import java.io.file; import java.io.fileoutputstream; import java.io.ioexception; import java.util.arraylist; import java.util.list; public class mainactivity extends appcompatactivity { private roundimageview mheadimage; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); mheadimage = (roundimageview) findviewbyid(r.id.main_roundimage); } private final int photo_picked_from_camera = 1; // 用来标识头像来自系统拍照 private final int photo_picked_from_file = 2; // 用来标识从相册获取头像 private final int crop_from_camera = 3; private void geticonfromphoto(){ intent intent = new intent(intent.action_get_content, media.external_content_uri); intent.settype("image/*"); startactivityforresult(intent, photo_picked_from_file); } private void selectphoto() { list<string> list = new arraylist<>(); list.add("拍照"); list.add("相册"); showdialog(new selectdialoglistener() { @override public void onitemclick(adapterview<?> parent, view view, int position, long id) { switch (position){ case 0: geticonfromcamera(); break; case 1: geticonfromphoto(); // 从系统相册获取 break; default: break; } } },list); } private uri imguri; // 由于android手机的图片基本都会很大,所以建议用uri,而不用bitmap /** * 调用系统相机拍照 */ private void geticonfromcamera() { intent intent = new intent(mediastore.action_image_capture); imguri = uri.fromfile(new file(environment.getexternalstoragedirectory(), "avatar_"+string.valueof(system.currenttimemillis())+".png")); intent.putextra(mediastore.extra_output,imguri); startactivityforresult(intent,photo_picked_from_camera); } private selectdialog showdialog(selectdialoglistener listener, list<string> list){ selectdialog dialog = new selectdialog(this, r.style.transparentframewindowstyle,listener,list); dialog.show(); return dialog; } /** * 尝试裁剪图片 */ private void docrop(){ final arraylist<cropoption> cropoptions = new arraylist<>(); final intent intent = new intent("com.android.camera.action.crop"); intent.settype("image/*"); list<resolveinfo> list = getpackagemanager().queryintentactivities(intent,0); int size = list.size(); if (size == 0){ uiutil.showtoast(this,"当前不支持裁剪图片!"); return; } intent.setdata(imguri); intent.putextra("outputx",300); intent.putextra("outputy",300); intent.putextra("aspectx",1); intent.putextra("aspecty",1); intent.putextra("scale",true); intent.putextra("return-data",true); // only one if (size == 1){ intent intent1 = new intent(intent); resolveinfo res = list.get(0); intent1.setcomponent(new componentname(res.activityinfo.packagename,res.activityinfo.name)); startactivityforresult(intent1,crop_from_camera); }else { // 很多可支持裁剪的app for (resolveinfo res : list) { cropoption co = new cropoption(); co.title = getpackagemanager().getapplicationlabel(res.activityinfo.applicationinfo); co.icon = getpackagemanager().getapplicationicon(res.activityinfo.applicationinfo); co.appintent = new intent(intent); co.appintent.setcomponent(new componentname(res.activityinfo.packagename,res.activityinfo.name)); cropoptions.add(co); } commonadapter<cropoption> adapter = new commonadapter<cropoption>(this,cropoptions,r.layout.layout_crop_selector) { @override public void convert(viewholder holder, cropoption item) { holder.setimagedrawable(r.id.iv_icon,item.icon); holder.settext(r.id.tv_name,item.title); } }; alertdialog.builder builder = new builder(this); builder.settitle("choose a app"); builder.setadapter(adapter, new dialoginterface.onclicklistener() { @override public void onclick(dialoginterface dialog, int which) { startactivityforresult(cropoptions.get(which).appintent,crop_from_camera); } }); builder.setoncancellistener(new oncancellistener() { @override public void oncancel(dialoginterface dialog) { if (imguri != null){ getcontentresolver().delete(imguri,null,null); imguri = null; } } }); alertdialog dialog = builder.create(); dialog.show(); } } @override protected void onactivityresult(int requestcode, int resultcode, intent data) { super.onactivityresult(requestcode, resultcode, data); if (resultcode != result_ok){ return; } switch (requestcode) { case photo_picked_from_camera: docrop(); break; case photo_picked_from_file: imguri = data.getdata(); docrop(); break; case crop_from_camera: if (data != null){ setcropimg(data); } break; default: break; } } private void setcropimg(intent picdata){ bundle bundle = picdata.getextras(); if (bundle != null){ bitmap mbitmap = bundle.getparcelable("data"); mheadimage.setimagebitmap(mbitmap); savebitmap(environment.getexternalstoragedirectory() + "/crop_" +system.currenttimemillis() + ".png",mbitmap); } } private void savebitmap(string filename,bitmap bitmap){ file file = new file(filename); fileoutputstream fout = null; try { file.createnewfile(); fout = new fileoutputstream(file); bitmap.compress(compressformat.png,100,fout); fout.flush(); } catch (ioexception e) { e.printstacktrace(); } finally { try { if (fout!=null){ fout.close(); } uiutil.showtoast(mainactivity.this,"保存成功!"); } catch (ioexception e) { e.printstacktrace(); } } } public void btnclick(view view) { selectphoto(); } }
布局:
<?xml version="1.0" encoding="utf-8"?> <relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.nanchen.cropimagetest.mainactivity"> <com.example.nanchen.cropimagetest.roundimageview android:id="@+id/main_roundimage" android:layout_width="100dp" android:layout_height="100dp" android:layout_alignparenttop="true" android:layout_centerinparent="true" android:src="@drawable/default_avatar"/> <button android:id="@+id/main_btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/main_roundimage" android:onclick="btnclick" android:text="设置头像"/> </relativelayout>
以上所述是小编给大家介绍的android仿微信qq设置图形头像裁剪功能,希望对大家有所帮助