转载请注明出处: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