使用 viewpager 创建多个片段
Posted
技术标签:
【中文标题】使用 viewpager 创建多个片段【英文标题】:Multiple Fragments getting created using a 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: ")
【问题讨论】:
【参考方案1】:默认情况下,片段的左侧和右侧被预加载到 ViewPager 中。它不允许设置小于 1 的“离屏页面限制”,但在 Viewpager 2 中,您可以将最小离屏页面限制设置为零。所以只会加载当前片段。
viewPager2.setOffscreenPageLimit(0);【讨论】:
没用。实际上,正在生成 2 个片段实例和 2 个独立的生命周期。但是第二个项目的数据都进来了。以上是关于使用 viewpager 创建多个片段的主要内容,如果未能解决你的问题,请参考以下文章
在android中使用viewpager的带有多个片段的SearchView