【问题标题】:Multiple Fragments getting created using a viewpager使用 viewpager 创建多个片段
【发布时间】:2021-10-07 11:57:44
【问题描述】:

当这个特定片段打开时,列表第二项的数据(问题模型)被膨胀到 ui 并且生命周期方法也被调用两次。

我也进行了滑动刷新以进行检查。刷新后对应的数据就来了。

如果我滑动到第 3 或第 4 位置并在一段时间后返回第一页,那么正确的数据也会膨胀。

寻呼机适配器类

import android.util.Log
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import androidx.viewpager.widget.ViewPager
import com.iisc.englishgyani.fragments.*
import com.iisc.englishgyani.models.topic_details.PracticeModel

class TopicPracticeAdapter(var fragmentManager: FragmentManager, var practiceList: ArrayList<PracticeModel>, var topic_name: String, var topicId: Int): FragmentPagerAdapter(fragmentManager) {

    private val TAG = TopicPracticeAdapter::class.java.simpleName

    init {
        Log.d(TAG, "TopicPracticeAdapter: $practiceList")
        Log.d(TAG, "topic_name: $topic_name")
    }

    override fun getCount() = practiceList.size

    override fun getItem(position: Int): Fragment {
        return when {
            practiceList.get(position).type.equals("text") -> {
                FragmentTutorialText.newInstance(practiceList.get(position).tutorialModel, topic_name)
            }
            practiceList.get(position).type.equals("video") -> {
                FragmentTutorialVideo.newInstance(practiceList.get(position).tutorialModel, topic_name)
            }
            practiceList.get(position).type.equals("link") -> {
                FragmentTutorialDoc.newInstance(practiceList.get(position).tutorialModel, topic_name)
            }
            practiceList.get(position).type.equals("MCQ") -> {
                FragmentMCQ.newInstance(practiceList.get(position).questionModel, topicId)
            }
            practiceList.get(position).type.equals("FIB") -> {
                FragmentMCQ.newInstance(practiceList.get(position).questionModel, topicId)
            }
            practiceList.get(position).type.equals("DND") -> {
                return FragmentDragAndDrop().newInstance(practiceList.get(position).questionModel, topicId)
            }
            practiceList.get(position).type.equals("MTF") -> {
                FragmentMatchTheFollowing.newInstance(practiceList.get(position).questionModel, topicId)
            }
            else -> {
                FragmentTutorialText.newInstance(practiceList.get(position).tutorialModel, topic_name)
            }
        }
    }
}

FragmentDragAndDrop


import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import com.google.android.flexbox.*
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.iisc.englishgyani.adapters.SentenceAdapter
import com.iisc.englishgyani.adapters.WordsAdapter
import com.iisc.englishgyani.callbacks.DropListener
import com.iisc.englishgyani.databinding.FragmentDragAndDropBinding
import com.iisc.englishgyani.interfaces.NextFragment
import com.iisc.englishgyani.models.answer.AnswerResponse
import com.iisc.englishgyani.models.topic_details.QuestionModel
import com.iisc.englishgyani.network.ApiService
import com.iisc.englishgyani.network.ApiUtils
import com.iisc.englishgyani.utils.Constants.Companion.showNextFragment
import com.iisc.englishgyani.utils.NetworkHelper
import com.iisc.englishgyani.utils.ProgressDialogHelper
import okhttp3.MediaType
import okhttp3.RequestBody
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER

private const val ARG_QUESTION_MODEL = "questionModel"
private const val ARG_TOPIC_ID = "topicId"

private val words = ArrayList<String>()
private val sentence = mutableListOf<String>()

class FragmentDragAndDrop : Fragment() {

    private lateinit var nextFragment: NextFragment
    private var _binding: FragmentDragAndDropBinding? = null
    private val binding get() = _binding!!

    private var selectedWord = ""
    private val TAG = FragmentDragAndDrop::class.java.simpleName

    // TODO: Rename and change types of parameters
    private var questionModel: QuestionModel? = null
    private var topicId: Int? = null

    private var mAPIService: ApiService? = null
    var loader: ProgressDialogHelper? = null

