昨天我们已经把登入画面做好了,大家有没有觉得万事起头难呢? 既然我们已经有登入画面了,当然要有注册画面啦,否则我们永远登不进去画面~ 那麽就开始啦!
先给大家看长相
2.1 先建立 dimen/string
2.2 去 layout
<dimen name="toolbar_textSize">18sp</dimen>
<string name="toolbar_title_register_account">注册帐号</string>
<string name="hint_enter_your_name">请输入您的姓名</string>
<string name="hint_enter_again_password">请再次输入您的密码</string>
<string name="hint_do_not_enter_same_password">请再确认密码是否一致</string>
<string name="register_already_have_account">我已经注册罗!</string>
<string name="register_pick_me_to_login">点我登入</string>
<string name="register_success">恭喜您注册成功!</string>
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.fragment.RegisterFragment">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar_register_fragment"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="@color/light_pewter_blue"
app:layout_constraintTop_toTopOf="parent">
<com.example.petsmatchingapp.utils.JFTextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="@color/white"
android:text="@string/toolbar_title_register_account"
android:textStyle="bold"
android:gravity="center"
android:textSize="@dimen/toolbar_textSize"/>
</androidx.appcompat.widget.Toolbar>
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/toolbar_register_fragment"
app:layout_constraintBottom_toBottomOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tip_register_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginStart="@dimen/tip_margin_start_end"
android:layout_marginEnd="@dimen/tip_margin_start_end"
android:layout_marginTop="@dimen/tip_margin_top_bottom">
<com.example.petsmatchingapp.utils.JFEditText
android:id="@+id/ed_register_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/edText_padding"
android:inputType="textPersonName"
android:hint="@string/hint_enter_your_name"
android:textSize="@dimen/edText_textSize"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tip_register_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tip_register_name"
android:layout_marginStart="@dimen/tip_margin_start_end"
android:layout_marginEnd="@dimen/tip_margin_start_end"
android:layout_marginTop="@dimen/tip_margin_top_bottom">
<com.example.petsmatchingapp.utils.JFEditText
android:id="@+id/ed_register_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/edText_padding"
android:inputType="textEmailAddress"
android:hint="@string/hint_enter_your_email"
android:textSize="@dimen/edText_textSize"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tip_register_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tip_register_email"
android:layout_marginStart="@dimen/tip_margin_start_end"
android:layout_marginEnd="@dimen/tip_margin_start_end"
android:layout_marginTop="@dimen/tip_margin_top_bottom">
<com.example.petsmatchingapp.utils.JFEditText
android:id="@+id/ed_register_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/edText_padding"
android:inputType="numberPassword"
android:hint="@string/hint_enter_your_password"
android:textSize="@dimen/edText_textSize"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tip_register_again_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tip_register_password"
android:layout_marginStart="@dimen/tip_margin_start_end"
android:layout_marginEnd="@dimen/tip_margin_start_end"
android:layout_marginTop="@dimen/tip_margin_top_bottom">
<com.example.petsmatchingapp.utils.JFEditText
android:id="@+id/ed_register_password_again"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/edText_padding"
android:inputType="numberPassword"
android:hint="@string/hint_enter_again_password"
android:textSize="16sp"/>
</com.google.android.material.textfield.TextInputLayout>
<com.example.petsmatchingapp.utils.JFButton
android:id="@+id/btn_register"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintTop_toBottomOf="@id/tip_register_again_password"
android:layout_marginTop="@dimen/tip_margin_top_bottom"
android:text="@string/register"
android:background="@drawable/button_background"
android:foreground="?attr/selectableItemBackground"
android:textColor="@color/white"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/btn_register"
android:layout_marginTop="20dp"
android:gravity="center"
android:orientation="horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<com.example.petsmatchingapp.utils.JFTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/hint_word_textSize"
android:text="@string/register_already_have_account"/>
<com.example.petsmatchingapp.utils.JFTextView
android:id="@+id/tv_register_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/hint_word_textSize"
android:textStyle="bold"
android:text="@string/register_pick_me_to_login"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
稍微看了一下我们的layout後会发现,我们会有好几个editText,代表我们要确认使用者是否输入为空,太棒了,我们就可以用之前在LoginFragment的方式来check
private fun validDataForm(): Boolean{
return when{
TextUtils.isEmpty(binding.edRegisterName.text.toString().trim()) -> {
showSnackBar("请输入名称",true)
return false
}
TextUtils.isEmpty(binding.edRegisterEmail.text.toString().trim()) -> {
showSnackBar("请输入有效的Email",true)
return false
}
TextUtils.isEmpty(binding.edRegisterPassword.text.toString().trim()) -> {
showSnackBar("请输入有效的密码",true)
return false
}
TextUtils.isEmpty(binding.edRegisterPasswordAgain.text.toString().trim()) -> {
showSnackBar("请输入有效的确认密码",true)
return false
}
binding.edRegisterPasswordAgain.text.toString().trim() != binding.edRegisterPassword.text.toString().trim() ->{
showSnackBar("请再确认密码是否一致",true)
return false
}
else -> true
}
}
这边需要跟大家稍微说明一下,我们 sign in 的时候帐号资讯会在Authentication里面,而每一个帐号也可以去设定userName跟photoUri,但是因为我们除了这些外,我们还会需要有其他user资讯。所以我们这次只在auth储存 email+password,其他的资讯都储存在 Firestore database。
首先我们需要来创造一个data class,并且命名为 User
//每个都设预设值,好让我们在创立 Object的时候,可以不用全部都指定
data class User(
val id: String = "",
val name: String = "",
val email: String = "",
val password: String = "",
val image: String = "",
val gender: String = "",
val profileCompleted: Boolean = false
)
在RegisterFragment一样去继承 View.OnClickListener,并且override onClick
override fun onClick(v: View?) {
//透过 when,来设定当user点击时的回馈方式
when(v){
binding.btnRegister ->{
//如果validDataForm 回馈的为 true
if(validDataForm()){
showDialog(resources.getString(R.string.please_wait))
//实例化 user,并且把 edText拿到的资料给它。
val user = User(
name = binding.edRegisterName.text.toString().trim(),
email = binding.edRegisterEmail.text.toString().trim(),
password = binding.edRegisterPassword.text.toString().trim(),
)
//并把 fragment + user 丢给 viewModel
accountViewModel.registerWithEmailAndPassword(this,user)
}
}
}
}
一样,因为要叫出 AccountViewModel,所以我们要在最上面新增
private val accountViewModel: AccountViewModel by sharedViewModel()
以及因为我们用override OnClick,所以我们也要在 onCreateView里面设定
binding.btnRegister.setOnClickListener(this)
好啦,我们要回到 AccountViewModel
//把 RegisterFragment跟 user传进去
fun registerWithEmailAndPassword(fragment: RegisterFragment, user: User){
//直接拿 auth的instance,并且透过 email跟 password来办帐号
FirebaseAuth.getInstance().createUserWithEmailAndPassword(user.email,user.password)
.addOnSuccessListener{
val newUser = User(
name = user.name,
email = user.email,
password = user.password,
id =it.user!!.uid
)
addUserDetailsToFireStore(fragment,newUser)
}
.addOnFailureListener{
fragment.registerFail(it.toString())
}
}
★ 注意,我们在createUserWithEmailAndPassword的funtion後面新增的 addOnSuccessListener,它会传回一个 AuthResult,我们可以直接透过它去拿到该帐号的使用者id,我们再透过这个id,去设定firestore database的 document id,我们把两个id设为一样,可以帮助我们找寻资料。
好的,写完上面的 Code後会发现,我们有红字,那我们现在要解决的就是在 Firebase Auth创号帐号後,要再把其他资讯在Firestore database 储存资料。
private fun addUserDetailsToFireStore(fragment: RegisterFragment,user: User){
//这边创立 Firestore的 instance,并且collection内指定集合为user,集合里面填string,我们用Constant来确保每次呼叫都不会拼错字
FirebaseFirestore.getInstance().collection(Constant.USER)
//这边指定 document的id为刚刚auth回传的 uid
.document(user.id)
.set(user, SetOptions.merge())
.addOnSuccessListener{
fragment.registerSuccessful()
}
.addOnFailureListener{
fragment.registerFail(it.toString())
}
}
Firestote有两种新增资料的方法
至於说刚刚的Constant怎麽写呢?
在创立 class的地方,我们创立一个Object,并且命名为 Constant
并在里面新增名为 USER的变数即可,就可以在任何地方呼叫它。
object Constant{
const val USER: String = "user"
}
接下来,我们回到 RegisterFragment,并且新增把资料上传到Firestore成功或失败後会跑的funtion
fun registerSuccessful(){
hideDialog()
showSnackBar(resources.getString(R.string.register_success),false)
//当成功上传後,跳转到 loginFragment
nav.navigate(R.id.action_registerFragment_to_loginFragment)
}
fun registerFail(e: String){
hideDialog()
//失败则show 错误讯息
showSnackBar(e,true)
}
我们可以看到 nav.navigate(R.id.action_registerFragment_to_loginFragment) 是红字。
首先我们要把nav这个解决,之前提到我们可以透过findNavController,来控制Fragment跳转的事件~
再过来要解决後面的 R.id的红字,这边的id都是action的id
因为我们目前在 account_nav里面只有一个Fragment,所以我们要跟昨天交的方式,把RegisterFragment新增进去,并且用连连看的方式把它们连起来。点选圆圈圈,就可以拖移到想要转换到的Fragment。并且因为我们会从LoginFragment透过textView转换到RegisterFragment,以及RegisterFragment透过textView转到LoginFragment,所以我们两边都要连起来。
这样就可以啦! 那我们是不是还忘了什麽啊??
没错,就是我们 layout里面有一个 已经登入了的选项,我们当然也要把这个加入到 onClick的地方啊,并且透过 navigation 的方式转移到 LoginFragment,在onClick新增
binding.tvRegisterLogin -> {
nav.navigate(R.id.action_registerFragment_to_loginFragment)
}
并在 onCreateView新增
binding.tvRegisterLogin.setOnClickListener(this)
那再来的步骤,就是我们要把我们的 LoginFragment转到RegisterFragment
先到LoginFragment,并在 onClick的Funtion 里面新增
binding.tvRegister -> {
nav.navigate(R.id.action_loginFragment_to_registerFragment)
}
还有在class下面新增
//延迟初始化
private lateinit var nav: NavController
在onCreateView 里面
//初始化 NavController
nav = findNavController()
//设定 onClickListener
binding.tvRegister.setOnClickListener(this)
接下来要设定在toolbar左上方的回退键
1.先新增 vector asset,并且选择一个往左边的箭头
2.在onCreateView写入以下Code
//设定刚刚的icon
binding.toolbarRegisterFragment.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
//设定导航事件
binding.toolbarRegisterFragment.setNavigationOnClickListener {
requireActivity().onBackPressed()
}
然後当然还有到 Firebase平台,点选自己的专案後,到达 Firestroe database,点选建立资料库,选择测试模式。
并且需要更改读取和写入的规则
上面的流程透过 email+password注册帐号後,可以从Firebase Auth 看到
从 Firestore database 看到
完成品出来啦!!
(请原谅这个动画是我在还没新增返回键时就拍摄的,所以时间跟完文章後,左上角会有白色的返回键,且可以使用 XD)
好啦!! 明天会是轻松的部分,如果忘记密码後怎麽办呢!!!!
大家纠期待一下啦!!!! へけ
今天的内容属於设计模式的一种。 当我们从後端接到资料後,有时後资料格式往往不是如我们所想,所以会再加...
post — 传统表单输入介绍 这个功能常用在注册帐号时,将使用者输入的资料跟资料库做比对,检查是否...
What is function? Simple explanation: when you fin...
今年的疫情蛮严重的,希望大家都过得安好,希望疫情快点过去,能回到一些线下技术聚会的时光~ 今天目标:...
大 module 小 module,能够重复使用又好维护的就是好 module 上一章介绍 modu...