1. 程式人生 > >Android專案實戰——註冊功能

Android專案實戰——註冊功能

每款App中必不可少的肯定有註冊功能,而且實現的具體方式各有千秋,為了在以後的開發中提供參考,下面就針對我個人專案中的註冊功能,做一個大概的記錄,其中肯定有不規範的地方,大家可以多提提建議。


同時,還用到了:

2)載入圈控制元件 Rotateloading:在 app目錄下的 build.gradle檔案中,新增依賴compile 'com.victor:lib:1.0.4'

這些工具的配置就不詳細說明了,大家可以檢視詳細文件。

1、註冊介面的佈局 activity_reg.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
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"> <include layout="@layout/toolbar"/> <EditText
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"/> <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時,圖片周圍會產生黑邊

6、下面看看實現的效果

以上就是整體過程,謝謝!