接下来完成MeFragment中可选择进入的AccountInfoActivity(账号信息详情页),可修改个人信息
1. activity_account_info.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:fitsSystemWindows="true">
<include layout="@layout/title_layout"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/interval_color">
<View
android:layout_width="match_parent"
android:layout_height="10dp"/>
<RelativeLayout
android:id="@+id/layout_head"
android:background="@color/white_color"
android:layout_width="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_height="80dp">
<TextView
android:text="@string/account_head_picture"
android:gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="16sp"
android:textColor="@color/app_black_color"/>
<com.joooonho.SelectableRoundedImageView
android:id="@+id/iv_head_picture"
android:layout_alignParentRight="true"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_width="70dp"
android:scaleType="fitXY"
app:sriv_oval="true"
android:src="@mipmap/app_logo_main"
android:layout_height="70dp"/>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.8dp"/>
<RelativeLayout
android:background="@color/white_color"
android:layout_width="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_height="40dp">
<TextView
android:id="@+id/tv_1"
android:text="账号"
android:gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="16sp"
android:textColor="@color/app_black_color"/>
<TextView
android:layout_toRightOf="@+id/tv_1"
android:layout_marginLeft="10dp"
android:textSize="16sp"
android:textColor="@color/default_text_color"
android:id="@+id/tv_account"
android:gravity="center_vertical|right"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.8dp"/>
<RelativeLayout
android:background="@color/white_color"
android:layout_width="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_height="40dp">
<TextView
android:id="@+id/tv_2"
android:text="@string/account_nick"
android:gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="16sp"
android:textColor="@color/app_black_color"/>
<EditText
android:layout_toRightOf="@+id/tv_2"
android:layout_marginLeft="10dp"
android:textSize="16sp"
android:textColor="@color/default_text_color"
android:id="@+id/et_account_nick"
android:gravity="center_vertical|right"
android:layout_width="match_parent"
android:background="@null"
android:layout_height="match_parent"/>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.8dp"/>
<RelativeLayout
android:background="@color/white_color"
android:layout_width="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_height="40dp">
<TextView
android:id="@+id/tv_3"
android:text="@string/account_sex"
android:gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="16sp"
android:textColor="@color/app_black_color"/>
<TextView
android:layout_toRightOf="@+id/tv_3"
android:layout_marginLeft="10dp"
android:textSize="16sp"
android:textColor="@color/default_text_color"
android:id="@+id/tv_account_sex"
android:gravity="center_vertical|right"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.8dp"/>
<RelativeLayout
android:background="@color/white_color"
android:layout_width="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_height="40dp">
<TextView
android:id="@+id/tv_4"
android:text="@string/account_birth"
android:gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="16sp"
android:textColor="@color/app_black_color"/>
<TextView
android:layout_toRightOf="@+id/tv_4"
android:layout_marginLeft="10dp"
android:textSize="16sp"
android:textColor="@color/default_text_color"
android:id="@+id/tv_account_birth"
android:gravity="center_vertical|right"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.8dp"/>
<RelativeLayout
android:background="@color/white_color"
android:layout_width="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_height="40dp">
<TextView
android:id="@+id/tv_5"
android:text="@string/account_location"
android:gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="16sp"
android:textColor="@color/app_black_color"/>
<TextView
android:layout_toRightOf="@+id/tv_5"
android:layout_marginLeft="10dp"
android:gravity="center_vertical|right"
android:textSize="16sp"
android:textColor="@color/default_text_color"
android:id="@+id/tv_account_location"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.8dp"/>
<RelativeLayout
android:background="@color/white_color"
android:layout_width="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_height="60dp">
<TextView
android:id="@+id/tv_6"
android:text="@string/account_signature"
android:gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="16sp"
android:textColor="@color/app_black_color"/>
<EditText
android:hint="@string/signature_hint"
android:layout_toRightOf="@+id/tv_6"
android:layout_marginLeft="10dp"
android:textSize="16sp"
android:textColor="@color/default_text_color"
android:id="@+id/et_account_signature"
android:gravity="center_vertical|right"
android:maxLines="2"
android:background="@null"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
</LinearLayout>
</LinearLayout>
2. 显示数据
通过NimUserHandler获得本地账户,由在本地账户中设置的属性数据设置个人信息页并显示
private void showData() {
mAccountBean = NimUserHandler.getInstance().getLocalAccount();
if (mAccountBean != null) {
ImageUtils.setImageByFile(this, mIvHead,
mAccountBean.getHeadImgUrl(), R.mipmap.bg_img_defalut);
mTvAccount.setText(mAccountBean.getAccount());
mEtNick.setText(mAccountBean.getNick());
if (mAccountBean.getGenderEnum() == GenderEnum.FEMALE) {
mTvSex.setText("女");
} else if (mAccountBean.getGenderEnum() == GenderEnum.MALE) {
mTvSex.setText("男");
} else {
mTvSex.setText("保密");
}
mEtSignature.setText(mAccountBean.getSignature());
String birthday = mAccountBean.getBirthDay();
if (TextUtils.isEmpty(birthday)) {
mTvBirthDay.setText("未设置");
} else {
mTvBirthDay.setText(birthday);
}
String location = mAccountBean.getLocation();
if (TextUtils.isEmpty(location)) {
mTvLocation.setText("未设置");
} else {
mTvLocation.setText(location);
}
}
}
3. 初始化
mInputMethodManager用于控制显示或隐藏输入法面板
然后在可以为设置头像,性别,生日,地区,编辑状态,回退这些控件上添加OnClickListener
为编辑昵称和签名的控件上添加OnTouchListener
private void init() {
mInputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
// 文字
mLayoutHead.setOnClickListener(this);
mTvSex.setOnClickListener(this);
mTvBirthDay.setOnClickListener(this);
mTvLocation.setOnClickListener(this);
// 标题栏
mIvBack.setOnClickListener(this);
mIvMenu.setOnClickListener(this);
// 输入框
mEtNick.setOnTouchListener(this);
mEtSignature.setOnTouchListener(this);
// 结束编辑,相当于初始化为非编辑状态
finishEdit();
}
4. onClick
设置点击到不同控件时,可以设置对应的不同属性
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.layout_head:
setHeadImg();
break;
case R.id.tv_account_sex:
setSex();
break;
case R.id.tv_account_location:
setLocation();
break;
case R.id.tv_account_birth:
setBirthday();
break;
case R.id.iv_back_btn:
this.finish();
break;
case R.id.iv_menu_btn:
if (isEditor) {
finishEdit();
} else {
startEdit();
}
break;
}
}
5. onTouch——用于设置要自行输入的几项属性(昵称,签名)
如果是要设置昵称和签名时,获取焦点并将光标移动到末尾
显示软键盘
@Override
public boolean onTouch(View v, MotionEvent event) {
if (isEditor) {
if (v.getId() == R.id.et_account_nick) {
mEtNick.requestFocus();
mEtNick.setSelection(mEtNick.getText().length());
mInputMethodManager.showSoftInput(mEtNick, 0);
} else if (v.getId() == R.id.et_account_signature) {
mEtSignature.requestFocus();
mEtSignature.setSelection(mEtSignature.getText().length());
mInputMethodManager.showSoftInput(mEtSignature, 0);
}
return true;
}
return false;
}
6. 启动编辑
当右上方菜单栏为此图标时,处在编辑状态(即点击该图标,可进入结束编辑状态)
所有控件处于可点击(头像,性别,地区,生日)或可编辑(昵称,签名)状态
private void startEdit() {
mIvMenu.setImageResource(R.mipmap.done);
// 可点击
mLayoutHead.setClickable(true);
mTvSex.setClickable(true);
mTvLocation.setClickable(true);
mTvBirthDay.setClickable(true);
// 可编辑
mEtNick.setFocusable(true);
mEtNick.setFocusableInTouchMode(true);
mEtSignature.setFocusable(true);
mEtSignature.setFocusableInTouchMode(true);
isEditor = true;
}
7. 结束编辑
判断是否有修改
若有修改,通过NimUserHandler的setLocalAccount方法将数据更新到缓存,syncChange2Servive方法将数据更新到服务器
结束编辑后,右上方菜单栏图标变换为(即点击该图标,可重新进入编辑状态)
并将控件设置为不可点击(头像,性别,地区,生日),不可编辑(昵称,签名)状态
private void finishEdit() {
if (!mEtNick.getText().toString()
.equals(mAccountBean.getNick())) {
mAccountBean.setNick(mEtNick.getText().toString());
haveAccountChange = true;
}
if (!mEtSignature.getText().toString()
.equals(mAccountBean.getSignature())) {
mAccountBean.setSignature(mEtSignature.getText().toString());
haveAccountChange = true;
}
if (haveAccountChange) {
// 将数据更新到缓存
NimUserHandler.getInstance().setLocalAccount(mAccountBean);
// 通知handler将数据更新到服务器
NimUserHandler.getInstance().syncChange2Service();
haveAccountChange = false;
}
mIvMenu.setImageResource(R.mipmap.editor);
// 不可点击
mLayoutHead.setClickable(false);
mTvSex.setClickable(false);
mTvLocation.setClickable(false);
mTvBirthDay.setClickable(false);
// 不可编辑
mEtNick.setFocusable(false);
mEtNick.setFocusableInTouchMode(false);
mEtSignature.setFocusable(false);
mEtSignature.setFocusableInTouchMode(false);
isEditor = false;
}
8. 设置性别
利用AlertDialog
private void setSex(){
final int[] selected = new int[1];
if (mAccountBean.getGenderEnum() == GenderEnum.MALE) {
selected[0] = 0;
} else if (mAccountBean.getGenderEnum() == GenderEnum.FEMALE) {
selected[0] = 1;
} else {
selected[0] = 2;
}
final String[] items = new String[]{"男", "女", "保密"};
new AlertDialog.Builder(this)
.setTitle("性别")
.setSingleChoiceItems(items, selected[0], new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which != selected[0]) {
if (which == 0) {
mAccountBean.setGenderEnum(GenderEnum.MALE);
mTvSex.setText("男");
} else if (which == 1) {
mAccountBean.setGenderEnum(GenderEnum.FEMALE);
mTvSex.setText("女");
} else {
mAccountBean.setGenderEnum(GenderEnum.UNKNOWN);
mTvSex.setText("保密");
}
haveAccountChange = true;
}
dialog.dismiss();
}
}).create().show();
}
9. 设置头像,拍照或选择照片
用LayoutInflater找到dialog_set_head_img.xml布局文件,并实例化
dialog_set_head_img.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white_color"
android:id="@+id/layout_dialog">
<TextView
android:id="@+id/tv_take_photo"
android:layout_width="match_parent"
android:layout_height="45dp"
android:text="@string/dialog_take_photo"
android:textColor="@color/app_black_color"
android:textSize="18sp"
android:gravity="center"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/interval_color"/>
<TextView
android:id="@+id/tv_select_img"
android:layout_width="match_parent"
android:layout_height="45dp"
android:text="@string/dialog_select_photo"
android:textColor="@color/app_black_color"
android:textSize="18sp"
android:gravity="center"/>
</LinearLayout>
(1)为“拍照”控件添加OnClickListener
使用MediaStore.ACTION_IMAGE_CAPTURE打开照相机
设置图片路径,创建图片文件并保存
若出错,显示“启动相机出错!请重试”
(2)为“从相册中选择”控件添加OnClickListener
查询外置内存卡(EXTERNAL_CONTENT_URI)
选择类型为图片
private void setHeadImg() {
View view = LayoutInflater.from(this).inflate(R.layout.dialog_set_head_img, null);
final AlertDialog alertDialog = new AlertDialog.Builder(this).setView(view).create();
TextView take = (TextView) view.findViewById(R.id.tv_take_photo);
TextView select = (TextView) view.findViewById(R.id.tv_select_img);
take.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
alertDialog.dismiss();
try {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
mHeadImgPath = Constant.APP_CACHE_PATH + File.separator + "image"
+ File.separator + mAccountBean.getAccount() + ".jpg";
Uri uri = Uri.fromFile(new File(mHeadImgPath));
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);//设置图像文件名
startActivityForResult(intent, TAKE_PHOTO);
} catch (Exception e) {
ToastUtils.showMessage(AccountInfoActivity.this, "启动相机出错!请重试");
e.printStackTrace();
}
}
});
select.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
alertDialog.dismiss();
Intent intent = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setType("image/*");
startActivityForResult(Intent.createChooser(intent, "选择头像图片"), SELECT_PHOTO);
}
});
alertDialog.show();
}
10. 处理拍照回传数据
获取图片旋转角度
从图像路径获取bitmap,并根据给定的显示宽高对bitmap进行压缩(600x400)
根据给定的角度(bitmapDegree),对bitmap进行旋转
将bitmap保存到本地
显示,记录更新,同步至网易云服务器
private void dealTakePhotoResult() {
Flowable.just(mHeadImgPath)
.map(new Function<String, Bitmap>() {
@Override
public Bitmap apply(String path) throws Exception {
// 调整旋转角度,压缩
int bitmapDegree = ImageUtils.getBitmapDegree(mHeadImgPath);
Bitmap bitmap = ImageUtils.getBitmapFromFile(mHeadImgPath, 600, 400);
bitmap = ImageUtils.rotateBitmapByDegree(bitmap, bitmapDegree);
ImageUtils.saveBitmap2Jpg(bitmap, path);
return bitmap;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Bitmap>() {
@Override
public void accept(Bitmap bitmap) throws Exception {
// 显示,记录更新,同步至网易云服务器
if (bitmap != null) {
// 上传至服务器
uploadHeadImg(bitmap);
}
}
});
}
11. 得到回传数据
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (requestCode == TAKE_PHOTO) {
dealTakePhotoResult();
} else if (requestCode == SELECT_PHOTO) {
mHeadImgPath = ImageUtils.getFilePathFromUri(AccountInfoActivity.this, data.getData());
dealTakePhotoResult();
}
}
}
12. 将头像数据上传至网易云服务器存储,获取服务器返回URL
通过NimClient的getService接口获取到NosService(网易云存储服务)服务实例,调用upload方法上传到网易云服务器存储图像
private void uploadHeadImg(final Bitmap bitmap) {
AbortableFuture<String> upload = NIMClient.getService(NosService.class)
.upload(new File(mHeadImgPath), "image/ipeg");
upload.setCallback(new RequestCallback() {
@Override
public void onSuccess(Object param) {
Log.e(TAG,"uploadHeadImg onSuccess url = " + param.toString());
mIvHead.setImageBitmap(bitmap);
// 保存图片本地路径和服务器路径
mAccountBean.setHeadImgUrl(param.toString());
haveAccountChange = true;
}
@Override
public void onFailed(int code) {
Log.e(TAG,"uploadHeadImg onFailed code " + code);
ToastUtils.showMessage(AccountInfoActivity.this,
"修改失败,头像上传失败,code:" + code);
}
@Override
public void onException(Throwable exception) {
Log.e(TAG,"uploadHeadImg onException message " + exception.getMessage());
ToastUtils.showMessage(AccountInfoActivity.this,
"修改失败,图像上传出错:" + exception.getMessage());
}
});
}
}