Android專案實戰——註冊功能
阿新 • • 發佈:2019-01-07
每款App中必不可少的肯定有註冊功能,而且實現的具體方式各有千秋,為了在以後的開發中提供參考,下面就針對我個人專案中的註冊功能,做一個大概的記錄,其中肯定有不規範的地方,大家可以多提提建議。
同時,還用到了:
2)載入圈控制元件 Rotateloading:在 app目錄下的 build.gradle檔案中,新增依賴compile 'com.victor:lib:1.0.4'
這些工具的配置就不詳細說明了,大家可以檢視詳細文件。
1、註冊介面的佈局 activity_reg.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayoutxmlns: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"> <include layout="@layout/toolbar"/> <EditTextandroid: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"/> <EditText 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"/> <EditText 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"/> <TextView 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"/> <com.facebook.drawee.view.SimpleDraweeView 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" /> <TextView 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"/> <Button android:id="@+id/reg_singup" android:layout_width="match_parent" android:layout_height="40dp" android:layout_margin="30dp" android:text="@string/reg_title" android:textSize="15sp" android:textColor="@color/white" android:background="@drawable/shape_btn" /> </LinearLayout>
其中,toolbar佈局如下,採用白色風格,標題居中
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.AppBarLayout 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.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/colorPrimary"> <!-- 自定義標題 --> <TextView 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"/> </android.support.v7.widget.Toolbar> </android.support.design.widget.AppBarLayout>
這是toolbar的style,在styles.xml檔案中宣告
<style name="toolbar_theme" parent="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> <item name="colorControlNormal">@color/white</item> </style>
還有 com.facebook.drawee.view.SimpleDraweeView,就是圖片載入控制元件,通過設定屬性
app:roundAsCircle="true"
讓它可以顯示圓形圖片
2、建立驗證工具類 VerifyUtil
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; } }
手機號驗證的正則表示式是我結合網上資料和目前的新出號碼寫的,可以驗證2G、3G、部分4G號碼。
3、建立圖片類工具 ImageUtil
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; } } }
儲存完頭像後返回的Uri,是為了方便在佈局中進行頭像載入。
4、自定義載入提示框
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); } }
其中,佈局檔案 loading_dialog.xml 如下
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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"> <include layout="@layout/loading" /> <TextView 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"/> </LinearLayout>
載入圈佈局loading.xml如下:
<?xml version="1.0" encoding="utf-8"?> <com.victor.loading.rotate.RotateLoading 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" />
而且 Dialog 的風格 themeResId 如下,去掉標題欄:
<!-- 自定義 loading dialog --> <style name="loading_dialog" parent="android:style/Theme.Dialog"> <item name="android:windowFrame">@null</item> <item name="android:windowNoTitle">true</item> <item name="android:windowIsFloating">true</item> <item name="android:windowContentOverlay">@null</item> </style>
5、在RegActivity中,實現上傳頭像、註冊使用者
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<UserInfo>() { @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(); } } }
選擇頭像使用Intent操作,主要分兩步:
1)選擇拍照還是從相簿獲取
2)對圖片進行剪裁併儲存到本地
注意:
1)為了防止記憶體洩漏,在 onDestory 方法中要關閉載入提示框
2)RegActivity 使用的是 NoActionBar 的風格
3)剪裁圖片的寬高儘量都設定在 100 以上,因為一些低配手機在剪裁圖片的寬高低於100時,圖片周圍會產生黑邊