    override fun onAttach(context: Context) {
        super.onAttach(context)
        try {
            nextFragment = context as NextFragment
        } catch (e: ClassCastException) {
            throw ClassCastException((context as Activity).localClassName
                    + " must implement OnButtonClickListener")
        }
        Log.d(TAG, "onAttach: ")
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(TAG, "onCreate: ")
        mAPIService = ApiUtils.getApiService()
        loader = context?.let {
            ProgressDialogHelper(it)
        }
    }

    private fun setUI(){

        Log.d(TAG, "setUI showNextFragment: $showNextFragment")

//        if(showNextFragment) {
//
//            showNextFragment = false

            activity?.actionBar?.title = questionModel?.qid.toString()

            Log.d(TAG, "setUI backStackEntryCount: ${activity?.supportFragmentManager?.backStackEntryCount}")

            binding.fragmentSwipe.isRefreshing = false
            arguments?.let {
                questionModel = it.getParcelable(ARG_QUESTION_MODEL)
                topicId = it.getInt(ARG_TOPIC_ID)
            }

            Log.d(TAG, "setUI: $questionModel")
            Log.d(TAG, "setUI: $topicId")
            Log.d(TAG, "setUI qid: ${questionModel?.qid}")

            binding.fragmentSwipe.setOnRefreshListener {
                setUI()
            }

            words.clear()
            questionModel?.choices?.forEach {
                words.add(it)
            }

            val sentenceAdapter = SentenceAdapter {
                selectedWord = it
            }.apply {
                submitList(sentence)
            }

            val wordsAdapter = WordsAdapter {
                selectedWord = it
            }.apply {
                submitList(words)
            }

            val layoutManager = FlexboxLayoutManager(context, FlexDirection.ROW, FlexWrap.WRAP).apply {
                justifyContent = JustifyContent.FLEX_START
                alignItems = AlignItems.FLEX_START
            }

//        layoutManager.orientation = GridLayoutManager.VERTICAL

            binding.tvInst.text = questionModel?.desc
            binding.tvQues.text = questionModel?.question?.get(0)?.question

            binding.rvSentence.layoutManager = layoutManager

            binding.rvSentence.adapter = sentenceAdapter

            binding.rvSentence.setOnDragListener(
                    DropListener {
                        Log.d(TAG, "setUI selectedWord: $selectedWord")
                        Log.d(TAG, "setUI sentenceAdapter: ${sentenceAdapter.currentList}")
                        Log.d(TAG, "setUI wordsAdapter: ${wordsAdapter.currentList}")
                        wordsAdapter.removeItem(selectedWord)
                        if (!sentence.contains(selectedWord)) {
                            sentenceAdapter.addItem(selectedWord)
                        }
//                    sentenceAdapter.notifyDataSetChanged()
//                    wordsAdapter.notifyItemRemoved(words.indexOf(selectedWord))
                    }
            )
            binding.rvWords.layoutManager = FlexboxLayoutManager(context, FlexDirection.ROW, FlexWrap.WRAP).apply {
                justifyContent = JustifyContent.FLEX_START
                alignItems = AlignItems.FLEX_START
            }

            binding.rvWords.adapter = wordsAdapter

            binding.rvWords.setOnDragListener(
                    DropListener {
                        sentenceAdapter.removeItem(selectedWord)
                        wordsAdapter.addItem(selectedWord)
                    }
            )

            binding.cardSubmit.setOnClickListener {
                val selectedAnswer = StringBuffer()
                sentenceAdapter.currentList.forEach {
//                selectedAnswer.commonPrefixWith(" ", sentenceAdapter.currentList.indexOf(it) == sentenceAdapter.currentList.size)
                    selectedAnswer.append("$it ")
                }
                Log.d(TAG, "setUI:${selectedAnswer.lastIndex}")
                Log.d(TAG, "setUI:${selectedAnswer.length}")
                Log.d(TAG, "setUI:${selectedAnswer.substring(0, selectedAnswer.lastIndex)}")
                val choice = ArrayList<String>()
                choice.add(selectedAnswer.substring(0, selectedAnswer.lastIndex))
                validate(choice)
            }
//        }

    }

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

        Log.d(TAG, "onCreateView: ")
        _binding = FragmentDragAndDropBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        setUI()

        Log.d(TAG, "onViewCreated: ")

