Foggy day

Android(Kotlin) - question example using assets file - 1 본문

Android

Android(Kotlin) - question example using assets file - 1

jinhan38 2021. 3. 14. 21:12

 

 

This article is question example using assets file.

The purpose of creation is to make a survey Form requires a variety of questions and answers

The used things are Assets File(Json), Viewpager2, RecyclerView, Fragment navigation graph, Gson, ViewBinding, Parcelize, EventBus and so on.

 

 

 

Video

 

 

 

 

 

 

 

 

 

1. package structure

 

 

 

 

 

- survey_nav_graph.xml

 

 

 

2. Navigation graph container activity

 

- ViewPagerQuestionBaseActivity

It is activity for a container of fragments and don't have any code.

class ViewPagerQuestionBaseActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_pager_question_base)
    }
}

 

- activity_view_pager_question_base.xml 

Add FragmentContainerView and some properties.

<?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=".viewPager.survey.container.ViewPagerQuestionBaseActivity">


    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/survey_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/survey_nav_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

 

3. assets file

- survey.json

I made a json assets file with 3 questions.

{
  "totalScore": 3,
  "list": [
    {
      "number": 1,
      "question": "5 + 5 ?",
      "answerList": [
        {
          "answer": false,
          "item": "1. 5"
        },
        {
          "answer": false,
          "item": "2. 6"
        },
        {
          "answer": false,
          "item": "3. 7"
        },
        {
          "answer": false,
          "item": "4. 8"
        },
        {
          "answer": true,
          "item": "5. 10"
        }
      ]
    },
    {
      "number": 2,
      "question": "4 X 7 ?",
      "answerList": [
        {
          "answer": false,
          "item": "1. 11"
        },
        {
          "answer": false,
          "item": "2. 17"
        },
        {
          "answer": false,
          "item": "3. 15"
        },
        {
          "answer": true,
          "item": "4. 28"
        },
        {
          "answer": false,
          "item": "5. 27"
        }
      ]
    },
    {
      "number": 3,
      "question": "20 / 4 ?",
      "answerList": [
        {
          "answer" : true,
          "item" : "1. 5"
        },{
          "answer" : false,
          "item" : "2. 6"
        },{
          "answer" : false,
          "item" : "3. 7"
        },{
          "answer" : false,
          "item" : "4. 8"
        },{
          "answer" : false,
          "item" : "5. 9"
        }
      ]
    }
  ]
}

 

 

 

 

4. Model (data class, enum class)

 

- SurveyListModel

This model is the data class for reading 'survey.json' file.

@Parcelize
data class SurveyListModel(

    var totalScore: Int,
    var type: Int,
    var list: ArrayList<List>
) : Parcelable {

    @Parcelize
    data class List(
        var number: Int,
        var question: String,
        var answerList: ArrayList<AnswerList>
    ) : Parcelable {

        @Parcelize
        data class AnswerList(
            var answer: Boolean,
            var item: String,
        ) : Parcelable
    }
}

 

-SurveyType

This example have four question list. But I implemented question one.

enum class SurveyType {
    QUESTION_ONE,
    QUESTION_TWO,
    QUESTION_THREE,
    QUESTION_FOUR,
}

 

- SurveyChoiceModel

This model is needed when you click item of question. 

data class SurveyChoiceModel(
    var questionNumber: Int,
    var clickedNumber: Int,
)

 

 

 

5. startFragment

 

Layout image

 

- fragment_survey_start.xml

<?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=".viewPager.survey.start.SurveyStartFragment">


    <TextView
        android:id="@+id/textView4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100dp"
        android:text="Welcome Survey"
        android:textColor="@color/black"
        android:textSize="40dp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


    <LinearLayout
        android:layout_width="match_parent"
        android:paddingTop="50dp"
        android:layout_height="0dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView4">


        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/btnSurvey1"
            android:layout_width="match_parent"
            android:layout_height="70dp"
            android:text="Survey 1"
            android:gravity="center_vertical"
            android:padding="0dp"
            android:textAllCaps="false"
            android:paddingStart="20dp"
            android:layout_marginHorizontal="30dp"
            android:textStyle="normal"
            android:textColor="@color/black"
            android:textSize="16dp"/>

        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/btnSurvey2"
            android:layout_width="match_parent"
            android:layout_height="70dp"
            android:text="Survey 2"
            android:gravity="center_vertical"
            android:padding="0dp"
            android:textAllCaps="false"
            android:paddingStart="20dp"
            android:layout_marginHorizontal="30dp"
            android:textStyle="normal"
            android:textColor="@color/black"
            android:textSize="16dp"/>

        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/btnSurvey3"
            android:layout_width="match_parent"
            android:layout_height="70dp"
            android:text="Survey 3"
            android:gravity="center_vertical"
            android:padding="0dp"
            android:textAllCaps="false"
            android:paddingStart="20dp"
            android:layout_marginHorizontal="30dp"
            android:textStyle="normal"
            android:textColor="@color/black"
            android:textSize="16dp"/>

        <androidx.appcompat.widget.AppCompatButton
            android:layout_width="match_parent"
            android:id="@+id/btnSurvey4"
            android:layout_height="70dp"
            android:text="Survey 4"
            android:gravity="center_vertical"
            android:padding="0dp"
            android:textAllCaps="false"
            android:paddingStart="20dp"
            android:layout_marginHorizontal="30dp"
            android:textStyle="normal"
            android:textColor="@color/black"
            android:textSize="16dp"/>

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

 

- SurveyStartFragment

If you click question one, you send question type and json file of the selected type to QuestionFragmentSurvey using Bundle, Stream, and Gson.

class SurveyStartFragment : Fragment(), View.OnClickListener {

    lateinit var b: FragmentSurveyStartBinding
    lateinit var mContext: Context


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View? {

        b = FragmentSurveyStartBinding.inflate(layoutInflater)
        mContext = requireContext()
        setupListener()

        return b.root
    }


    fun setupListener() {

        b.btnSurvey1.setOnClickListener(this)

    }


    override fun onClick(v: View?) {

        var surveyType: SurveyType? = null

        when (v!!.id) {
            R.id.btnSurvey1 -> {
                surveyType = SurveyType.QUESTION_ONE
            }
        }

        val bundle = Bundle()
        bundle.putString("surveyType", surveyType.toString())
        bundle.putParcelable("surveyData", getModel(surveyType!!))
        findNavController().navigate(R.id.action_surveyStartFragment_to_surveyQuestionFragment,
            bundle)
    }


    fun getModel(surveyType: SurveyType): SurveyListModel? {

        var surveyListModel: SurveyListModel? = null
        var jsonFileString: String? = null
        when (surveyType) {
            SurveyType.QUESTION_ONE -> {
                jsonFileString = Common.getJsonDataFromAsset(mContext, "survey.json")
                val gson = Gson()
                surveyListModel = gson.fromJson(jsonFileString, SurveyListModel::class.java)
            }
            SurveyType.QUESTION_TWO -> {

            }
            SurveyType.QUESTION_THREE -> {

            }
            SurveyType.QUESTION_FOUR -> {

            }

        }

        return surveyListModel
    }

}

 

- Common.getJsonDataFromAsset method

        fun getJsonDataFromAsset(context: Context, fileName: String): String? {
            val jsonString: String
            try {
                jsonString = context.assets.open(fileName).bufferedReader().use {
                    it.readText()
                }
            } catch (e: Exception) {
                Log.d("getJsonDataFromAsset", "getJsonDataFromAsset: $e ")
                return null
            }

            return jsonString

        }