转载请注明出处:http://blog.csdn.net/qq_35372900/article/details/60957378
许多多项目都有用户设置头像的功能。本篇文章就带大家学习一下如何实现用户头像设置。
首先看一下效果。
项目源码:https://github.com/moonfollower/SetHeadPortraitDemo
如上图所示,我们要实现让用户点击 ”更换头像“ 按钮时弹出一个窗口,用户可选择使用相机拍照或从手机相册中选择图片作为头像,并对用户确认图片进行截图。最后让切图完成的图片显示在我们的控件上并上传到服务器。
圆形图片的显示有两种方法,一是自定义圆形ImageView(本文采用此方法),二是将用户截图完成后的图片裁剪成圆形。
自定义CircleImageView网上有很多文章了,这里就不做解释了。感兴趣的同学可以参考我的Github,网址如下:
https://github.com/moonfollower/CircleImageView
弹出框这里是自定义的弹出框,可以根据项目需求进行更改。下面是我的弹出框代码。
public class ChangeHeadImagePop extends PopupWindow implements View.OnClickListener { private Activity activity; private View popView; private View v_item1; private View v_item2; private View v_item3; private OnItemClickListener onItemClickListener; /** * * @author moonfolower枚举,用于区分选择了哪个选项 */ public enum MENUITEM { ITEM1, ITEM2, ITEM3 } public ChangeHeadImagePop(final Activity activity) { super(activity); this.activity = activity; LayoutInflater inflater = (LayoutInflater) activity .getSystemService(Context.LAYOUT_INFLATER_SERVICE); popView = inflater.inflate(R.layout.chose_headimage_popwind, null);// 加载菜单布局文件 this.setContentView(popView);// 把布局文件添加到popupwindow中 this.setWidth(LinearLayout.LayoutParams.MATCH_PARENT);// 设置菜单的宽度(需要和菜单于右边距的距离搭配,可以自己调到合适的位置) this.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT); this.setFocusable(true);// 获取焦点 this.setTouchable(true); // 设置PopupWindow可触摸 this.setOutsideTouchable(true); // 设置非PopupWindow区域可触摸 ColorDrawable dw = new ColorDrawable(0x00000000); this.setBackgroundDrawable(dw); this.setOnDismissListener(new OnDismissListener() { @Override public void onDismiss() { /*setBackgroundAlpha(activity,1f);*/ } }); /*setBackgroundAlpha(activity,0.7f);*/ // 获取选项卡 v_item1 = popView.findViewById(R.id.camera); v_item2 = popView.findViewById(R.id.photoAlbum); v_item3 = popView.findViewById(R.id.cancel); // 添加监听 v_item1.setOnClickListener(this); v_item2.setOnClickListener(this); v_item3.setOnClickListener(this); } /** * 设置显示的位置 */ public void showLocation(View anchorView) { /*int windowPos[] = PopupWindowUtil.calculatePopWindowPos(anchorView, popView); int xOff = 20; // 可以自己调整偏移 windowPos[0] -= xOff;*/ this.showAtLocation(anchorView, Gravity.BOTTOM , 0, 0); } /** * 设置背景色 */ private void setBackgroundAlpha(Activity context, float bgAlpha) { WindowManager.LayoutParams lp = context.getWindow().getAttributes(); lp.alpha = bgAlpha; context.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); context.getWindow().setAttributes(lp); } // 点击监听接口 public interface OnItemClickListener { void onClick(MENUITEM item, String str); } // 设置监听 public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } @Override public void onClick(View v) { MENUITEM menuitem = null; String str = ""; if (v == v_item1) { menuitem = MENUITEM.ITEM1; str = "选项卡一"; } else if (v == v_item2) { menuitem = MENUITEM.ITEM2; str = "选项卡二"; } else if (v == v_item3) { menuitem = MENUITEM.ITEM3; str = "选项卡三"; } if (onItemClickListener != null) { onItemClickListener.onClick(menuitem, str); } dismiss(); } } XML布局 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:divider="@drawable/divider" android:showDividers = "middle" android:background="#fff" android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/camera" android:text="拍照" android:textSize="14sp" android:background="@null" android:layout_width="match_parent" android:layout_height="42dp" /> <Button android:id="@+id/photoAlbum" android:background="@null" android:text="从手机上获取" android:textSize="14sp" android:layout_width="match_parent" android:layout_height="42dp" /> <Button android:id="@+id/cancel" android:background="@null" android:text="取消" android:textSize="14sp" android:layout_width="match_parent" android:layout_height="42dp" /> </LinearLayout> LinearLayout分割线,主要加上size属性 <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="#f00" /> <size android:height="1px" /> </shape> 有人要说了,讲了半天还没说到头像设置呢?简直是个坑货。别急别急,准备工作必不可少,下面就进入正题。1、点击拍照按钮启动相机进行拍照
首先给我们拍得的照片设置一个地址,方便截图使用。然后打开相机进行拍照。
注意这里有个坑,某些手机不支持直接修改SD子目录,如果你直接使用
Environment.getExternalStorageDirectory();这个目录,选择相机进行拍照时,拍照完成时,因照片保存失败,导致不能从相机跳转回我们的项目。所以, 为适配所有手机,建议大家不要把图片保存地址放在SD卡直接子目录中,多写两层文件目录。 File dir = new File(imageDirPath); if(!dir.exists()){ dir.mkdirs(); } File file = new File(dir,takePhotoImageName);//指定拍照后相片保存地址,以覆盖方式保存。 Log.e("相机存放图片地址",file.getAbsolutePath()); iconUri = Uri.fromFile(file); // 启动相机 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 设置输出路径 intent.putExtra(MediaStore.EXTRA_OUTPUT, iconUri); startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO); 2、选择手机相册中的图片。 Intent intent2 = new Intent(Intent.ACTION_PICK, null); intent2.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); startActivityForResult(intent2, REQUEST_CODE_CHOOSE_IMAGE);
3、对获取到的图片进行截图(更改图片大小,设置图片位置等)。
/** * 裁剪图片 * @param uri */ public void startCropImage(Uri uri) { Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, "image/*"); // 图片处于可裁剪状态 intent.putExtra("crop", "true"); // aspectX aspectY 是宽高的比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // 是否之处缩放 intent.putExtra("scale", true); // 设置图片的输出大小, 对于普通的头像,应该设置一下,可提高头像的上传速度 intent.putExtra("outputX", 300); intent.putExtra("outputY", 300); // 以Uri的方式传递照片 File dir = new File(imageDirPath); if(!dir.exists()){ dir.mkdirs(); } File crop_image = new File(dir,crop_ImageName); cropImageUri = Uri.fromFile(crop_image); intent.putExtra(MediaStore.EXTRA_OUTPUT, cropImageUri); // 设置图片输出格式 intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("return-data", false); // 关闭人脸识别 intent.putExtra("noFaceDetection", false); startActivityForResult(intent, REQUEST_CODE_CROP_IMAGE); }
在onActivityResult方法中进行操作
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // 用户没有进行有效的设置操作,返回 if (resultCode == RESULT_CANCELED) { Toast.makeText(MainActivity.this,"取消",Toast.LENGTH_SHORT).show(); return; } switch (requestCode){ case REQUEST_CODE_TAKE_PHOTO: if(null != iconUri){ File file = new File(iconUri.getPath()); Log.e("文件是否存在",file.exists()+""); // 打开相机页面后,如果按返回键也会回调,所以需要判断是否拍摄了照片 if (file.exists()) { // 裁剪图片 startCropImage(iconUri); } } break; case REQUEST_CODE_CHOOSE_IMAGE: Log.e("相册选择",data.getData()+""); if (data.getData() != null) { iconUri = data.getData(); startCropImage(iconUri); } break; case REQUEST_CODE_CROP_IMAGE: Toast.makeText(MainActivity.this,"剪切完毕",Toast.LENGTH_SHORT).show(); //上传图片,然后设置图片,这里不上传图片。 if(null !=cropImageUri){ Log.e("剪切图片地址",cropImageUri.getPath()); Bitmap bitmap = BitmapFactory.decodeFile(imageDirPath+File.separator+crop_ImageName); circleImageView.setImageBitmap(bitmap); bitmap.recycle(); } break; } } 最后是自定义的一些常量,包括图片存放地址,图片文件名和请求码等。 public static final String imageDirPath = Environment.getExternalStorageDirectory()+"/moon/images"; public static final String takePhotoImageName = "take_photo.jpg"; public static final String crop_ImageName = "crop_image.jpg"; //照相 private static final int REQUEST_CODE_TAKE_PHOTO = 0; //选择图片 private static final int REQUEST_CODE_CHOOSE_IMAGE = 1; //剪切图片 private static final int REQUEST_CODE_CROP_IMAGE = 2;到这里就完成了,是不是很easy,如果不太明白,可以下载源码。
项目源码:https://github.com/moonfollower/SetHeadPortraitDemo