編輯:關於Android編程
幾乎每個app都會有登錄注冊的功能,可以看看筆者開發的『南方周末新聞閱讀器』,登錄、手機注冊、忘記密碼這些入口,這些功能在app中要如何來實現呢?這個模塊看似很簡單,但要做好就需要考慮很多細節,比如對用戶的輸入的容錯,操作的提示文案的設定,登錄成功保存用戶信息等等。
上一節的流程圖已經很清晰的展現了登錄注冊的流程,這裡繼續用文字說明一下:
1. 點擊進入個人中心或者需要用戶登錄狀態的操作,先判斷用戶是否已經登錄。
2. 如果已經登錄,則繼續後面的業務,否則,跳轉到登錄頁面進行登錄。
3. 如果已經有賬號,則可以直接登錄,或者可以直接選擇第三方平台授權登錄。
4. 如果未注冊賬號,則需要先進行賬號注冊,注冊成功後再登錄;也可以不注冊賬號,通過第三方平台授權進行登錄。
5. 如果有賬號,但忘記密碼,則需要進行重置密碼,否則直接登錄。
登錄可以使用賬號登錄,現在的app基本上都是手機號碼登錄,注冊的時候也是一個手機對應一個賬號,通過發送驗證碼進行驗證;用戶也可以選擇第三方平台進行登錄,一般會提供微信、QQ、新浪微博這樣的主流社交平台進行授權登錄,這裡筆者使用了友盟的SDK進行實現。
示例代碼:LoginActivity.java
package com.devilwwj.loginandregister;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
import android.widget.Toast;
import com.devilwwj.loginandregister.global.AppConstants;
import com.devilwwj.loginandregister.utils.LogUtils;
import com.devilwwj.loginandregister.utils.ProgressDialogUtils;
import com.devilwwj.loginandregister.utils.RegexUtils;
import com.devilwwj.loginandregister.utils.ShareUtils;
import com.devilwwj.loginandregister.utils.SpUtils;
import com.devilwwj.loginandregister.utils.ToastUtils;
import com.devilwwj.loginandregister.utils.Utils;
import com.devilwwj.loginandregister.views.CleanEditText;
import com.umeng.socialize.bean.SHARE_MEDIA;
import com.umeng.socialize.controller.UMServiceFactory;
import com.umeng.socialize.controller.UMSocialService;
import com.umeng.socialize.controller.listener.SocializeListeners.UMAuthListener;
import com.umeng.socialize.controller.listener.SocializeListeners.UMDataListener;
import com.umeng.socialize.exception.SocializeException;
import java.util.Map;
import static android.view.View.OnClickListener;
/**
* @desc 登錄界面
* Created by devilwwj on 16/1/24.
*/
public class LoginActivity extends Activity implements OnClickListener {
private static final String TAG = "loginActivity";
private static final int REQUEST_CODE_TO_REGISTER = 0x001;
// 界面控件
private CleanEditText accountEdit;
private CleanEditText passwordEdit;
// 第三方平台獲取的訪問token,有效時間,uid
private String accessToken;
private String expires_in;
private String uid;
private String sns;
// 整個平台的Controller,負責管理整個SDK的配置、操作等處理
private UMSocialService mController = UMServiceFactory
.getUMSocialService(AppConstants.DESCRIPTOR);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initViews();
// 配置分享平台
ShareUtils.configPlatforms(this);
}
/**
* 初始化視圖
*/
private void initViews() {
accountEdit = (CleanEditText) this.findViewById(R.id.et_email_phone);
accountEdit.setImeOptions(EditorInfo.IME_ACTION_NEXT);
accountEdit.setTransformationMethod(HideReturnsTransformationMethod
.getInstance());
passwordEdit = (CleanEditText) this.findViewById(R.id.et_password);
passwordEdit.setImeOptions(EditorInfo.IME_ACTION_DONE);
passwordEdit.setImeOptions(EditorInfo.IME_ACTION_GO);
passwordEdit.setTransformationMethod(PasswordTransformationMethod
.getInstance());
passwordEdit.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE
|| actionId == EditorInfo.IME_ACTION_GO) {
clickLogin();
}
return false;
}
});
}
private void clickLogin() {
String account = accountEdit.getText().toString();
String password = passwordEdit.getText().toString();
if (checkInput(account, password)) {
// TODO: 請求服務器登錄賬號
}
}
/**
* 檢查輸入
*
* @param account
* @param password
* @return
*/
public boolean checkInput(String account, String password) {
// 賬號為空時提示
if (account == null || account.trim().equals("")) {
Toast.makeText(this, R.string.tip_account_empty, Toast.LENGTH_LONG)
.show();
} else {
// 賬號不匹配手機號格式(11位數字且以1開頭)
if ( !RegexUtils.checkMobile(account)) {
Toast.makeText(this, R.string.tip_account_regex_not_right,
Toast.LENGTH_LONG).show();
} else if (password == null || password.trim().equals("")) {
Toast.makeText(this, R.string.tip_password_can_not_be_empty,
Toast.LENGTH_LONG).show();
} else {
return true;
}
}
return false;
}
@Override
public void onClick(View v) {
Intent intent = null;
switch (v.getId()) {
case R.id.iv_cancel:
finish();
break;
case R.id.btn_login:
clickLogin();
break;
case R.id.iv_wechat:
clickLoginWexin();
break;
case R.id.iv_qq:
clickLoginQQ();
break;
case R.id.iv_sina:
loginThirdPlatform(SHARE_MEDIA.SINA);
break;
case R.id.tv_create_account:
enterRegister();
break;
case R.id.tv_forget_password:
enterForgetPwd();
break;
default:
break;
}
}
/**
* 點擊使用QQ快速登錄
*/
private void clickLoginQQ() {
if (!Utils.isQQClientAvailable(this)) {
ToastUtils.showShort(LoginActivity.this,
getString(R.string.no_install_qq));
} else {
loginThirdPlatform(SHARE_MEDIA.QZONE);
}
}
/**
* 點擊使用微信登錄
*/
private void clickLoginWexin() {
if (!Utils.isWeixinAvilible(this)) {
ToastUtils.showShort(LoginActivity.this,
getString(R.string.no_install_wechat));
} else {
loginThirdPlatform(SHARE_MEDIA.WEIXIN);
}
}
/**
* 跳轉到忘記密碼
*/
private void enterForgetPwd() {
Intent intent = new Intent(this, ForgetPasswordActivity.class);
startActivity(intent);
}
/**
* 跳轉到注冊頁面
*/
private void enterRegister() {
Intent intent = new Intent(this, SignUpActivity.class);
startActivityForResult(intent, REQUEST_CODE_TO_REGISTER);
}
/**
* 授權。如果授權成功,則獲取用戶信息
*
* @param platform
*/
private void loginThirdPlatform(final SHARE_MEDIA platform) {
mController.doOauthVerify(LoginActivity.this, platform,
new UMAuthListener() {
@Override
public void onStart(SHARE_MEDIA platform) {
LogUtils.i(TAG, "onStart------"
+ Thread.currentThread().getId());
ProgressDialogUtils.getInstance().show(
LoginActivity.this,
getString(R.string.tip_begin_oauth));
}
@Override
public void onError(SocializeException e,
SHARE_MEDIA platform) {
LogUtils.i(TAG, "onError------"
+ Thread.currentThread().getId());
ToastUtils.showShort(LoginActivity.this,
getString(R.string.oauth_fail));
ProgressDialogUtils.getInstance().dismiss();
}
@Override
public void onComplete(Bundle value, SHARE_MEDIA platform) {
LogUtils.i(TAG, "onComplete------" + value.toString());
if (platform == SHARE_MEDIA.SINA) {
accessToken = value.getString("access_key");
} else {
accessToken = value.getString("access_token");
}
expires_in = value.getString("expires_in");
// 獲取uid
uid = value.getString(AppConstants.UID);
if (value != null && !TextUtils.isEmpty(uid)) {
// uid不為空,獲取用戶信息
getUserInfo(platform);
} else {
ToastUtils.showShort(LoginActivity.this,
getString(R.string.oauth_fail));
}
}
@Override
public void onCancel(SHARE_MEDIA platform) {
LogUtils.i(TAG, "onCancel------"
+ Thread.currentThread().getId());
ToastUtils.showShort(LoginActivity.this,
getString(R.string.oauth_cancle));
ProgressDialogUtils.getInstance().dismiss();
}
});
}
/**
* 獲取用戶信息
*
* @param platform
*/
private void getUserInfo(final SHARE_MEDIA platform) {
mController.getPlatformInfo(LoginActivity.this, platform,
new UMDataListener() {
@Override
public void onStart() {
// 開始獲取
LogUtils.i("getUserInfo", "onStart------");
ProgressDialogUtils.getInstance().dismiss();
ProgressDialogUtils.getInstance().show(
LoginActivity.this, "正在請求...");
}
@Override
public void onComplete(int status, Map info) {
try {
String sns_id = "";
String sns_avatar = "";
String sns_loginname = "";
if (info != null && info.size() != 0) {
LogUtils.i("third login", info.toString());
if (platform == SHARE_MEDIA.SINA) { // 新浪微博
sns = AppConstants.SINA;
if (info.get(AppConstants.UID) != null) {
sns_id = info.get(AppConstants.UID)
.toString();
}
if (info.get(AppConstants.PROFILE_IMAGE_URL) != null) {
sns_avatar = info
.get(AppConstants.PROFILE_IMAGE_URL)
.toString();
}
if (info.get(AppConstants.SCREEN_NAME) != null) {
sns_loginname = info.get(
AppConstants.SCREEN_NAME)
.toString();
}
} else if (platform == SHARE_MEDIA.QZONE) { // QQ
sns = AppConstants.QQ;
if (info.get(AppConstants.UID) == null) {
ToastUtils
.showShort(
LoginActivity.this,
getString(R.string.oauth_fail));
return;
}
sns_id = info.get(AppConstants.UID)
.toString();
sns_avatar = info.get(
AppConstants.PROFILE_IMAGE_URL)
.toString();
sns_loginname = info.get(
AppConstants.SCREEN_NAME)
.toString();
} else if (platform == SHARE_MEDIA.WEIXIN) { // 微信
sns = AppConstants.WECHAT;
sns_id = info.get(AppConstants.OPENID)
.toString();
sns_avatar = info.get(
AppConstants.HEADIMG_URL)
.toString();
sns_loginname = info.get(
AppConstants.NICKNAME).toString();
}
// 這裡直接保存第三方返回來的用戶信息
SpUtils.putBoolean(LoginActivity.this,
AppConstants.THIRD_LOGIN, true);
LogUtils.e("info", sns + "," + sns_id + ","
+ sns_loginname);
// TODO: 這裡執行第三方連接(綁定服務器賬號)
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
示例代碼:SignUpActivity.java
package com.devilwwj.loginandregister;
import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.devilwwj.loginandregister.utils.RegexUtils;
import com.devilwwj.loginandregister.utils.ToastUtils;
import com.devilwwj.loginandregister.utils.VerifyCodeManager;
import com.devilwwj.loginandregister.views.CleanEditText;
/**
* @desc 注冊界面
* 功能描述:一般會使用手機登錄,通過獲取手機驗證碼,跟服務器交互完成注冊
* Created by devilwwj on 16/1/24.
*/
public class SignUpActivity extends Activity implements OnClickListener{
private static final String TAG = "SignupActivity";
// 界面控件
private CleanEditText phoneEdit;
private CleanEditText passwordEdit;
private CleanEditText verifyCodeEdit;
private Button getVerifiCodeButton;
private VerifyCodeManager codeManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_signup);
initViews();
codeManager = new VerifyCodeManager(this, phoneEdit, getVerifiCodeButton);
}
/**
* 通用findViewById,減少重復的類型轉換
*
* @param id
* @return
*/
@SuppressWarnings("unchecked")
public final E getView(int id) {
try {
return (E) findViewById(id);
} catch (ClassCastException ex) {
Log.e(TAG, "Could not cast View to concrete class.", ex);
throw ex;
}
}
private void initViews() {
getVerifiCodeButton = getView(R.id.btn_send_verifi_code);
getVerifiCodeButton.setOnClickListener(this);
phoneEdit = getView(R.id.et_phone);
phoneEdit.setImeOptions(EditorInfo.IME_ACTION_NEXT);// 下一步
verifyCodeEdit = getView(R.id.et_verifiCode);
verifyCodeEdit.setImeOptions(EditorInfo.IME_ACTION_NEXT);// 下一步
passwordEdit = getView(R.id.et_password);
passwordEdit.setImeOptions(EditorInfo.IME_ACTION_DONE);
passwordEdit.setImeOptions(EditorInfo.IME_ACTION_GO);
passwordEdit.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
// 點擊虛擬鍵盤的done
if (actionId == EditorInfo.IME_ACTION_DONE
|| actionId == EditorInfo.IME_ACTION_GO) {
commit();
}
return false;
}
});
}
private void commit() {
String phone = phoneEdit.getText().toString().trim();
String password = passwordEdit.getText().toString().trim();
String code = verifyCodeEdit.getText().toString().trim();
if (checkInput(phone, password, code)) {
// TODO:請求服務端注冊賬號
}
}
private boolean checkInput(String phone, String password, String code) {
if (TextUtils.isEmpty(phone)) { // 電話號碼為空
ToastUtils.showShort(this, R.string.tip_phone_can_not_be_empty);
} else {
if (!RegexUtils.checkMobile(phone)) { // 電話號碼格式有誤
ToastUtils.showShort(this, R.string.tip_phone_regex_not_right);
} else if (TextUtils.isEmpty(code)) { // 驗證碼不正確
ToastUtils.showShort(this, R.string.tip_please_input_code);
} else if (password.length() < 6 || password.length() > 32
|| TextUtils.isEmpty(password)) { // 密碼格式
ToastUtils.showShort(this,
R.string.tip_please_input_6_32_password);
} else {
return true;
}
}
return false;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_send_verifi_code:
// TODO 請求接口發送驗證碼
codeManager.getVerifyCode(VerifyCodeManager.REGISTER);
break;
default:
break;
}
}
}
登錄注冊的解決方案,筆者已經做成一個Demo放到github了,大家在實際開發的時候可以參考著根據自身的業務進行調整,但基本上不會差太多,第三方登錄、驗證碼這個都可以選用第三方服務來實現,github地址如下:
登錄注冊解決方案
關於IPC應該不用多介紹了,Android系統中的進程之間不能共享內存,那麼如果兩個不同的應用程序之間需要通訊怎麼辦呢?比如公司的一個項目要更新,產品的需求是依附於當前項
效果圖:源碼:package com.zihao.radar;import android.app.Activity;import android.os.Bundle;i
今年的 I/O 2016 Google 放出了 Android Studio 2.2 的預覽版,透露改進了多項功能,只不過為了保證公司項目不受影響,我一般都不安裝預覽版的
說到黑白,我就想起小時候家裡的黑白電視機,顯示效果比起現在的液晶顯示器不知道差到哪裡去了,但是一放假就看電視依然成為周末必做的是。那麼,在Android中要怎麼實現黑白濾