Сделайте свои диалоги такими, как вы хотите
Диалог в программировании - это представление, которое предлагает пользователю какое-либо предупреждение или запрос пользователя выполнить действие, в Android диалоговые окна следуют одному и тому же варианту использования и бывают многих типов, таких как выбор даты, выбор времени, диалоговое окно предупреждений, но есть также возможность создавать и настраивать свои диалоги
В этом уроке я покажу вам, как вы можете создать свой собственный диалог для любого варианта использования, который вы хотите, шаг за шагом с помощью Kotlin.
AlertDialog
В Android диалоговое окно «Предупреждение» - это самый простой тип диалогового окна. Обычно оно состоит из предупреждения и кнопки или двух кнопок, которые обычно имеют значение «Да», «Нет» или «ОК».
Создать диалоговое окно с предупреждением очень просто, код Kotlin ниже делает именно это.
val builder = AlertDialog.Builder(requireActivity()) builder.setMessage("This is an Alert dialog in kotlin") .setCancelable(false) .setPositiveButton("ok") { dialog, id -> dismiss()} val alert = builder.create() alert.show()
DialogFragment
DailogFragment в android является подклассом класса Fragment, но он специализируется на создании диалогов, это ключ для создания любого настраиваемого диалогового окна по вашему выбору.
В следующих разделах мы собираемся создать два настраиваемых диалоговых окна, AgePickerDialog и FruitPickerDialog, используя класс DialogFragment, адаптеры и настраиваемые слушатели событий для взаимодействия с диалоговыми окнами.
AgePickerDialog и FruitPickerDialog
Наш AgePickerDialog предложит пользователю выбрать его / ее возрастную группу, и после выбора AlertDialog отобразит выбор пользователя на экране.
Наш FruitPickerDialog будет делать то же самое, что и AgePickerDialog, но будет предлагать пользователю выбрать фрукты, и при выборе имя фрукта будет отображаться в AlertDialog.
Создайте новый проект студии Android и назовите его «CustomDialogs» или дайте ему имя по вашему выбору, после того, как проект будет создан и построен в первый раз, перейдите к узлу приложения проекта в разделе структуры проекта, затем перейдите к приложению. / java / (имя вашего проекта) в этом узле создайте следующие пакеты: Adapters, Listeners, Models, Dialogs.
Мы будем использовать вышеуказанные пакеты для структурирования нашего решения.
теперь перейдите к своему основному файлу макета деятельности и создайте две кнопки, одна для отображения AgePickerDialog, а вторая кнопка будет для отображения FruitPickerDialog.
ваш основной файл макета деятельности должен выглядеть, как показано ниже
<?xml version="1.0" encoding="utf-8"?> <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" tools:context=".CustomDialogActivity"> <Button android:id="@+id/age" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="24dp" android:layout_marginTop="24dp" android:layout_marginEnd="24dp" android:text="Select Age Group" android:onClick="showSelectAgeDialog" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/fruit" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="24dp" android:layout_marginTop="24dp" android:layout_marginEnd="24dp" android:text="Select Fruit" android:onClick="showFruitSelectDialog" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/age" /> </androidx.constraintlayout.widget.ConstraintLayout>
Добавьте приведенный ниже код в свой класс MainActivity
class CustomDialogActivity : AppCompatActivity() { // age select button lateinit var ageSelect:Button //fruit select button lateinit var fruitSelect:Button override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_custom_dialog) // initializing buttons ageSelect = findViewById(R.id.age) fruitSelect = findViewById(R.id.fruit) } //display AgePickerDialog when age select button is clicked fun showSelectAgeDialog(v: View){ val ageSelector:AgePickerDialog = AgePickerDialog() ageSelector.show(supportFragmentManager,"age_select") } //display FruitPickerDialog when fruitSelect button is clicked fun showFruitSelectDialog(v: View){ val fruitSelector:FruitPickerDialog = FruitPickerDialog() fruitSelector.show(supportFragmentManager,"fruit_select") } }
MainActivity имеет две функции, которые вызываются при нажатии кнопок в макете.
showSelectAgeDialog () вызывается, когда пользователь нажимает кнопку ageSelect, эта функция просто отображает наш AgePickerDialog.
showFruitSelectDialog () вызывается, когда пользователь нажимает кнопку fruitSelect, эта функция просто отображает наш FruitPickerDialog.
В пакете Models, который мы создали выше, создайте новый класс Kotlin и назовите его AgeGroupModel, этот класс будет использоваться для создания данных возрастной группы, необходимых для AgePickerDialog.
class AgeGroupModel() { var label :String = ""; var startInterval:Int = 0; var endInterval:Int = 0; constructor(label:String, startInterval:Int, endInterval:Int):this(){ this.label = label this.startInterval = startInterval this.endInterval = endInterval } }
В пакете Listeners создайте два интерфейса, которые помогут в обнаружении и обработке события щелчка по элементам наших диалогов.
interface AgeItemSelectListener { fun itemClicked(ageGroup:AgeGroupModel, position:Int) }
В функции itemClicked () интерфейса AgeItemSelectListener мы будем обрабатывать событие щелчка для выбранного элемента возраста.
interface FruitItemListener { fun onFruitClicked(fruit:String, position:Int) }
В функции onFruitClicked () интерфейса FruitItemListener мы будем обрабатывать событие щелчка для выбранного фруктового элемента.
Чтобы иметь возможность отображать список элементов и фруктов AgeGroupModel в их соответствующих диалоговых окнах, нам нужно будет использовать адаптеры RecyclerView, которые будут принимать в качестве параметров список элементов и их соответствующие интерфейсы для прослушивания события щелчка, экземпляры адаптеров затем будут должен быть установлен в RecyclerViews в обоих диалогах для отображения их элементов.
В пакете адаптеров создайте два класса, расширяющие класс RecyclerView.Adapter.
AgeGroupAdapter
class AgeGroupAdapter(private val ageList:ArrayList<AgeGroupModel>, private val listenerAge: AgeItemSelectListener): RecyclerView.Adapter<AgeGroupAdapter.AgeGroupViewHolder>() { //initialize itemView for each item override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AgeGroupViewHolder { val itemView: View = LayoutInflater.from(parent.context).inflate(R.layout.layout_age_group_item,parent,false) return AgeGroupViewHolder(itemView) } //bind the items to their data override fun onBindViewHolder(holder: AgeGroupViewHolder, position: Int) { holder.itemPosition = position holder.bind() } // return total number of items to be displayed override fun getItemCount(): Int { return ageList.size } //Viewholder class for handling interactions with corresponding item inner class AgeGroupViewHolder(itemView: View): RecyclerView.ViewHolder(itemView), View.OnClickListener{ var label:TextView = itemView.findViewById(R.id.label) var group:TextView = itemView.findViewById(R.id.group) var itemPosition:Int = 0 // bind data to item views fun bind(){ itemView.setOnClickListener(this) label.text = ageList.get(itemPosition).label val ag:String = ageList.get(itemPosition).startInterval.toString()+"-"+ageList.get(itemPosition).endInterval+" years" group.text = ag } //report click events to dialog with listener override fun onClick(v: View?) { listenerAge.itemClicked(ageList.get(itemPosition),itemPosition) } } }
В приведенном выше AgeGroupAdapter есть внутренний класс AgeGroupViewHolder, который расширяет класс RecyclerView.ViewHolder, этот класс описывает представление элемента и метаданные о его месте в RecyclerView, в нашем случае он также используется для установки данных для каждого элемента. ему соответствующий itemView.
Макет для каждого элемента RecyclerView инициализируется в методе onCreateViewHolder адаптера и передается в качестве параметра новому экземпляру AgeGroupViewHolder.
age_group_item_layout.xml
<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="wrap_content" android:layout_margin="8dp" android:foreground="?attr/selectableItemBackground" android:background="@color/white"> <TextView android:id="@+id/label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" android:gravity="start|center" android:text="Age Label" android:textSize="16sp" android:textStyle="bold" android:textColor="@color/black" android:typeface="sans" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/group" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" android:gravity="start|center" android:text="Age Group" android:textSize="16sp" android:textColor="@color/black" android:typeface="sans" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@+id/label" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
FruitsAdapter
class AgeGroupAdapter(private val ageList:ArrayList<AgeGroupModel>, private val listenerAge: AgeItemSelectListener): RecyclerView.Adapter<AgeGroupAdapter.AgeGroupViewHolder>() { //initialize itemView for each item override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AgeGroupViewHolder { val itemView: View = LayoutInflater.from(parent.context).inflate(R.layout.layout_age_group_item,parent,false) return AgeGroupViewHolder(itemView) } //bind the items to their data override fun onBindViewHolder(holder: AgeGroupViewHolder, position: Int) { holder.itemPosition = position holder.bind() } // return total number of items to be displayed override fun getItemCount(): Int { return ageList.size } //Viewholder class for handling interactions with corresponding item inner class AgeGroupViewHolder(itemView: View): RecyclerView.ViewHolder(itemView), View.OnClickListener{ var label:TextView = itemView.findViewById(R.id.label) var group:TextView = itemView.findViewById(R.id.group) var itemPosition:Int = 0 // bind data to item views fun bind(){ itemView.setOnClickListener(this) label.text = ageList.get(itemPosition).label val ag:String = ageList.get(itemPosition).startInterval.toString()+"-"+ageList.get(itemPosition).endInterval+" years" group.text = ag } //report click events to dialog with listener override fun onClick(v: View?) { listenerAge.itemClicked(ageList.get(itemPosition),itemPosition) } } }
Выше представлен наш класс FruitsAdapter для FruitPickerDialog, он следует той же структуре, что и AgeGroupAdapter, макет для каждого элемента инициализируется в методе onCreateViewHolder адаптера, и все взаимодействия между элементами и представлением происходят в классе FruitViewHolder.
layout_fruit_item.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="8dp" android:background="@color/white" android:foreground="?attr/selectableItemBackground"> <TextView android:id="@+id/fruit_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" android:gravity="start|center" android:text="Fruit" android:textColor="@color/black" android:textSize="16sp" android:textStyle="bold" android:typeface="sans" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
Теперь мы закончим, создав два наших настраиваемых класса диалоговых окон. В пакете Dialogs, который мы создали выше, создайте два класса Kotlin и назовите их AgePickerDialog и FruitPickerDialog соответственно.
Добавьте приведенный ниже код к обоим классам соответственно, код хорошо прокомментирован, чтобы помочь вам понять процесс.
AgepickerDialog.kt
class AgePickerDialog() : DialogFragment() { // RecyclerView for listing age groups private lateinit var list: RecyclerView // dialog view is created override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { Objects.requireNonNull(dialog)?.window!!.requestFeature(Window.FEATURE_NO_TITLE) return inflater.inflate(R.layout.layout_dialog_fragment_age_select,null,false) } //dialog view is ready override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) // initialize and setup RecyclerView list = view.findViewById(R.id.list) list.setHasFixedSize(true) list.setItemViewCacheSize(20) list.layoutManager = LinearLayoutManager(requireActivity(), RecyclerView.VERTICAL, false) // create Arraylist of AgeGroupModel to be displayed in RecyclerView val ageList :ArrayList<AgeGroupModel> = ArrayList() ageList.add(AgeGroupModel("Kid",1,11)) ageList.add(AgeGroupModel("Teen",12,17)) ageList.add(AgeGroupModel("Adult",18,45)) ageList.add(AgeGroupModel("Old",46,80)) ageList.add(AgeGroupModel("Ancient",81,110)) ageList.add(AgeGroupModel("Artifact",111,200)) // create AgeItemSelectListener to listen to click event on items from the RecyclerView Adapter val listenerAge:AgeItemSelectListener = object : AgeItemSelectListener { override fun itemClicked(ageGroup: AgeGroupModel, position: Int) { //when item in adapter is clicked, show selected age in an AlertDialog showSelectedItemAlert(ageGroup,position) } }; // create AgeGroupAdapter and pass as parameters the agelist and the AgeItemSelectListener val adapter: AgeGroupAdapter = AgeGroupAdapter(ageList,listenerAge) list.adapter = adapter } private fun showSelectedItemAlert(ageGroup: AgeGroupModel, position: Int){ val builder = AlertDialog.Builder(requireActivity()) builder.setMessage(ageGroup.label+" you are") .setCancelable(false) .setPositiveButton("ok") { dialog, id -> dismiss()} val alert = builder.create() alert.show() } }
layout_dialog_fragment_age_picker.xml
<FrameLayout 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"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="300dp" android:layout_height="500dp" android:layout_gravity="center" android:background="@drawable/rounded_shadow_box"> <TextView android:id="@+id/select_title" android:layout_width="0dp" android:layout_height="wrap_content" android:background="@color/teal_200" android:padding="8dp" android:text="Select your age Group" android:textColor="@color/white" android:textSize="20sp" android:textStyle="bold" app:layout_constraintBottom_toTopOf="@+id/list" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/list" android:layout_width="0dp" android:layout_height="0dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/select_title" app:layout_constraintVertical_bias="1.0" /> </androidx.constraintlayout.widget.ConstraintLayout> </FrameLayout>
В методе onViewCreated () нашего AgePickerDialog мы инициализируем и настраиваем RecyclerView и ArrayList AgeGroupModel, которые будут использоваться в качестве элементов в RecyclerView, также инициализируется AgeItemSelectListener, и в нем метод itemClicked () мы вызываем метод showSelectedItemAlert (), который отобразит элемент, выбранный пользователем из RecyclerView, в AlertDialog.
Наконец, мы инициализируем экземпляр FruitsAdapter и передаем ArrayList AgeGroupModel и AgeItemSelectListener в качестве параметров его конструктору.
FruitPickerDialog.kt
class FruitPickerDialog() : DialogFragment() { //RecyclerView for displaying fruit names to the screen private lateinit var fruitList: RecyclerView; // dialog view is created override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { Objects.requireNonNull(dialog)?.window!!.requestFeature(Window.FEATURE_NO_TITLE) return inflater.inflate(R.layout.layout_dialog_fragment_fruit,null,false); } //dialog view is ready override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) // initialize and setup fruitList RecyclerView fruitList = view.findViewById(R.id.fruit_list) fruitList.setHasFixedSize(true) fruitList.setItemViewCacheSize(20) fruitList.layoutManager = LinearLayoutManager(requireActivity(), RecyclerView.VERTICAL, false) // create ArrayList of String with fruit names to be displayed in RecyclerView val fruits:ArrayList<String> = ArrayList() fruits.add("Banana"); fruits.add("Orange"); fruits.add("Avocado") fruits.add("Guava"); fruits.add("Apple"); fruits.add("Pineapple") // create FruitItemListener to listen to click event on items from the RecyclerView Adapter val listener:FruitItemListener = object : FruitItemListener{ override fun onFruitClicked(fruit: String, position: Int) { //when item in adapter is clicked, show selected fruit in an AlertDialog showSelectedFruit(fruit) } } // create FruitsAdapter and pass as parameters the fruits and the FruitItemListener val adapter:FruitsAdapter = FruitsAdapter(fruits,listener) fruitList.adapter = adapter } private fun showSelectedFruit(fruit:String){ val builder = AlertDialog.Builder(requireActivity()) builder.setMessage("you love $fruit") .setCancelable(false) .setPositiveButton("ok") { dialog, id -> dismiss()} val alert = builder.create() alert.show() } }
layout_dialog_fragment_fruit_picker.xml
<FrameLayout 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"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="300dp" android:layout_height="500dp" android:layout_gravity="center" android:background="@drawable/rounded_shadow_box"> <TextView android:id="@+id/select_title2" android:layout_width="0dp" android:layout_height="wrap_content" android:background="@color/teal_200" android:padding="8dp" android:text="Select your Best Fruit" android:textColor="@color/white" android:textSize="20sp" android:textStyle="bold" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/fruit_list" android:layout_width="0dp" android:layout_height="0dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/select_title2" /> </androidx.constraintlayout.widget.ConstraintLayout> </FrameLayout>
В методе onViewCreated () нашего FruitPickerDialog мы инициализируем и настраиваем RecyclerView и ArrayList of String с названиями фруктов, которые будут использоваться в качестве элементов в RecyclerView, также инициализируется FruitItemListener
, и в нем onFruitClicked (), мы вызываем метод showSelectedFruit (), который отображает имя плода, которое пользователь выбрал из RecyclerView в AlertDialog.
Наконец, мы инициализируем экземпляр FruitsAdapter и передаем ArrayList имен фруктов и FruitItemListener в качестве параметров его конструктору.
Вывод
В этом уроке мы создали наши настраиваемые диалоги с классическим вариантом использования, который выбирает элементы в диалоговом окне или проверяет действие, но на самом деле вы можете сделать гораздо больше с DialogFragment в Android, так что позвольте своему воображению разгуляться и счастливому кодированию .