diff --git a/app/build.gradle b/app/build.gradle
index 2507e8c8..f6914d93 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,11 +8,20 @@ android {
buildToolsVersion "30.0.3"
defaultConfig {
- applicationId "com.rsschool.quiz"
- minSdkVersion 22
+ applicationId "by.godevelopment.fandroidviewpager2"
+ minSdkVersion 23
targetSdkVersion 30
versionCode 1
versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
}
compileOptions {
@@ -22,6 +31,10 @@ android {
kotlinOptions {
jvmTarget = '1.8'
}
+
+ buildFeatures {
+ viewBinding = true
+ }
}
dependencies {
@@ -31,4 +44,14 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ testImplementation 'junit:junit:4.+'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+
+ implementation 'com.google.android.material:material:1.3.0'
+ implementation "androidx.viewpager2:viewpager2:1.0.0"
+
+ // val fragment_version = "1.3.4"
+ // implementation("androidx.fragment:fragment-ktx:$fragment_version")
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 26555680..ba99c480 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,10 +5,10 @@
+ android:theme="@style/Theme.Quiz.Purple">
@@ -18,6 +18,10 @@
+
\ No newline at end of file
diff --git a/app/src/main/java/com/rsschool/quiz/ActionListener.kt b/app/src/main/java/com/rsschool/quiz/ActionListener.kt
new file mode 100644
index 00000000..0b1f849c
--- /dev/null
+++ b/app/src/main/java/com/rsschool/quiz/ActionListener.kt
@@ -0,0 +1,9 @@
+package com.rsschool.quiz
+
+interface ActionListener {
+ fun nextFragment()
+ fun backFragment()
+ fun runResultFragment()
+ fun addAnswer(numberQuest: Int, numberAnswer: Int)
+ fun checkAnswersCount(): Boolean
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/rsschool/quiz/BlankFragment.kt b/app/src/main/java/com/rsschool/quiz/BlankFragment.kt
new file mode 100644
index 00000000..b11d4606
--- /dev/null
+++ b/app/src/main/java/com/rsschool/quiz/BlankFragment.kt
@@ -0,0 +1,107 @@
+package com.rsschool.quiz
+
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import com.rsschool.quiz.databinding.FragmentQuizBinding
+
+
+// Вне тела класса создаем константу для ключа аргумента, который будем передавать в каждый новый экземпляр фрагмента.
+const val ARG_OBJECT = "object"
+
+// Этот фрагмент будет использоваться для каждого нового экрана в приложении, мы будем создавать новый его экземпляр и передавать туда его порядковый номер.
+class BlankFragment : Fragment() {
+
+ // Крутая штука, что бы словить null и получить исключение именно здесь, а не в методах класса
+ private var _listener: ActionListener? = null
+ private val listener get() = _listener!!
+ private var _binding: FragmentQuizBinding? = null
+ private val binding get() = _binding!!
+
+ private var currentFragment = 0
+
+ // Добавляем сслыку на MainActivity через контекст
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ _listener = context as ActionListener
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+
+ // return inflater.inflate(R.layout.fragment_blank, container, false)
+ _binding = FragmentQuizBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ // В переопределенном методе onViewCreated получаем аргумент, находим текстовое поле и передаем туда значение аргумента для отображения.
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+
+ // Берем аргумент Bundle arguments, если он имеет ключ ARG_OBJECT и применяем лямбду
+ arguments?.takeIf { it.containsKey(ARG_OBJECT) }?.apply {
+
+ // Чтобы получить отправленные данные при загрузке из Bundle arguments, можно воспользоваться методом get(), в который передается ключ объекта:
+ val quizObject: QuizObject = get(ARG_OBJECT) as QuizObject
+
+ // Для условий
+ currentFragment = quizObject.numberQuestion
+
+ // Заполняем текстовые формы
+ binding.toolbar.title = "Question ${quizObject.numberQuestion}"
+ binding.question.text = quizObject.question
+ binding.optionOne.text = quizObject.answers[0]
+ binding.optionTwo.text = quizObject.answers[1]
+ binding.optionThree.text = quizObject.answers[2]
+ binding.optionFour.text = quizObject.answers[3]
+ binding.optionFive.text = quizObject.answers[4]
+
+ // Блокируем кнопку до выбора ответа
+ binding.nextButton.isEnabled = false
+ // Блокируем кнопки первого фрагмента
+ if (currentFragment < 2) {
+ binding.previousButton.isEnabled = false
+ binding.toolbar.navigationIcon = null
+ }
+
+ // Меняем надпись на последнем фрагменте
+ if (currentFragment > 4) binding.nextButton.text = "Submit"
+ }
+
+ binding.nextButton.setOnClickListener {
+ if (listener.checkAnswersCount()) listener.runResultFragment()
+ else listener.nextFragment()
+ }
+
+ binding.previousButton.setOnClickListener {
+ listener.backFragment()
+ }
+
+ binding.toolbar.setOnClickListener {
+ listener.backFragment()
+ }
+
+ binding.radioGroup.setOnCheckedChangeListener { _, checkId ->
+ when (checkId) {
+ binding.optionOne.id -> listener.addAnswer(currentFragment, 1)
+ binding.optionTwo.id -> listener.addAnswer(currentFragment, 2)
+ binding.optionThree.id -> listener.addAnswer(currentFragment, 3)
+ binding.optionFour.id -> listener.addAnswer(currentFragment, 4)
+ binding.optionFive.id -> listener.addAnswer(currentFragment, 5)
+ }
+ // Когда выбор ответа сделан - освобождаем кнопку
+ binding.nextButton.isEnabled = true
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/rsschool/quiz/DataObjectAccess.kt b/app/src/main/java/com/rsschool/quiz/DataObjectAccess.kt
new file mode 100644
index 00000000..5f87a6ed
--- /dev/null
+++ b/app/src/main/java/com/rsschool/quiz/DataObjectAccess.kt
@@ -0,0 +1,54 @@
+package com.rsschool.quiz
+
+// Этот класс реализует доступ к базе данных, в которой хранятся User и QuizObject объекты
+class DataObjectAccess {
+
+ // Таблица БД
+ private val quizGames: List = listOf(
+ QuizObject("Вы готовы, дети?", listOf("Да, капитан!", "Да!", "Нет.", "Нет, капитан", "буль-буль-буль"), 1, 1),
+ QuizObject("Ктоооооооо... Кто проживает на дне океана?", listOf("Рыбки", "Губка Боб Квадратные Штаны!", "Водолаз", "Никто не проживает", "Terror from the Deep"), 2,2),
+ QuizObject("Жёлтая губка, малыш без изъяна?", listOf("С изъяном", "Не малышь", "Губка Боб Квадратные Штаны!", "Синяя губка", "Жёлтая подводная лодка"), 3,3),
+ QuizObject("Кто побеждает всегда и везде?", listOf("Годзилла", "Рэмбо", "Капитан Америка", "Губка Боб Квадратные Штаны!", "Фиолетовый из Повер Рэнджерс"), 4,4),
+ QuizObject("Кто также ловок, как рыба в воде?", listOf("Другая рыба", "Аквамен", "Глубина", "Тазик залитый бетоном", "Губка Боб Квадратные Штаны!"), 5,5),
+ )
+
+ // Номер вопроса - Номер ответа
+ private val numAnswers: HashMap = hashMapOf()
+
+ // hashMap позволяет нам удобно перезаписывать уже выбранные ответы
+ fun addAnswer(numberQuest: Int, numberAnswer: Int) {
+ numAnswers[numberQuest-1] = numberAnswer
+ // if (numberQuest > 1 && numberQuest < quizGames.size) numAnswers[numberQuest-1] = numberAnswer
+ }
+
+ // Счетчик ответов
+ fun checkAnswers(): Boolean {
+ return numAnswers.size == quizGames.size
+ }
+
+ // Метод написания отчета и подсчета правильных ответов
+ fun getResultMessage(): User {
+ var result = 0
+ var history = "История вопросов и ответов.\n"
+
+ for (x in 0..quizGames.lastIndex) {
+
+ val y = numAnswers[x]!!
+ if (quizGames[x].numberCorrectAnswer == numAnswers[x]) ++result
+ history += "\nВопрос: ${quizGames[x].question}\nВаш ответ: ${quizGames[x].answers[y - 1]}\nПравильный ответ: ${quizGames[x].answers[quizGames[x].numberCorrectAnswer - 1]}"
+ }
+
+ return User("jesus@haven.com", "Результат квиза: $result из 5.", history)
+ }
+
+ // Для создания фрагментов
+ fun getQuizObject(num: Int) : QuizObject {
+ return if (num <= quizGames.lastIndex) quizGames[num] else nullQuizObject
+ }
+
+ // Для ViewPager2
+ fun getSize() : Int = quizGames.lastIndex + 1
+
+ // Патерн Null-объект
+ private val nullQuizObject = QuizObject("nullQuizObject", listOf("nullQuizObject", "nullQuizObject", "nullQuizObject", "nullQuizObject", "nullQuizObject"), 1,1)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/rsschool/quiz/MainActivity.kt b/app/src/main/java/com/rsschool/quiz/MainActivity.kt
index 77bd5424..9e9627d4 100644
--- a/app/src/main/java/com/rsschool/quiz/MainActivity.kt
+++ b/app/src/main/java/com/rsschool/quiz/MainActivity.kt
@@ -1,11 +1,79 @@
package com.rsschool.quiz
-import androidx.appcompat.app.AppCompatActivity
+import android.content.Intent
import android.os.Bundle
+import androidx.fragment.app.FragmentActivity
+import androidx.viewpager2.widget.ViewPager2
+import com.rsschool.quiz.databinding.ActivityMainBinding
+
+
+const val EXTRA_MESSAGE = "com.rsschool.quiz.MESSAGE"
+
+// MainActivity Унаследуем от FragmentActivity.
+class MainActivity : FragmentActivity(), ActionListener {
+ // Объявим переменные для адаптера и вьюпейджера.
+ private lateinit var adapter: NumberAdapter
+ private lateinit var viewPager: ViewPager2
+
+ // Ресурсы
+ private lateinit var binding: ActivityMainBinding
+ private val dataQuiz = DataObjectAccess()
-class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
+
+ // Чтобы создать объект класса ResultProfileBinding, надо вызвать статический метод inflate()
+ binding = ActivityMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ // В методе onCreate инициализируем адаптер, передав ему текущее активити как владелец жизненного цикла
+ adapter = NumberAdapter(this)
+
+ // находим вьюпейджер по идентификатору и передаем ему адаптер.
+ viewPager = binding.pager
+ viewPager.adapter = adapter
+
+ // Слушаем изменение фрагментов и меняем цвет statusBar
+ viewPager.registerOnPageChangeCallback(object:ViewPager2.OnPageChangeCallback(){
+ override fun onPageSelected(position: Int) {
+ when(position){
+ 0 -> window.statusBarColor = getColor(R.color.secondaryDarkColorPurple)
+ 1 -> window.statusBarColor = getColor(R.color.secondaryDarkColorBlue)
+ 2 -> window.statusBarColor = getColor(R.color.secondaryDarkColorGreen)
+ 3 -> window.statusBarColor = getColor(R.color.secondaryDarkColorYellow)
+ 4 -> window.statusBarColor = getColor(R.color.secondaryDarkColorOrange)
+ else -> window.statusBarColor = getColor(R.color.secondaryDarkColor)
+ }
+ }
+ }
+ )
+ }
+
+ // Методы для кнопок, менять фрагменты
+ override fun nextFragment() {
+ ++viewPager.currentItem
}
+
+ override fun backFragment() {
+ --viewPager.currentItem
+ }
+
+ // Метод реализации меню результата закостылил созданием нового фрагмента и удалением предыдущего
+ override fun runResultFragment() {
+
+ val intent = Intent(this, ResultActivity::class.java).apply {
+ putExtra(EXTRA_MESSAGE, dataQuiz.getResultMessage())
+ }
+ startActivity(intent)
+ finish()
+ }
+
+ override fun addAnswer(numberQuest: Int, numberAnswer: Int) {
+ dataQuiz.addAnswer(numberQuest, numberAnswer)
+ }
+
+ override fun checkAnswersCount(): Boolean {
+ return dataQuiz.checkAnswers()
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/rsschool/quiz/NumberAdapter.kt b/app/src/main/java/com/rsschool/quiz/NumberAdapter.kt
new file mode 100644
index 00000000..0daa43fd
--- /dev/null
+++ b/app/src/main/java/com/rsschool/quiz/NumberAdapter.kt
@@ -0,0 +1,39 @@
+package com.rsschool.quiz
+
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import androidx.viewpager2.adapter.FragmentStateAdapter
+
+// Адаптер - специальный компонент, который связывает источник данных с виджетом списка.
+// Создадим адаптер, задачей которого будет предоставление фрагментов для слайдера:
+class NumberAdapter(private val fragment: FragmentActivity) : FragmentStateAdapter(fragment) {
+
+ // Набор данных, которые свяжем со списком
+ private val dao = DataObjectAccess()
+
+ // Требуется переопределить метод getItemCount, возвращающий общее количество элементов списка.
+ override fun getItemCount(): Int = dao.getSize()
+
+ // Переопределим метод createFragment, возвращающий фрагмент для каждого элемента слайдера.
+ override fun createFragment(position: Int): Fragment {
+
+ // Перед созданием фрагмента устанавливаем тему приложения
+ when (position) {
+ 0 -> fragment.theme?.applyStyle(R.style.Theme_Quiz_Purple, true)
+ 1 -> fragment.theme?.applyStyle(R.style.Theme_Quiz_Blue, true)
+ 2 -> fragment.theme?.applyStyle(R.style.Theme_Quiz_Green, true)
+ 3 -> fragment.theme?.applyStyle(R.style.Theme_Quiz_Yellow, true)
+ 4 -> fragment.theme?.applyStyle(R.style.Theme_Quiz_Orange, true)
+ else -> fragment.theme?.applyStyle(R.style.Theme_Quiz_First, true)
+ }
+
+ val fragment = BlankFragment()
+ fragment.arguments = Bundle().apply {
+ // Передаем данные в созданный фрагмент
+ putSerializable(ARG_OBJECT, dao.getQuizObject(position))
+ }
+ return fragment
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/rsschool/quiz/QuizObject.kt b/app/src/main/java/com/rsschool/quiz/QuizObject.kt
new file mode 100644
index 00000000..6cd18474
--- /dev/null
+++ b/app/src/main/java/com/rsschool/quiz/QuizObject.kt
@@ -0,0 +1,9 @@
+package com.rsschool.quiz
+
+import java.io.Serializable
+
+data class QuizObject(val question: String,
+ val answers: List,
+ val numberQuestion: Int,
+ val numberCorrectAnswer: Int
+) : Serializable
\ No newline at end of file
diff --git a/app/src/main/java/com/rsschool/quiz/ResultActivity.kt b/app/src/main/java/com/rsschool/quiz/ResultActivity.kt
new file mode 100644
index 00000000..c60e9368
--- /dev/null
+++ b/app/src/main/java/com/rsschool/quiz/ResultActivity.kt
@@ -0,0 +1,56 @@
+package com.rsschool.quiz
+
+import android.content.Intent
+import android.net.Uri
+import androidx.appcompat.app.AppCompatActivity
+import android.os.Bundle
+import android.view.View
+import com.rsschool.quiz.databinding.ActivityResultBinding
+
+class ResultActivity : AppCompatActivity() {
+
+ private lateinit var binding: ActivityResultBinding
+ private lateinit var userObject: User
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_result)
+
+ // Чтобы создать объект класса ResultProfileBinding, надо вызвать статический метод inflate()
+ binding = ActivityResultBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ // Get the Intent that started this activity and extract the string
+ userObject = intent.getSerializableExtra(EXTRA_MESSAGE) as User
+
+ // Capture the layout's TextView and set the string as its text
+ binding.textResult.apply {
+ text = userObject.resultMessage
+ }
+ }
+
+ // android:onClick
+ fun sendEmail(view: View) {
+
+ val intent = Intent(Intent.ACTION_SENDTO).apply {
+ data = Uri.parse("mailto:" + userObject.email) // only email apps should handle this
+ putExtra(Intent.EXTRA_SUBJECT, userObject.resultMessage)
+ putExtra(Intent.EXTRA_TEXT, userObject.resultQuiz)
+ }
+ if (intent.resolveActivity(packageManager) != null) {
+ startActivity(intent)
+ }
+ }
+
+ // android:onClick
+ fun restartQuiz(view: View) {
+ val intent = Intent(this, MainActivity::class.java)
+ startActivity(intent)
+ finish()
+ }
+
+ // android:onClick
+ fun exitApp(view: View) {
+ finish()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/rsschool/quiz/TestDAO.kt b/app/src/main/java/com/rsschool/quiz/TestDAO.kt
new file mode 100644
index 00000000..808b8e0c
--- /dev/null
+++ b/app/src/main/java/com/rsschool/quiz/TestDAO.kt
@@ -0,0 +1,25 @@
+package com.rsschool.quiz
+
+ fun main(args: Array){
+ println("Hello Kotlin")
+ val dataQuiz = DataObjectAccess()
+
+ dataQuiz.addAnswer(1, 1)
+ dataQuiz.addAnswer(2, 2)
+ dataQuiz.addAnswer(3, 3)
+ dataQuiz.addAnswer(4, 2)
+ dataQuiz.addAnswer(5, 2)
+
+ //println(dataQuiz.numAnswers.toString())
+
+ print("кол-во ответов: ")
+ //println(dataQuiz.numAnswers.size)
+ println(dataQuiz.checkAnswers())
+
+ if (dataQuiz.checkAnswers()) {
+ val result = dataQuiz.getResultMessage()
+ println(result.email)
+ println(result.resultMessage)
+ println(result.resultQuiz)
+ }
+ }
\ No newline at end of file
diff --git a/app/src/main/java/com/rsschool/quiz/User.kt b/app/src/main/java/com/rsschool/quiz/User.kt
new file mode 100644
index 00000000..377a7c8d
--- /dev/null
+++ b/app/src/main/java/com/rsschool/quiz/User.kt
@@ -0,0 +1,8 @@
+package com.rsschool.quiz
+
+import java.io.Serializable
+
+data class User(val email: String,
+ val resultMessage: String,
+ val resultQuiz: String
+) : Serializable
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 4fc24441..b5abed55 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -6,10 +6,10 @@
android:layout_height="match_parent"
tools:context=".MainActivity">
-
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_quiz.xml b/app/src/main/res/layout/fragment_quiz.xml
index de9e0693..3a5da981 100644
--- a/app/src/main/res/layout/fragment_quiz.xml
+++ b/app/src/main/res/layout/fragment_quiz.xml
@@ -3,7 +3,8 @@
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:layout_height="match_parent"
+ tools:context=".BlankFragment">
+ app:title="Question #" />
@@ -84,6 +85,7 @@
app:layout_constraintStart_toStartOf="@id/margin_start_guideline"
app:layout_constraintTop_toBottomOf="@id/question">
+
#d1c4e9
#fff7ff
#a094b7
+
+ #ff7043
+ #ffa270
+ #c63f17
+ #e64a19
+ #ff7d47
+ #ac0800
+ #000000
+ #000000
+
+ #ffa726
+ #ffd95b
+ #c77800
+ #f57c00
+ #ffad42
+ #bb4d00
+ #000000
+ #000000
+
+ #ffee58
+ #ffff8b
+ #c9bc1f
+ #fbc02d
+ #fff263
+ #c49000
+ #000000
+ #000000
+
+ #66bb6a
+ #98ee99
+ #338a3e
+ #388e3c
+ #6abf69
+ #00600f
+ #000000
+ #000000
+
+ #42a5f5
+ #80d6ff
+ #0077c2
+ #1976d2
+ #63a4ff
+ #004ba0
+ #000000
+ #ffffff
+
+ #ab47bc
+ #df78ef
+ #790e8b
+ #7b1fa2
+ #ae52d4
+ #4a0072
+ #ffffff
+ #ffffff
+
\ No newline at end of file
diff --git a/app/src/main/res/values/myThemes.xml b/app/src/main/res/values/myThemes.xml
new file mode 100644
index 00000000..2b4d8da4
--- /dev/null
+++ b/app/src/main/res/values/myThemes.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5e3624e9..0e25f404 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,8 @@
- Quiz
+ Hello blank fragment
+ Back
+ Next
+ Share
+ Результат квиза: $result из 5.
+ Exit
\ No newline at end of file
diff --git a/app/src/main/res/values/style.xml b/app/src/main/res/values/style.xml
new file mode 100644
index 00000000..74fd6aeb
--- /dev/null
+++ b/app/src/main/res/values/style.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index b8e5fc34..8202b4bb 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -24,4 +24,5 @@
- @color/yellow_100_dark
+