提醒:今天的内容缺少了加密储存密码,是极度危险的功能,这部份预计会放到明天处理。
Layout的部份我一样使用Chip取代Checkbox,在Login页面中加入:
<!-- ... -->
<com.google.android.material.chip.ChipGroup
android:id="@+id/chipGroup"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:alpha="0"
app:layout_constraintBottom_toTopOf="@id/login"
app:layout_constraintEnd_toEndOf="@id/pwdLayout"
app:layout_constraintStart_toStartOf="@id/pwdLayout"
app:layout_constraintTop_toBottomOf="@id/pwdLayout">
<com.google.android.material.chip.Chip
android:id="@+id/saveId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkable="true"
android:fontFamily="sans-serif"
android:text="记住ID"
android:textSize="16sp"
android:textStyle="bold" />
<com.google.android.material.chip.Chip
android:id="@+id/savePwd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkable="true"
android:fontFamily="sans-serif"
android:text="记住密码"
android:textSize="16sp"
android:textStyle="bold" />
<com.google.android.material.chip.Chip
android:id="@+id/auto_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkable="true"
android:fontFamily="sans-serif"
android:text="自动登入"
android:textSize="16sp"
android:textStyle="bold"
android:visibility="gone" />
</com.google.android.material.chip.ChipGroup>
<!-- ... -->
储存的内容会存到SharePreferences中,先做一些前置作业:
const val PREF_NAME = "preferences"
const val PREF_FIELD_ID = "id"
const val PREF_FIELD_PWD = "pwd"
const val PREF_FIELD_AES_KEY = "aes_key"
const val PREF_FIELD_AUTO_LOGIN = "auto_login"
class LoginFragment : Fragment() {
// ...
private lateinit var preferences: SharedPreferences
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
preferences = view.context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
}
}
PREF_NAME是在Day21中已加入的值,其余是这两天会用到的。
储存的时机点我是放在登入成功时,以下片段节自Day07
// ...
4 -> { // "【主功能表】"
withContext(Dispatchers.Main) {
val editor = preferences.edit()
if (binding.saveId.isChecked) {
editor.putString(PREF_FIELD_ID, id)
}
if (binding.savePwd.isChecked) {
editor.putString(PREF_FIELD_PWD, pwd)
}
editor.putBoolean(
PREF_FIELD_AUTO_LOGIN,
binding.autoLogin.isChecked
)
editor.apply()
(requireActivity() as MainActivity).dismissLoading()
NavHostFragment.findNavController(this@LoginFragment)
.navigate(R.id.action_loginFragment_to_searchArticleFragment)
}
break
}
// ...
提取的位置我是放在开始执行进入动画前:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// ...
binding.chipGroup.postDelayed({
val id = preferences.getString(PREF_FIELD_ID, null)
val pwd = preferences.getString(PREF_FIELD_PWD, null)
if (!id.isNullOrBlank()) {
binding.idInput.setText(id)
binding.saveId.isChecked = true
}
if (!pwd.isNullOrBlank()) {
binding.pwdInput.setText(pwd)
binding.savePwd.isChecked = true
}
if (binding.saveId.isChecked && binding.savePwd.isChecked) {
binding.autoLogin.visibility = View.VISIBLE
binding.autoLogin.isChecked =
preferences.getBoolean(PREF_FIELD_AUTO_LOGIN, false)
}
enterAnimate(binding.chipGroup)
}, 267)
// ...
}
autoLogin的检查条件除了本身是否纪录有勾选外,还需要多判断是否有已储存的帐密,此外autoLogin的按钮我预设是隐藏的,只有在saveId和savePwd都被打勾的状态下才能勾选。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// ...
binding.saveId.setOnCheckedChangeListener { _, isChecked ->
checkShowAutoLogin()
if (!isChecked) {
preferences.edit().remove(PREF_FIELD_ID).apply()
}
}
binding.savePwd.setOnCheckedChangeListener { _, isChecked ->
checkShowAutoLogin()
if (!isChecked) {
preferences.edit().remove(PREF_FIELD_PWD).apply()
}
}
binding.autoLogin.setOnCheckedChangeListener { _, isChecked ->
preferences.edit().putBoolean(PREF_FIELD_AUTO_LOGIN, isChecked).apply()
}
// ...
}
private fun checkShowAutoLogin() {
if (binding.saveId.isChecked && binding.savePwd.isChecked) {
binding.autoLogin.visibility = View.VISIBLE
} else {
binding.autoLogin.visibility = View.GONE
binding.autoLogin.isChecked = false
}
}
如前所述autoLogin只有在saveId和savePwd都被打勾的状态下才能勾选,因此这两个按钮在点击时会去呼叫checkShowAutoLogin来判断目前状态,其余内容就是取消勾选时移除目前已储存的资料。
在处理自动登入时,除了autoLogin的勾选状态外,还需要考虑到登出和断线重连的状态。在这两个状态中我们不该执行自动登入的功能。
为了处理上述两个状态,我分别在loginFragment的fragment tag和welcome_to_login的action tag中加入Argument。
<argument
android:name="canAutoLogin"
android:defaultValue="false"
app:argType="boolean"
app:nullable="false" />
<argument
android:name="canAutoLogin"
android:defaultValue="true"
app:argType="boolean"
app:nullable="false" />
可以看到两者的差异只差在defaultValue,只有在从WelcomeFragment到LoginFragment的action中才会将canAutoLogin设为true
。
有了这个参数值,就可以做以下判断:
// ...
if (preferences.getBoolean(PREF_FIELD_AUTO_LOGIN, false)
&& LoginFragmentArgs.fromBundle(requireArguments()).canAutoLogin
) {
binding.login.performClick()
}
// ...
至此自动登入功能也就完成了。
最後再次提醒:今天的内容缺少了加密储存密码,是极度危险的功能,这部份预计会放到明天处理。
<<: DAY28 linebot message api-Template 介绍-1
>>: D33 - 用 Swift 和公开资讯,打造投资理财的 Apps { 台股申购功能扩充.4 }
TIMER+NVIC中断 今天我们来使用Timer的中断功能吧! 设定与昨天大致相同,只是我们现在需...
首先建立装载角色资料的 ViewModel,因为接下来的权限会以角色判断,ASP.NET Core ...
前言 我很喜欢这篇 CodeLab,我自己认为,如果这篇的内容看得懂那 Provider 基本上都会...
Day4有跟大家提到for回圈,但并非所有条件都必须用for回圈来写,这个时候我们就可以利用whil...
再来要讲到管理工具中耳熟能详的工具之一,甘特图。Gantt Chart在规划专案时,几乎就是所以人第...