Kotlin не может получить доступ к элементам во фрагменте дважды

#android #kotlin #android-fragments

#Android #kotlin #android-фрагменты

Вопрос:

Рис. Один

Я просто создаю простое приложение. У меня есть MainActivity, FragmentOne и FragmentTwo. На картинке вы можете видеть, что я могу использовать FragmentOne и FragmentTwo в любое время. И с помощью кнопки «Обновить Textview» я могу изменить textview во fragmentOne. Пример: когда я запускаю приложение, я просто переключаюсь на фрагмент один, затем на два, снова на один и так далее… И когда я нахожусь во FragmentOne, я нажимаю кнопку «Обновить», и текстовое представление изменяется. Проблем нет. И я перехожу к FragmentTwo и возвращаюсь к FragmentOne и пытаюсь снова нажать кнопку обновления, ничего не меняется.

Вкратце, вы можете видеть на картинке текстовое представление «СОЗДАННЫЙ ФРАГМЕНТ», я могу изменить его на один раз, я не могу изменить снова. Я не менял коды фрагмента. Это моя основная активность:

 class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    goFragmentOne.setOnClickListener {
        val myAction = supportFragmentManager.beginTransaction()
        myAction.replace(R.id.myFrameLayout,FragmentOne()).commit()
    }
    goFragmentTwo.setOnClickListener {
        val myAction = supportFragmentManager.beginTransaction()
        myAction.replace(R.id.myFrameLayout,FragmentTwo()).commit()
    }
    btnUpdate.setOnClickListener {
        textViewOne.text = "UPDATED"
    }
  }
}
 

Это мой MainActivity XML:

 <FrameLayout> 
    <Button
        android:id="@ id/goFragmentOne"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="GO FRAGMENT ONE"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@ id/goFragmentTwo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="GO FRAGMENT TWO"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <Button
        android:id="@ id/btnUpdate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="UPDATE TEXTVIEW"
        app:layout_constraintBottom_toTopOf="@ id/goFragmentOne"
        app:layout_constraintStart_toStartOf="parent" />

    <FrameLayout
        android:id="@ id/myFrameLayout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@ id/btnUpdate"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

</FrameLayout>
 

Комментарии:

1. Пожалуйста, добавьте код фрагмента.

2. Вероятно, вы меняете textview в методе onCreate.

3. Я не внес никаких изменений во фрагменты. В onCreate есть только это: «super.onCreate(savedInstanceState)». Я только что отредактировал MainActivity

4. Вы хотите изменить текстовое представление, которое находится внутри фрагмента, из действия. Лучший способ — изменить представления фрагмента изнутри фрагмента.

5. Создайте функцию внутри фрагмента, которая будет отвечать за изменение его представлений. Внутренняя активность пожалуйста, используйте следующий код для вызова функции внутри фрагмента. Фрагмент фрагмента = getFragmentManager().findFragmentByTag(FRAGMENT_TAG) фрагмент.functionHere();

Ответ №1:

Вы фиксируете старое текстовое представление внутри действия. После замены и воссоздания фрагментов старого объекта просмотра больше нет. то, что вы видите на экране, — это новое текстовое представление с новой ссылкой, но значение, которое вы записали в OnClick lambda, не соответствует этому новому значению. Попробуйте получить представление из фрагментов с помощью чего-то вроде

 currentlyLoadedFragment.view.findViewById(R.id.theTextViewID) // or similar
 

Комментарии:

1. Я новичок в Kotlin, так что я должен написать в «currentlyLoadedFragment»?

2. Когда вы выполняете транзакцию фрагмента, сохраните последний размещенный фрагмент в локальной переменной вашего действия и назовите переменную var currentlyLoadedFragment: Fragment? = null , чтобы вы могли использовать ее в методе onClick. Обратите внимание, что перед помещением любого фрагмента в действие у вас не будет фрагмента для установки текста

Ответ №2:

Вам следует немного больше разделить свой код. То, как у вас это сейчас, — плохая практика. «Действия» для FragmentA -> ваш btnUpdate должен быть только во FragmentA, поскольку он предназначен только для вашего FragmentA. Используя архитектуру single activity, вы должны поместить в свой MainActivity onCreate только это

 /*
You implement interfaces from FragmentA, and FragmentB 
( interface Callbacks{....} that were written in FragmentA and FragmentB )
 and then Android Studio will require you to implement methods defined in 
those interfaces (methods are goToFragmentA() and goToFragmentB()
*/
class MainActivity : AppCompatActivity(), FragmentA.Callbacks, FragmentB.Callbacks {

   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

//your starting layout, will put fragmentA as your starting layout if your fragment container is empty
        val currentFragmentOnScreen = supportFragmentManager.findFragmentById(R.id.myFrameLayout)
        if (currentFragmentOnScreen == null) {
            val fragment = FragmentA()
            supportFragmentManager.beginTransaction()
                .add(R.id.myFrameLayout, fragment)
                .commit()
        }
    }

//calls your function from FragmentB and goes to FragmentA on button click
   override fun goToFragmentA(){
      val fragment = FragmentA()
        supportFragmentManager
            .beginTransaction()
            .replace(R.id.myFrameLayout, fragment)
            .commit()
   }

//calls your function from FragmentA and goes to FragmentB on button click
   override fun goToFragmentB(){
      val fragment = FragmentB()
        supportFragmentManager
            .beginTransaction()
            .replace(R.id.myFrameLayout, fragment)
            .commit()
   }

 

затем в вашем FragmentA ()

 class FragmentA() : Fragment() {
   ...
   interface Callbacks {
      fun goToFragmentB()
   }
   ...
   private var callbacks: Callbacks? = null
   ...
   override fun onAttach(context: Context) {
        super.onAttach(context)
        callbacks = context as Callbacks?
    }
   ...
   override fun onStart() {
      ...
      btn.OnClickListener{
         callbacks?.goToFragmentB()
      }
      btnText.OnClickListener{
         textViewOne.text = "UPDATED"
      ...
   }
   ...
   override fun onDetach() {
        super.onDetach()
        callbacks = null
    }
   ...
 

И FragmentB

 class FragmentB() : Fragment() {
   ...
   interface Callbacks {
      fun goToFragmentA()
   }
   ...
   private var callbacks: Callbacks? = null
   ...
   override fun onAttach(context: Context) {
        super.onAttach(context)
        callbacks = context as Callbacks?
    }
   ...
   override fun onStart() {
      ...
      btn.OnClickListener{
         callbacks?.goToFragmentA()
      }
      ...
   }
   ...
   override fun onDetach() {
        super.onDetach()
        callbacks = null
    }
   ...
 

Комментарии:

1. Да, я могу это сделать, но нет никакого способа решить мою проблему? Например, что, если я создам таймер в MainActivity для изменения TextView каждую секунду. Если я использую таймер во фрагменте, таймер работает только при отображении фрагмента. Поэтому я хочу отказаться от своей деятельности.

Ответ №3:

Хорошо, я пишу этот код и работаю:

  btnUpdate.setOnClickListener {
        val tv = supportFragmentManager.findFragmentById(R.id.myFrameLayout)?.view?.findViewById<TextView>(R.id.textViewOne)
        if (tv != null) {
            tv.text = "UPDATED"
        }
    }
 

Спасибо, что помогли мне, ребята!