簡介
隨著互聯(lián)網(wǎng)的發(fā)展不光手機配置越來越高辣卒,app產(chǎn)品要求也越來越高了≈兰郑現(xiàn)在做APP時產(chǎn)品都會讓我們加上 多語言、指紋登錄荣茫、手勢登錄等功能想帅。下面代碼完全適配Android8.0
其它文章
OkHttp3簡單使用和封裝使用
Retrofit2.0+RxJava2.0封裝使用
Android使用IconFont阿里矢量圖標
Android Studio 使用SVN 主干和分支合并代碼
項目地址:https://github.com/pengjunshan/LanguageFingerprintGesture
效果圖
多語言
指紋登錄
手勢登錄
多語言
多語言可以實現(xiàn)國際化,但是并不是app上所有的地方都是用國際化語言啡莉,這個根據(jù)實際需求來做港准。這里做的是英語旨剥,適配8.0。
1.改變app語言環(huán)境后需要重新加載資源文件
2.改變app語言環(huán)境后殺死進程重啟仍是改變后的語言
1.第一步
首先在res資源文件下創(chuàng)建一個英語資源文件浅缸,res右鍵>New>Android Resource Directory(進入第二步)
2.第二步
選擇Locale 然后點擊箭頭選擇語言(進入第三步)
3.第三步
滑動Language滾動條可以查看所有支持的語言轨帜,然后選擇en:English,點擊OK(進入第四步)
4.第四步
進行完第三步后可以看到res文件夾下多了一個values-en文件夾衩椒,這個就是我們剛創(chuàng)建的英語資源文件蚌父,然后我們在此右鍵創(chuàng)建一個strings文件來存儲英語資源。(名字一定要是strings)
5.第五步
把需要做國際化的資源文件在英語strings.xml中放入響應的資源毛萌。
大家看到中文中的資源比英文中的多苟弛,中文里會報錯提示英文資源文件中少了這個資源,只是警告不影響編譯阁将。
6.第六步
然后開始核心代碼膏秫,布局中使用兩個RadioButton來選擇中文||英文。一個保存按鈕來修改語言設置和保存到本地標識做盅。
每次進入選擇語言界面時先判斷當前使用的是什么語言來設置哪個RadioButton為true荔睹。
修改語言后一定要重新加載資源,跳到主頁面設置Flage為Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK殺死所有activity重新打開主頁面 資源也就會重新加載
package com.kelin.languagefingerprintgesture;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.AppCompatButton;
import android.text.TextUtils;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import com.kelin.languagefingerprintgesture.utils.LocaleManager;
import com.kelin.languagefingerprintgesture.utils.PreferenceUtils;
/**
* @author:PengJunShan. 時間:On 2019-04-22.
*
* 描述:切換多語言
*/
public class LanguageActivity extends AppCompatActivity {
@BindView(R.id.rbChinese)
RadioButton rbChinese;
@BindView(R.id.rbEnglish)
RadioButton rbEnglish;
@BindView(R.id.rgLanguages)
RadioGroup rgLanguages;
@BindView(R.id.commit)
AppCompatButton commit;
private String mLanguageType;
private boolean languageType;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_language);
ButterKnife.bind(this);
initView();
}
private void initView() {
/**
* 初始化判斷使用的是什么語言
*/
if (PreferenceUtils.getBoolean("isChinese", true)) {
rbChinese.setChecked(true);
} else {
rbEnglish.setChecked(true);
}
/**
* 監(jiān)聽RadioGroup
*/
rgLanguages.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (checkedId) {
case R.id.rbChinese:
mLanguageType = LocaleManager.LANGUAGE_CHINESE;
languageType = true;
break;
case R.id.rbEnglish:
mLanguageType = LocaleManager.LANGUAGE_ENGLISH;
languageType = false;
break;
}
}
});
}
@OnClick(R.id.commit)
public void onViewClicked() {
if (!TextUtils.isEmpty(mLanguageType)) {
/**
* 修改語言
*/
LocaleManager.setNewLocale(LanguageActivity.this, mLanguageType);
/**
* 保存使用語言標識
*/
PreferenceUtils.commitBoolean("isChinese", languageType);
/**
* 跳轉到主頁 殺死其它所有的頁面 重新加載資源文件
*/
Intent i = new Intent(LanguageActivity.this, MainActivity.class);
startActivity(i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK));
finish();
}
}
}
在BaseActivity中重寫 @Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(LocaleManager.setLocale(base));
}
所有Activity都要繼承BaseActivity每次初始化時都會設置Locale
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(LocaleManager.setLocale(base));
}
每次運行app時都要初始化語言設置Locale言蛇,一般寫在自定義的Application中。
package com.kelin.languagefingerprintgesture.base;
import android.app.Application;
import android.content.Context;
import android.content.res.Configuration;
import com.kelin.languagefingerprintgesture.utils.LocaleManager;
import com.kelin.languagefingerprintgesture.utils.PreferenceUtils;
/**
* 作者:PengJunShan.
*
* 時間:On 2019-04-22.
*
* 描述:
*/
public class MyApplication extends Application {
public static Context context;
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
/**
* 初始化SP
*/
PreferenceUtils.init(context, "TRIP");
/**
* 初始化語言
*/
LocaleManager.setLocale(this);
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(LocaleManager.setLocale(base));
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
LocaleManager.setLocale(this);
}
}
核心類LocaleManager類宵距,主要是更新Locale腊尚,存取語言標識。代碼中都有注釋满哪。
這里要說下8.0適配婿斥,剛開始寫的時候app運行在8.0以下沒有問題,8.0則不成功哨鸭。在android o上民宿,google改變了locale的規(guī)則。之前只存在一個locale像鸡,而后面是可以支持一個locale list活鹰。代碼中時修改后的。
package com.kelin.languagefingerprintgesture.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.LocaleList;
import java.util.Locale;
public class LocaleManager {
/**
* 中文
*/
public static final String LANGUAGE_CHINESE = "zh";
/**
* 英文
*/
public static final String LANGUAGE_ENGLISH = "en";
/**
* 初始化語言設置
*/
public static Context setLocale(Context c) {
return updateResources(c, getLanguage());
}
/**
* 設置語言
* @param c
* @param language
* @return
*/
public static Context setNewLocale(Context c, String language) {
persistLanguage(language);
return updateResources(c, language);
}
/**
* 得到語言設置
* @return
*/
public static String getLanguage() {
return PreferenceUtils.getString(Constants.LANGUAGE_KEY, LANGUAGE_CHINESE);
}
/**
* 存儲設置的語言
* @param language
*/
@SuppressLint("ApplySharedPref")
private static void persistLanguage(String language) {
PreferenceUtils.commitString(Constants.LANGUAGE_KEY, language);
}
/**
* 更新Locale
* 適配8.0
* @param language
* @return
*/
private static Context updateResources(Context context, String language) {
Locale locale = new Locale(language);
Locale.setDefault(locale);
Resources res = context.getResources();
Configuration configuration = res.getConfiguration();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
configuration.setLocale(locale);
LocaleList localeList = new LocaleList(locale);
LocaleList.setDefault(localeList);
configuration.setLocales(localeList);
context = context.createConfigurationContext(configuration);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
configuration.setLocale(locale);
context = context.createConfigurationContext(configuration);
}
return context;
}
}
指紋登錄
指紋登錄可以實現(xiàn)快捷登錄只估,在Android6.0谷歌才提供統(tǒng)一指紋SDK接口志群,在6.0之前都是各個廠商自定義。
1.開啟指紋登錄時需驗證你的指紋
2.關閉指紋登錄時需彈出對話框確認
之前實現(xiàn)指紋解鎖都是用的FingerprintManager類蛔钙,F(xiàn)ingerprintManager在最新的Android 9.0系統(tǒng)上已經(jīng)被廢棄了锌云,當Google在v4包中把FingerprintManager改為了FingerprintManagerCompat,而Compat是兼容的意思吁脱,所以Google在v4包中做了一些兼容性處理桑涎,官方推薦使用后者彬向。所以本demo用的就是FingerprintManagerCompat工具類。
FingerprintManagerCompat: 指紋管理工具類
FingerprintManagerCompat.AuthenticationCallback :使用驗證的時候傳入該接口攻冷,通過該接口進行驗證結果回調
FingerprintManagerCompat.CryptoObject: FingerprintManagerCompat支持的分裝加密對象的類
申請權限
<!-- 指紋權限 -->
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
首先判斷當前手機設備是否支持指紋解鎖娃胆,然后判斷是否添加過指紋至少添加一個指紋。
FingerprintManagerCompat提供了三個方法:
- isHardwareDetected() 判斷是否有硬件支持
- isKeyguardSecure() 判斷是否設置鎖屏讲衫,因為一個手機最少要有兩種登錄方式
- hasEnrolledFingerprints() 判斷系統(tǒng)中是否添加至少一個指紋
/**
*判斷是否支持指紋識別
*/
public static boolean supportFingerprint(Context mContext) {
if (VERSION.SDK_INT < 23) {
// MyToast.showToast("您的系統(tǒng)版本過低缕棵,不支持指紋功能");
return false;
} else {
KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class);
FingerprintManagerCompat fingerprintManager = FingerprintManagerCompat.from(mContext);
if (!fingerprintManager.isHardwareDetected()) {
MyToast.showToast("您的手機不支持指紋功能");
return false;
} else if (!keyguardManager.isKeyguardSecure()) {
MyToast.showToast("您還未設置鎖屏,請先設置鎖屏并添加一個指紋");
return false;
} else if (!fingerprintManager.hasEnrolledFingerprints()) {
MyToast.showToast("您至少需要在系統(tǒng)設置中添加一個指紋");
return false;
}
}
return true;
}
生成一個對稱加密的key (下載demo在ToolUtils中)
@TargetApi(23)
public static void initKey() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyGenerator keyGenerator = KeyGenerator
.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
Builder builder = new Builder(DEFAULT_KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
keyGenerator.init(builder.build());
keyGenerator.generateKey();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
生成一個Cipher對象,(下載demo在ToolUtils中)
@TargetApi(23)
public static Cipher initCipher() {
try {
SecretKey key = (SecretKey) keyStore.getKey(DEFAULT_KEY_NAME, null);
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
cipher.init(Cipher.ENCRYPT_MODE, key);
} catch (Exception e) {
throw new RuntimeException(e);
}
return cipher;
}
創(chuàng)建FingerprintManagerCompat指紋管理工具類涉兽,谷歌已經(jīng)不推薦使用FingerprintManager類招驴。
FingerprintManagerCompat fingerprintManagerCompat = FingerprintManagerCompat.from(mActivity);
拿到FingerprintManagerCompat對象后就可以調authenticate方法進行指紋識別了,先看下官網(wǎng)給的參數(shù)枷畏。
- CryptoObject這是一個加密類的對象别厘,指紋掃描器會使用這個對象來判斷認證結果的合法性。這個對象可以是null拥诡,但是這樣的話触趴,就意味這app無條件信任認證的結果,雖然從理論上這個過程可能被攻擊渴肉,數(shù)據(jù)可以被篡改冗懦,這是app在這種情況下必須承擔的風險。因此仇祭,建議這個參數(shù)不要置為null披蕉。
FingerprintManagerCompat.CryptoObject cryptoObject = new FingerprintManagerCompat.CryptoObject(cipher);
- flags 標識位,根據(jù)上圖的文檔描述乌奇,這個位暫時應該為0没讲,這個標志位應該是保留將來使用的。我們傳0
- cancel這個是CancellationSignal類的一個對象礁苗,這個對象用來在指紋識別器掃描用戶指紋的是時候取消當前的掃描操作爬凑,如果不取消的話,那么指紋掃描器會移植掃描直到超時(一般為30s试伙,取決于具體的廠商實現(xiàn))嘁信,這樣的話就會比較耗電。建議這個參數(shù)不要置為null疏叨。識別過程中可以手動取消指紋識別
CancellationSignal mCancellationSignal = new CancellationSignal();
//識別過程中可以手動取消指紋識別
// mCancellationSignal.cancel();
- callback這個是FingerprintManagerCompat.AuthenticationCallback類的對象吱抚,這個是這個接口中除了第一個參數(shù)之外最重要的參數(shù)了,稍后我們詳細來介紹考廉。這個參數(shù)不能為NULL秘豹。
public class MyCallBack extends FingerprintManagerCompat.AuthenticationCallback {
// 當出現(xiàn)錯誤的時候回調此函數(shù),比如多次嘗試都失敗了的時候昌粤,errString是錯誤信息
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
if (!isSelfCancelled) {
errorMsg.setText(errString);
Log.e("TAG", "errMsgId="+errMsgId);
Toast.makeText(mActivity, "errMsgId="+errMsgId, Toast.LENGTH_SHORT).show();
if (errMsgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT) {
Log.e("TAG", ""+errString);
dismiss();
}
}
}
// 當指紋驗證失敗的時候會回調此函數(shù)既绕,失敗之后允許多次嘗試啄刹,失敗次數(shù)過多會停止響應一段時間然后再停止sensor的工作
@Override
public void onAuthenticationFailed() {
errorMsg.setText("指紋認證失敗,請再試一次");
Log.e("TAG", "onAuthenticationFailed");
}
//錯誤時提示幫助凄贩,比如說指紋錯誤誓军,我們將顯示在界面上 讓用戶知道情況
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
errorMsg.setText(helpString);
Log.e("TAG", "helpString="+helpString);
}
// 當驗證的指紋成功時會回調此函數(shù),然后不再監(jiān)聽指紋sensor
@Override
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
if(onFingerprintSetting!=null) {
onFingerprintSetting.onFingerprint(true);
}
dismiss();
}
}
- handler 這是Handler類的對象疲扎,如果這個參數(shù)不為null的話昵时,那么FingerprintManagerCompat將會使用這個handler中的looper來處理來自指紋識別硬件的消息。通常來講椒丧,開發(fā)這不用提供這個參數(shù)壹甥,可以直接置為null,因為FingerprintManagerCompat會默認使用app的main looper來處理壶熏。我們傳null
這里就要介紹的是上面提到的FingerprintManagerCompat.AuthenticationCallback了句柠,因為掃描指紋和認證的過程都是在另外一個進程中完成的,所以我們需要采取異步的方式棒假,等操作完成之后溯职,讓系統(tǒng)回調給我們,回調方法就是AuthenticationCallback類中的4個方法了
- OnAuthenticationError(int errorCode, ICharSequence errString) 這個接口會再系統(tǒng)指紋認證出現(xiàn)不可恢復的錯誤的時候才會調用帽哑,當出現(xiàn)錯誤的時候回調此函數(shù)谜酒,比如多次嘗試都失敗了的時候,errString是錯誤信息并且參數(shù)errorCode就給出了錯誤碼妻枕。
- OnAuthenticationFailed()這個接口會在系統(tǒng)指紋認證失敗的情況的下才會回調僻族。注意這里的認證失敗和上面的認證錯誤是不一樣的,認證失敗是指所有的信息都采集完整佳头,并且沒有任何異常,但是這個指紋和之前注冊的指紋是不相符的晴氨;但是認證錯誤是指在采集或者認證的過程中出現(xiàn)了錯誤康嘉,比如指紋傳感器工作異常等。也就是說認證失敗是一個可以預期的正常情況籽前,而認證錯誤是不可預期的異常情況亭珍。
- OnAuthenticationHelp(int helpMsgId, ICharSequence helpString) 錯誤時提示幫助,比如說指紋錯誤枝哄,我們將顯示在界面上 讓用戶知道情況,給出了helpString錯誤信息肄梨。
+OnAuthenticationSucceeded(FingerprintManagerCompati.AuthenticationResult result)這個接口會在認證成功之后回調。我們可以在這個方法中提示用戶認證成功挠锥,然后不再監(jiān)聽指紋sensor众羡。
手勢解鎖
手勢登錄就是保存手勢設置的密碼,解鎖時進行對比蓖租。
我的這個手勢解鎖是用的別人寫好的我拿來稍稍修改一下粱侣,大神項目連接GestureLockView
我的demo中使用思路:
1.設置手勢密碼時羊壹,兩次手勢不同時手勢路徑變紅+震動,可以重置齐婴。
2.修改密碼時油猫,需驗證是否是本人。
3.解鎖時柠偶,手勢錯誤時手勢路徑變紅+震動情妖。
震動權限
<!-- 震動權限 -->
<uses-permission android:name="android.permission.VIBRATE"/>
設置手勢密碼布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
>
<!--設置手勢解鎖時提示view-->
<com.kelin.languagefingerprintgesture.gesture.GestureLockDisplayView
android:id="@+id/display_view"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
<TextView
android:id="@+id/setting_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:text="繪制解鎖圖案"
app:layout_constraintTop_toBottomOf="@+id/display_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
<TextView
android:id="@+id/hintTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="與第一次設置密碼不同,請再次設置"
android:textColor="#FC6265"
android:textSize="14sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/setting_hint"
android:layout_marginTop="10dp"
android:visibility="invisible"
/>
<!--手勢解鎖view-->
<com.kelin.languagefingerprintgesture.gesture.GestureLockLayout
android:id="@+id/gesture_view"
android:layout_width="300dp"
android:layout_height="300dp"
app:layout_constraintTop_toBottomOf="@+id/hintTV"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
<TextView
android:id="@+id/reSet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="重新設置"
app:layout_constraintTop_toBottomOf="@+id/gesture_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:textSize="16sp"
android:textColor="#333333"
android:layout_marginTop="10dp"
/>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
設置手勢密碼诱担,activity中初始化控件毡证,監(jiān)聽手勢密碼
package com.kelin.languagefingerprintgesture;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import com.kelin.languagefingerprintgesture.gesture.GestureLockDisplayView;
import com.kelin.languagefingerprintgesture.gesture.GestureLockLayout;
import com.kelin.languagefingerprintgesture.gesture.GestureLockLayout.OnLockResetListener;
import com.kelin.languagefingerprintgesture.utils.Constants;
import com.kelin.languagefingerprintgesture.utils.PreferenceUtils;
import com.kelin.languagefingerprintgesture.utils.ToolUtils;
import java.util.ArrayList;
import java.util.List;
/**
* @author:PengJunShan.
* 時間:On 2019-04-22.
* 描述:手勢登錄
*/
public class SetGestureLockActivity extends AppCompatActivity {
@BindView(R.id.display_view)
GestureLockDisplayView mLockDisplayView;
@BindView(R.id.setting_hint)
TextView mSettingHintText;
@BindView(R.id.gesture_view)
GestureLockLayout mGestureLockLayout;
@BindView(R.id.reSet)
TextView reSet;
@BindView(R.id.hintTV)
TextView hintTV;
private Animation animation;
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gesture_login);
ButterKnife.bind(this);
initView();
}
private void initView() {
mContext = this;
//設置提示view 每行每列點的個數(shù)
mLockDisplayView.setDotCount(3);
//設置提示view 選中狀態(tài)的顏色
mLockDisplayView.setDotSelectedColor(Color.parseColor("#01367A"));
//設置提示view 非選中狀態(tài)的顏色
mLockDisplayView.setDotUnSelectedColor(Color.parseColor("#999999"));
//設置手勢解鎖view 每行每列點的個數(shù)
mGestureLockLayout.setDotCount(3);
//設置手勢解鎖view 最少連接數(shù)
mGestureLockLayout.setMinCount(4);
//設置手勢解鎖view 模式為重置密碼模式
mGestureLockLayout.setMode(GestureLockLayout.RESET_MODE);
//初始化動畫
animation = AnimationUtils.loadAnimation(this, R.anim.shake);
initEvents();
}
private void initEvents() {
mGestureLockLayout.setOnLockResetListener(new OnLockResetListener() {
@Override
public void onConnectCountUnmatched(int connectCount, int minCount) {
//連接數(shù)小于最小連接數(shù)時調用
mSettingHintText.setText("最少連接" + minCount + "個點");
resetGesture();
}
@Override
public void onFirstPasswordFinished(List<Integer> answerList) {
//第一次繪制手勢成功時調用
Log.e("TAG","第一次密碼=" + answerList);
mSettingHintText.setText("確認解鎖圖案");
//將答案設置給提示view
mLockDisplayView.setAnswer(answerList);
//重置
resetGesture();
}
@Override
public void onSetPasswordFinished(boolean isMatched, List<Integer> answerList) {
//第二次密碼繪制成功時調用
Log.e("TAG","第二次密碼=" + answerList.toString());
if (isMatched) {
//兩次答案一致,保存
PreferenceUtils.commitString(Constants.GESTURELOCK_KEY, answerList.toString());
setResult(RESULT_OK);
finish();
} else {
hintTV.setVisibility(View.VISIBLE);
ToolUtils.setVibrate(mContext);
hintTV.startAnimation(animation);
mGestureLockLayout.startAnimation(animation);
resetGesture();
}
}
});
}
/**
* 重置手勢布局(只是布局)
*/
private void resetGesture() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mGestureLockLayout.resetGesture();
}
}, 300);
}
/**
* 重置手勢布局(布局加邏輯)
*/
@OnClick(R.id.reSet)
public void onViewClicked() {
mGestureLockLayout.setOnLockResetListener(null);
mSettingHintText.setText("繪制解鎖圖案");
mLockDisplayView.setAnswer(new ArrayList<Integer>());
mGestureLockLayout.resetGesture();
mGestureLockLayout.setMode(GestureLockLayout.RESET_MODE);
hintTV.setVisibility(View.INVISIBLE);
initEvents();
}
}
錄制GIF
手勢解鎖+指紋解鎖布局该肴,一般解鎖時手勢和指紋在一個activity中顯示情竹。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
>
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="152****2877 下午好"
android:textColor="#333"
android:textStyle="bold"
android:textSize="18sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:visibility="invisible"
/>
<TextView
android:id="@+id/hintTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="您還有5次機會"
android:textColor="#FC6265"
android:textSize="14sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/name"
android:layout_marginTop="30dp"
android:visibility="invisible"
/>
<com.kelin.languagefingerprintgesture.gesture.GestureLockLayout
android:id="@+id/gestureLock"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@+id/hintTV"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
<android.support.constraint.Group
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="name,gestureLock"
android:visibility="visible"
/>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
手勢解鎖 activity中初始化控件 監(jiān)聽手勢信息
package com.kelin.languagefingerprintgesture;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.support.constraint.Group;
import android.text.TextUtils;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.kelin.languagefingerprintgesture.FingerprintDialogFragment.OnFingerprintSetting;
import com.kelin.languagefingerprintgesture.base.BaseActivity;
import com.kelin.languagefingerprintgesture.gesture.GestureLockLayout;
import com.kelin.languagefingerprintgesture.gesture.GestureLockLayout.OnLockVerifyListener;
import com.kelin.languagefingerprintgesture.utils.Constants;
import com.kelin.languagefingerprintgesture.utils.MyToast;
import com.kelin.languagefingerprintgesture.utils.PreferenceUtils;
import com.kelin.languagefingerprintgesture.utils.ToolUtils;
import javax.crypto.Cipher;
/**
* @author:PengJunShan.
* 時間:On 2019-04-26.
* 描述:解鎖
*/
public class GestureLockActivity extends BaseActivity {
@BindView(R.id.gestureLock)
GestureLockLayout mGestureLockLayout;
@BindView(R.id.hintTV)
TextView hintTV;
@BindView(R.id.name)
TextView name;
@BindView(R.id.group)
Group group;
private Context mContext;
private Animation animation;
/**
* 最大解鎖次數(shù)
*/
private int mNumber = 5;
/**
* change:修改手勢 login:登錄
*/
private String type;
/**
* true:設置 false:未設置
*/
private Boolean isFingerprint, isGesture;
private FingerprintDialogFragment dialogFragment;
private Cipher cipher;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gesture_lock);
ButterKnife.bind(this);
initView();
}
protected void initView() {
mContext = this;
type = getIntent().getStringExtra("type");
isGesture = PreferenceUtils.getBoolean(Constants.ISGESTURELOCK_KEY, false);
isFingerprint = PreferenceUtils.getBoolean(Constants.ISFINGERPRINT_KEY, false);
if (isGesture) {
group.setVisibility(View.VISIBLE);
setGestureListener();
}
if ("login".equals(type) && isFingerprint) {
setFingerprint();
}
}
private void setFingerprint() {
if (ToolUtils.supportFingerprint(this)) {
ToolUtils.initKey(); //生成一個對稱加密的key
//生成一個Cipher對象
cipher = ToolUtils.initCipher();
}
if (cipher != null) {
showFingerPrintDialog(cipher);
}
}
private void showFingerPrintDialog(Cipher cipher) {
dialogFragment = new FingerprintDialogFragment();
dialogFragment.setCipher(cipher);
dialogFragment.show(getSupportFragmentManager(), "fingerprint");
dialogFragment.setOnFingerprintSetting(new OnFingerprintSetting() {
@Override
public void onFingerprint(boolean isSucceed) {
if (isSucceed) {
MyToast.showToastLong("指紋解鎖成功!");
startActivity(MainActivity.class);
finish();
} else {
MyToast.showToastLong("指紋解鎖失斣群濉秦效!");
}
}
});
}
private void setGestureListener() {
String gestureLockPwd = PreferenceUtils.getString(Constants.GESTURELOCK_KEY, "");
if (!TextUtils.isEmpty(gestureLockPwd)) {
mGestureLockLayout.setAnswer(gestureLockPwd);
} else {
MyToast.showToast("沒有設置過手勢密碼");
}
mGestureLockLayout.setDotCount(3);
mGestureLockLayout.setMode(GestureLockLayout.VERIFY_MODE);
//設置手勢解鎖最大嘗試次數(shù) 默認 5
mGestureLockLayout.setTryTimes(5);
animation = AnimationUtils.loadAnimation(this, R.anim.shake);
mGestureLockLayout.setOnLockVerifyListener(new OnLockVerifyListener() {
@Override
public void onGestureSelected(int id) {
//每選中一個點時調用
}
@Override
public void onGestureFinished(boolean isMatched) {
//繪制手勢解鎖完成時調用
if (isMatched) {
if ("change".equals(type)) {
startActivity(SetGestureLockActivity.class);
} else if ("login".equals(type)) {
startActivity(MainActivity.class);
}
finish();
} else {
hintTV.setVisibility(View.VISIBLE);
mNumber = --mNumber;
hintTV.setText("你還有" + mNumber + "次機會");
hintTV.startAnimation(animation);
mGestureLockLayout.startAnimation(animation);
ToolUtils.setVibrate(mContext);
}
resetGesture();
}
@Override
public void onGestureTryTimesBoundary() {
//超出最大嘗試次數(shù)時調用
mGestureLockLayout.setTouchable(false);
}
});
}
/**
* 重置手勢布局(只是布局)
*/
private void resetGesture() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mGestureLockLayout.resetGesture();
}
}, 300);
}
}
項目地址:https://github.com/pengjunshan/LanguageFingerprintGesture