这个项目的源码地址:https://github.com/yeaper/android_sample/tree/master/Sun
为了方便开发,项目使用了Bmob后端云平台,官网:http://www.bmob.cn/
同时,还用到了:
1)图片加载控件 Fresco:http://www.fresco-cn.org/
2)加载圈控件 Rotateloading:在 app目录下的 build.gradle文件中,添加依赖compile 'com.victor:lib:1.0.4'
3)依赖注入工具 ButterKnife:https://github.com/JakeWharton/butterknife/
这些工具的配置就不详细说明了,大家可以查看详细文档。
xml version="1.0" encoding="utf-8"?>xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/bg_grey"> layout="@layout/toolbar"/> android:id="@+id/reg_account" android:layout_width="match_parent" android:layout_height="40dp" android:singleLine="true" android:background="@drawable/shape_form" android:inputType="number" android:hint="@string/phone_number" android:textSize="16sp" android:textColor="@color/black" android:layout_marginLeft="30dp" android:layout_marginRight="30dp" android:layout_marginTop="30dp" android:layout_gravity="center_vertical"/> android:id="@+id/reg_password" android:layout_width="match_parent" android:layout_height="40dp" android:inputType="textPassword" android:hint="@string/password" android:textSize="16sp" android:textColor="@color/black" android:singleLine="true" android:background="@drawable/shape_form" android:layout_marginLeft="30dp" android:layout_marginRight="30dp" android:layout_marginTop="15dp" android:layout_gravity="center_vertical"/> android:id="@+id/reg_confirm_password" android:layout_width="match_parent" android:layout_height="40dp" android:inputType="textPassword" android:hint="@string/password_confirm" android:textSize="16sp" android:textColor="@color/black" android:singleLine="true" android:background="@drawable/shape_form" android:layout_marginLeft="30dp" android:layout_marginRight="30dp" android:layout_marginTop="15dp" android:layout_gravity="center_vertical"/> android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="30dp" android:layout_marginRight="30dp" android:layout_marginTop="10dp" android:text="@string/reg_upload_header" android:textSize="14sp" android:textColor="@color/deepgrey"/> android:id="@+id/reg_header_view" android:layout_width="72dp" android:layout_height="72dp" app:roundAsCircle="true" android:layout_marginLeft="30dp" android:layout_marginRight="30dp" android:layout_marginTop="10dp" /> android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="30dp" android:layout_marginRight="30dp" android:layout_marginTop="10dp" android:text="@string/reg_tip" android:textSize="14sp" android:textColor="@color/deepgrey"/>
xml version="1.0" encoding="utf-8"?>xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/toolbar_theme"> android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/colorPrimary"> android:id="@+id/toolbar_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textColor="@color/white" android:textSize="18sp"/>
app:roundAsCircle="true"
public class VerifyUtil { /** * 判断手机号码格式 */ public static boolean isMobile(String mobiles) { Pattern p = Pattern.compile("^((1[3,5,8][0-9])|(14[5,7])|(17[0,6,7,8])|(18[0,5-9]))\\d{8}$"); Matcher m = p.matcher(mobiles); return m.matches(); } /** * 检查设备是否存在SDCard * @return */ public static boolean hasSdcard() { String state = Environment.getExternalStorageState(); // 有存储的SDCard return state.equals(Environment.MEDIA_MOUNTED); } /** * 判断网络连接是否正常 * @param context * @return */ public static boolean isConnect(Context context) { // 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理) try { ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); if (connectivity != null) { // 获取网络连接管理的对象 NetworkInfo info = connectivity.getActiveNetworkInfo(); if (info != null&& info.isConnected()) { // 判断当前网络是否已经连接 if (info.getState() == NetworkInfo.State.CONNECTED) { return true; } } } } catch (Exception e) { // TODO: handle exception Log.v("error",e.toString()); } return false; } }
public class ImageUtil { //保存头像到本地 public static Uri saveImage(Bitmap bm, String fileName, String path){ if(VerifyUtil.hasSdcard()){ File foder = new File(path); File headImage = null; try{ if (!foder.exists()) { foder.mkdirs(); } headImage = new File(path, fileName); if (!headImage.exists()) { headImage.createNewFile(); }else { headImage.delete(); headImage.createNewFile(); } BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(headImage)); bm.compress(Bitmap.CompressFormat.JPEG, 80, bos); bos.flush(); bos.close(); }catch (IOException e){ e.printStackTrace(); } return Uri.fromFile(headImage); }else{ return null; } } }
public class LoadingDialog extends Dialog { private Context mContext; private View customView; private RotateLoading loadView; public LoadingDialog(Context context, int themeResId) { super(context, themeResId); this.mContext = context; } /** * 初始化自定义的Dialog布局 * @param msg */ public void initDialog(String msg){ LayoutInflater inflater = LayoutInflater.from(mContext); // 得到加载 view customView = inflater.inflate(R.layout.loading_dialog, null); // 加载圈 loadView = (RotateLoading) customView.findViewById(R.id.rotateLoading); // 提示文字 TextView tip = (TextView) customView.findViewById(R.id.loading_tip); // 加载圈转动 loadView.start(); // 设置提示信息 tip.setText(msg); setContentView(customView); } }
xml version="1.0" encoding="utf-8"?>xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:id="@+id/loading_dialog" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp" android:background="@android:color/transparent"> layout="@layout/loading" /> android:id="@+id/loading_tip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="15sp" android:textColor="@color/grey" android:layout_gravity="center_horizontal" android:layout_marginTop="10dp" android:layout_marginBottom="5dp"/>
xml version="1.0" encoding="utf-8"?>xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/rotateLoading" android:layout_width="70dp" android:layout_height="70dp" android:layout_centerInParent="true" android:layout_gravity="center" app:loading_color="@color/colorAccent" app:loading_speed="11" app:loading_width="5dp" />
public class RegActivity extends AppCompatActivity { ActionBar actionbar; @BindView(R.id.toolbar) Toolbar toolbar; @BindView(R.id.toolbar_title) TextView toolbar_title; @BindView(R.id.reg_account) EditText regAccount; @BindView(R.id.reg_password) EditText regPassword; @BindView(R.id.reg_confirm_password) EditText regConfirmPassword; @BindView(R.id.reg_header_view) SimpleDraweeView headerView; Context c; LoadingDialog loadingDialog; // 是否已保存头像 boolean isSaveImage = false; private String TAG = "RegActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_reg); ButterKnife.bind(this); c = this; initToolbar(); initHeaderView(); initLoadDialog(); } /** * 初始化头部 */ public void initToolbar() { // ToolBar toolbar.setTitleTextColor(getResources().getColor(R.color.white)); // 设置标题 toolbar_title.setText(R.string.reg_title); setSupportActionBar(toolbar); actionbar = getSupportActionBar(); if (actionbar != null) { // 设置返回按钮 actionbar.setDisplayHomeAsUpEnabled(true); // 去掉 ActionBar 自带标题 actionbar.setTitle(null); } } /** * 初始化上传头像的图片 */ public void initHeaderView(){ headerView.setImageURI(Uri.parse("res://com.yyp.sun/" + R.drawable.upload_image)); } /** * 初始化 LoadDialog */ public void initLoadDialog(){ loadingDialog = new LoadingDialog(c, R.style.loading_dialog); // 不能自己取消 loadingDialog.setCancelable(false); loadingDialog.initDialog("注册中..."); } /** * 点击监听 * @param v */ @OnClick({R.id.reg_singup, R.id.reg_header_view}) public void onClick(View v) { switch (v.getId()) { case R.id.reg_singup: if(VerifyUtil.isConnect(c)){ signUp(); }else { ToastUtil.showToast(c, "请检查网络设置"); } break; case R.id.reg_header_view: uploadHeaderView(); break; default: break; } } /** * 上传头像 */ private void uploadHeaderView() { Intent intentFromGallery = new Intent(); // 设置文件类型 intentFromGallery.setType("image/*"); intentFromGallery.setAction(Intent.ACTION_GET_CONTENT); // 进入相册选择图片 startActivityForResult(intentFromGallery, SunInfo.CODE_GALLERY_REQUEST); } /** * 裁剪原始的图片 */ public void cropRawPhoto(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); // outputX , outputY : 裁剪图片宽高 intent.putExtra("outputX", 127); intent.putExtra("outputY", 127); intent.putExtra("return-data", true); // 进入编辑器剪裁图片 startActivityForResult(intent, SunInfo.CODE_RESULT_REQUEST); } /** * 注册 */ public void signUp() { String account = regAccount.getText().toString(); String password = regPassword.getText().toString(); String confirmPsd = regConfirmPassword.getText().toString(); if(TextUtils.isEmpty(account) || TextUtils.isEmpty(password) || TextUtils.isEmpty(confirmPsd)){ ToastUtil.showToast(c, "请仔细填写"); }else{ if (!VerifyUtil.isMobile(account)){ ToastUtil.showToast(c, "手机号无效"); }else { if (!password.equals(confirmPsd)){ ToastUtil.showToast(c, "密码不一致"); }else{ if(password.length() <= 7){ ToastUtil.showToast(c, "密码不安全"); }else { if(!isSaveImage){ ToastUtil.showToast(c, "请重新选取头像"); }else{ // 显示加载圈 loadingDialog.show(); final UserInfo user = new UserInfo(); user.setUsername(account); user.setPassword(password); user.setSex("男"); user.setMobilePhoneNumber(account); user.setMobilePhoneNumberVerified(true); // 上传头像 final BmobFile bmobFile = new BmobFile(new File(SunInfo.BASE_FILE_URL+SunInfo.HEAD_IMAGE_URL+SunInfo.HEAD_IMAGE_NAME)); bmobFile.uploadblock(new UploadFileListener() { @Override public void done(BmobException e) { if(e==null){ ToastUtil.showToast(c, "头像上传成功"); // bmobFile.getFileUrl()--返回的上传文件的完整地址 user.setAvatarUrl(bmobFile.getFileUrl()); user.setAvatarName(bmobFile.getFilename()); // 注册 user.signUp(new SaveListener() { @Override public void done(UserInfo userInfo, BmobException e) { if(e == null){ ToastUtil.showToast(c, "注册成功"); Intent goLogin = new Intent(c, LoginActivity.class); startActivity(goLogin); finish(); }else { loadingDialog.dismiss(); ToastUtil.showToast(c, "注册失败"); } } }); }else{ loadingDialog.dismiss(); Log.e(TAG, "头像上传失败"+e.getMessage()); } } @Override public void onProgress(Integer value) { // 返回的上传进度(百分比) } }); } } } } } } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: //监听返回按钮 finish(); break; default: break; } return super.onOptionsItemSelected(item); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { // 用户没有进行有效的操作,直接返回 if (resultCode == RESULT_CANCELED) { return; } switch (requestCode) { case SunInfo.CODE_GALLERY_REQUEST: // 剪裁图片 cropRawPhoto(intent.getData()); break; case SunInfo.CODE_RESULT_REQUEST: if (intent != null) { // 图片拿到后,先保存到本地,再进行设置 Bitmap bitmap = intent.getExtras().getParcelable("data"); Uri uri = ImageUtil.saveImage(bitmap, SunInfo.HEAD_IMAGE_NAME, SunInfo.BASE_FILE_URL+SunInfo.HEAD_IMAGE_URL); // 判断是否保存了头像 if(uri == null){ isSaveImage = false; }else{ isSaveImage = true; headerView.setImageURI(uri); } } break; } super.onActivityResult(requestCode, resultCode, intent); } @Override protected void onDestroy() { super.onDestroy(); if(loadingDialog != null){ loadingDialog.dismiss(); } } }
1)选择拍照还是从相册获取
2)对图片进行剪裁并保存到本地
注意:
1)为了防止内存泄漏,在 onDestory 方法中要关闭加载提示框
2)RegActivity 使用的是 NoActionBar 的风格
3)剪裁图片的宽高尽量都设置在 100 以上,因为一些低配手机在剪裁图片的宽高低于100时,图片周围会产生黑边