        Log.d(TAG, "onViewCreated questionModel: $questionModel")
        Log.d(TAG, "onViewCreated topicId: $topicId")

    }

    private fun validate(choice: ArrayList<String>?) {

        // req -> {topic_id, qid, type, category, email_id, question array -> {question, user_answer}}
        // res <- {question array, type}

        try {
            if (NetworkHelper.isNetworkAvailable(context!!)) {
                submitAnswer(choice)
            } else {
                Toast.makeText(context, "Connect to internet", Toast.LENGTH_SHORT).show()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }

    private fun submitAnswer(choice: ArrayList<String>?) {

        loader?.showProgressDialog()

        val jsonObject = JSONObject()
        val question = JSONArray()
//        question.put(questionModel?.question?.get(0)?.question?.let {
//            choice?.let { choice ->
//                QAPairModel(it, choice.get(0))
//            }
//        })

        val jsonObjectQuestion = JSONObject()
        jsonObjectQuestion.put("question", questionModel?.question?.get(0)?.question)
        jsonObjectQuestion.put("user_answer", choice?.get(0))

        question.put(jsonObjectQuestion)

        try {
            val account = GoogleSignIn.getLastSignedInAccount(context)
            account?.let {
                jsonObject.put("topic_id", topicId)
                jsonObject.put("type", questionModel?.type)
                jsonObject.put("qid", questionModel?.qid)
                jsonObject.put("email_id", account.email)
                jsonObject.put("category", "grammer")
                jsonObject.put("question", question)
            }

        } catch (e: JSONException) {
            e.printStackTrace()
        }
        Log.d(TAG, "submitAnswer: json object $jsonObject")

        val body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonObject.toString())

        Log.d(TAG, "onResponse RequestBody: $body")

        mAPIService = ApiUtils.getApiService()
        mAPIService?.answer(body)?.enqueue(object : Callback<AnswerResponse> {
            override fun onResponse(call: Call<AnswerResponse>, response: Response<AnswerResponse>) {
                Log.d(TAG, "onResponse: ${response.body()}")
                if (response.isSuccessful) {
                    loader?.hideProgressDialog()
                    try {
                        Toast.makeText(context, response.body()?.message, Toast.LENGTH_SHORT).show()
                        nextFragment.showNextFragment()
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }
                }
            }

            override fun onFailure(call: Call<AnswerResponse>, t: Throwable) {
                loader?.hideProgressDialog()
                Log.d(TAG, "onFailure: ${t.message}")
            }

        })

    }

//    companion object {
//        /**
//         * Use this factory method to create a new instance of
//         * this fragment using the provided parameters.
//         *
//         * @param questionModel Parameter 1.
//         * @param topicId Parameter 2.
//         * @return A new instance of fragment FragmentDragAndDrop.
//         */
//        // TODO: Rename and change types and number of parameters
//        @JvmStatic
//        fun newInstance(questionModel: QuestionModel?, topicId: Int) =
//                FragmentDragAndDrop().apply {
//                    arguments = Bundle().apply {
//                        putParcelable(ARG_QUESTION_MODEL, questionModel)
//                        putInt(ARG_TOPIC_ID, topicId)
//                    }
//                }
//    }


    fun newInstance(questionModel: QuestionModel?, topicId: Int) =
            FragmentDragAndDrop().apply {
                arguments = Bundle().apply {
                    putParcelable(ARG_QUESTION_MODEL, questionModel)
                    putInt(ARG_TOPIC_ID, topicId)
                }
            }

    override fun onPause() {
        super.onPause()
        Log.d(TAG, "onPause: ")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy: ")
    }

    override fun onResume() {
        super.onResume()
        Log.d(TAG, "onResume: ")
        setUI()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        Log.d(TAG, "onDestroyView: ")
    }

    override fun onStop() {
        super.onStop()
        Log.d(TAG, "onStop: ")
    }

}

【问题讨论】:

    标签: android kotlin android-fragments android-viewpager


    【解决方案1】:

    默认情况下,片段的左侧和右侧被预加载到 ViewPager 中。它不允许设置小于 1 的“离屏页面限制”,但在 Viewpager 2 中,您可以将最小离屏页面限制设置为零。所以只会加载当前片段。

    • viewPager2.setOffscreenPageLimit(0);

    【讨论】:

    • 没用。实际上,正在生成 2 个片段实例和 2 个独立的生命周期。但是第二个项目的数据都进来了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-03
    相关资源
    最近更新 更多