Android開發(fā) 多語言六水、指紋登錄、手勢登錄

簡介

隨著互聯(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

效果圖

多語言
多語言.gif
指紋登錄
指紋登錄.gif
手勢登錄
手勢登錄.gif
多語言

多語言可以實現(xiàn)國際化,但是并不是app上所有的地方都是用國際化語言啡莉,這個根據(jù)實際需求來做港准。這里做的是英語旨剥,適配8.0
1.改變app語言環(huán)境后需要重新加載資源文件
2.改變app語言環(huán)境后殺死進程重啟仍是改變后的語言

1.第一步

首先在res資源文件下創(chuàng)建一個英語資源文件浅缸,res右鍵>New>Android Resource Directory(進入第二步)

第一步.png
2.第二步

選擇Locale 然后點擊箭頭選擇語言(進入第三步)

第二步.png
3.第三步

滑動Language滾動條可以查看所有支持的語言轨帜,然后選擇en:English,點擊OK(進入第四步)

第三步.png
4.第四步

進行完第三步后可以看到res文件夾下多了一個values-en文件夾衩椒,這個就是我們剛創(chuàng)建的英語資源文件蚌父,然后我們在此右鍵創(chuàng)建一個strings文件來存儲英語資源。(名字一定要是strings)

第四步
5.第五步

把需要做國際化的資源文件在英語strings.xml中放入響應的資源毛萌。
大家看到中文中的資源比英文中的多苟弛,中文里會報錯提示英文資源文件中少了這個資源,只是警告不影響編譯阁将。

資源文件.png
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ù)枷畏。

圖片.png
  • 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

點擊下方喜歡收藏,萬一后期項目用得到呢
謝謝進入我的博客涎嚼,請多多點評
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末阱州,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子法梯,更是在濱河造成了極大的恐慌苔货,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件立哑,死亡現(xiàn)場離奇詭異夜惭,居然都是意外死亡,警方通過查閱死者的電腦和手機铛绰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門诈茧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人捂掰,你說我怎么就攤上這事敢会。” “怎么了这嚣?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵鸥昏,是天一觀的道長。 經(jīng)常有香客問我姐帚,道長吏垮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮惫皱,結果婚禮上像樊,老公的妹妹穿的比我還像新娘。我一直安慰自己旅敷,他們只是感情好生棍,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著媳谁,像睡著了一般涂滴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上晴音,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天柔纵,我揣著相機與錄音,去河邊找鬼锤躁。 笑死搁料,一個胖子當著我的面吹牛,可吹牛的內容都是我干的系羞。 我是一名探鬼主播郭计,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼椒振!你這毒婦竟也來了昭伸?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤澎迎,失蹤者是張志新(化名)和其女友劉穎础嫡,沒想到半個月后诵冒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡香罐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年赫舒,在試婚紗的時候發(fā)現(xiàn)自己被綠了阶捆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片双肤。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡排苍,死狀恐怖,靈堂內的尸體忽然破棺而出袁铐,到底是詐尸還是另有隱情揭蜒,我是刑警寧澤横浑,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布剔桨,位于F島的核電站,受9級特大地震影響徙融,放射性物質發(fā)生泄漏洒缀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望树绩。 院中可真熱鬧萨脑,春花似錦、人聲如沸饺饭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘫俊。三九已至鹊杖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間扛芽,已是汗流浹背骂蓖。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留川尖,地道東北人登下。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像叮喳,于是被迫代替她去往敵國和親被芳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容

  • 好久沒寫文章了嘲更,最近也比較偷懶筐钟,今天繼續(xù)討論我實際開發(fā)中遇到的需求,那就是關于APP解鎖赋朦,大家都知道÷ǔ澹現(xiàn)在越來越多...
    青蛙要fly閱讀 3,051評論 2 26
  • 最近項目中添加了一個新的登錄方式:指紋識別。好巧不巧這塊分給了我宠哄,然后一頓研究壹将。下面想把一些實現(xiàn)的代碼貼出來做個筆...
    SHERLOCKvv閱讀 23,689評論 6 28
  • 搬家之難兮,難于上山巔毛嫉。搬家之亂兮诽俯,諸事記心間。東市購家電承粤,西市看床單暴区。南市買麻袋,北市找車搬辛臊。大物用大箱仙粱,小件靠...
    風小揚閱讀 368評論 6 5
  • 上一篇spring getBean 源碼學習(上)基本上把getBean的過程細節(jié)了解清楚了,還剩下一些疑問以及沒...
    jwfy閱讀 2,474評論 0 0
  • 【日精進打卡第30天】 【知~學習】 《六項精進》2遍 共53遍 《大學》2遍 共54遍 【經(jīng)典名句分享】 抱最大...
    斐然之夏閱讀 143評論 0 0