ایجاد فایل های پروژه و ویرایش آنها
در پروژه فایل های زیر را ایجاد و ویرایش میکنیم
Activity:
MainActivity.kt
activity_main.xml
توجه:
هنگام ایجاد پروژه اگه گزینه ی EmptyActivity رو بزنید فایل های Activity به صورت خودکارساخته میشن پس از این قسمت عبور میکنیم.
Fragments:
LoginFragment.kt
fragment_login.xml
ProfileFragment.kt
fragment_profile.xml
NavGraph:
main_navgraph.xml
۱- ایجاد ویو های فرگمنت :
در مسیر زیر فایل های xml مورد نظر رو ایجاد کنید:
RootProject -> app -> main -> res -> layout
fragment_login.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:layout_margin="16dp"
android:text="@string/login_label_description"
android:gravity="center"
/>
<EditText
android:id="@+id/username_input"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:inputType="textAutoComplete"
android:layout_margin="8dp"
/>
<EditText
android:id="@+id/password_input"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:layout_margin="8dp"
app:hintEnabled="true"
app:helperText="Input" />
<Button
android:id="@+id/login_button"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/login"
/>
</LinearLayout>
</layout>
fragment_profile.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="username"
type="String" />
<variable
name="userId"
type="String" />
</data>
<androidx.constraintlayout.widget.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">
<TextView
android:id="@+id/welcome_text_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="@string/welcome"
android:textSize="32sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/id_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:padding="8dp"
android:text="@string/id"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/welcome_text_label" />
<TextView
android:id="@+id/textview_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:padding="8dp"
android:text="@{userId}"
android:textSize="16sp"
app:layout_constraintStart_toEndOf="@id/id_label"
app:layout_constraintTop_toBottomOf="@id/welcome_text_label"
tools:text="text" />
<TextView
android:id="@+id/username_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:padding="8dp"
android:text="@string/user"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/id_label" />
<TextView
android:id="@+id/textview_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:fontFamily="monospace"
android:padding="8dp"
android:text="@{username}"
android:textSize="16sp"
app:layout_constraintStart_toEndOf="@id/username_label"
app:layout_constraintTop_toBottomOf="@id/id_label"
tools:text="Rawhide95" />
<com.google.android.material.button.MaterialButton
android:id="@+id/logout_button"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/logout"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textview_id" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
توضیح:
در این دو فایل از عنصر layout به عنوان روت فایل ها استفاده کردیم که بیانگر استفاده از databinding است
سپس با استفاده از عنصر data داده های مورد نظر که باید view ها بهشون وصل بشن رو تعریف کردیم
در fragment_profile ویو های مورد نظر رو به داده های تعریف شده متصل کردیم
@{username}
@{userId}
۲-ایجاد کلاس های فرگمنت
در مسیر زیر دو کلاس زیر رو ایجاد کنید:
RootProject -> app -> main -> java -> com.skybirdbits.logindemo
LoginFragment.kt
package com.skybirdbits.logindemo
import androidx.fragment.app.Fragment
class LoginFragment: Fragment() {
}
ProfileFragment.kt
package com.skybirdbits.logindemo
import androidx.fragment.app.Fragment
class ProfileFragment: Fragment() {
}
۳- ایجاد فایل NavGraph :
در مسیر زیر فایل navgraph رو ایجاد کنید و کد هاشو به صورت زیر تغییر بدید:
RootProject -> app -> main -> res -> navigation
main_nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/main_nav_graph"
app:startDestination="@id/loginFragment">
<fragment
android:id="@+id/loginFragment"
android:name="com.skybirdbits.logindemo.LoginFragment"
android:label="LoginFragment"
tools:layout="@layout/fragment_login">
<action
android:id="@+id/from_loginFragment_to_profileFragment"
app:destination="@id/profileFragment"
app:popUpTo="@id/loginFragment"
app:popUpToInclusive="true">
<argument
android:name="usrname"
app:argType="string"
app:nullable="false" />
<argument
android:name="id"
app:argType="string"
app:nullable="false"/>
</action>
</fragment>
<fragment
android:id="@+id/profileFragment"
android:name="com.skybirdbits.logindemo.ProfileFragment"
android:label="ProfileFragment"
tools:layout="@layout/fragment_profile" >
<action
android:id="@+id/from_profileFragment_to_loginFragment"
app:destination="@id/loginFragment"
app:popUpTo="@id/profileFragment"
app:popUpToInclusive="true" />
</fragment>
</navigation>
توضیح:
در فایل بالا برای هرکدوم از فرگمنت ها یک عنصر fragment تعریف کردیم
با استفاده از عنصر action مسیر های بین این دو فرگمنت رو تعریف کردیم
عناصر argument به عنوان فرزند عنصر action در loginFragment حامل مقادیری هستند که میخوایم هنگام رفتن
کاربر از LoginFragment به ProfileFragment منتقل بشه
توضیح مختصر از فیلد popUpTo در عنصر اکشن:
فرض کنید به صورت زیر سه تا فرگمنت داریم و کاربر بینشون جا به جا شده
A -> B -> C
میخوایم بعد از رفتن کاربر از B به C با زدن دکمه ی back بدون برگشتن به B به A برگرده به عبارتی
فرگمنت B رو در Stack حذف کنیم در این صورت برای عنصر action مورد نظر که مسیر B به C رو تعریف کرده Id
فرگمنت A رو به عنوان popUpTo تعریف میکنیم
۴-تعریف عنصر FragmentContainerView به عنوان NavHost در activity_main
فایل activity_main رو به صورت زیر ویرایش کنید:
<?xml version="1.0" encoding="utf-8"?>
<merge 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"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/main_nav_graph"
app:defaultNavHost="true" />
</merge>
توضیح:
-از FragmentContainerView به عنوان navhost استفاده کردیم
- در فیلد name اسم کلاس NavHostFragment رو وارد کردیم تا FragmentContainerView به عنوان
NavHostFragment شناخته بشه
-در فیلد navGraph فایل main_nav_graph رو به عنوان NavGraph مورد استفاده قرار دادیم
-در فیلد defaultNavhost چ با قرار دادن true گفتیم که عنصر FragmentContainerView به عنوان NavHost
پیشفرض در نظر گرفته بشه
۵- وصل کردن view ها به کلاس های فرگمنت و استفاده از NavController برای جابه جایی کاربر بین دو
فرگمنت:
کلاس LoginFragment رو به صورت زیر ویرایش کنید:
LoginFragment.kt
package com.skybirdbits.logindemo
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar
import com.skybirdbits.logindemo.databinding.FragmentLoginBinding
import kotlin.random.Random
class LoginFragment: Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = FragmentLoginBinding.inflate(inflater, container , false)
val usernameInput = binding.usernameInput
val passwordInput = binding.passwordInput
binding.loginButton.setOnClickListener {
val username = usernameInput.text.toString()
val password = passwordInput.text.toString()
val id = Random(1000)
if(password.isNotBlank() && username.isNotBlank()) {
Snackbar.make(binding.root, "Welcome $username" , Snackbar.LENGTH_SHORT).show()
val action =
LoginFragmentDirections.fromLoginFragmentToProfileFragment(username, id.nextLong().toString())
findNavController().navigate(action)
}else {
Toast.makeText(context , "Username and password should not be empty!", Toast.LENGTH_SHORT).show()
}
}
return binding.root
}
}
توضیح:
-با توجه به عنصر layout که در فایل fragment_login تعریف کرده بودیم کلاس FragmentLoginBinding به صورت
خودکار ایجاد شده با استفاده از این کلاس فایل fragment_login رو inflate کردیم سپس view ها رو به صورت
مستقیم صدا زدیم
-دو متغیر usernameInput و passwordInput همون ویو های داخل fragment_login هستن در واقع از روی id
انها٬ به صورت خودکار ساخته شده اند و مستقیم به خود view ها اشاره دارن
-با کلاس Random یک متغیر تصادفی از جنس Long ایجاد کردیم که به عنوان آی دی کاربر در نظر گرفته میشه
سپس با شرط بررسی کردیم اگه usernameInput و passwordInput خالی نبودن
-با Snackbar پیغام ورود موفقیت امیز به کاربر بده و کاربر رو به ProfileFragment منتقل کنه
-متد fromLoginFragmentToProfileFragment رو به متد navigate پاس دادیم تا کاربر به ProfileFragment
منتقل بشه؛ این متد بر اساس id عنصر اکشن در loginFragment ایجاد شده
کلاس ProfileFragment رو به صورت زیر ویرایش کنید:
ProfileFragment.kt
package com.skybirdbits.logindemo
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar
import com.skybirdbits.logindemo.databinding.FragmentProfileBinding
class ProfileFragment: Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = FragmentProfileBinding.inflate(inflater, container, false)
val username = requireArguments().getString("usrname")
val id = requireArguments().getString("id")
binding.username = username
binding.userId = id
binding.logoutButton.setOnClickListener {
val action = ProfileFragmentDirections.fromProfileFragmentToLoginFragment()
findNavController().navigate(action)
Snackbar.make(binding.root,"You've been logged out", Snackbar.LENGTH_SHORT).show()
}
return binding.root
}
}
توضیح:
-با توجه به عنصر layout که در فایل fragment_profile تعریف کرده بودیم کلاس FragmentProfileBinding به
صورت خودکار ایجاد شده با استفاده از این کلاس فایل fragment_profile رو inflate کردیم
-در کلاس بالا همونطور که مشخصه با استفاده از متد requireArguments داده های حمل شده توسط عناصر
argument در فایل NavGraph رو در ProfileFragment تحویل گرفتیم.
-با استفاده از binding.username و binding.id متغیر های username و id تعریف شده در عنصر data در xml
مورد نظر رو مقدارهی کردیم.
-متد fromProfileFragmentToLoginFragment رو داخل navigate پاس دادیم تا به وسیلش کاربر از
ProfileFragment به LoginFragment منتقل بشه این متد بر اساس id عنصر action مربوط به profileFragmnet
به صورت خودکار ساخته شده.
-با Snackbar پیغام خروج کاربر رو نمایش دادیم.