Tongireth
Tongireth

Reputation: 91

Android Kotlin: Custom view causing crash on changing fragments

In my project I have three fragments attached to an activity, and one of these fragments, again, fragments that display texts using custom views. So they are basically fragments in a fragment.

In the main fragment I put these codes to switch between text-displaying fragments. It is triggered on codes passed by its child fragments.

private fun diagCommence(target: Fragment) {
    val transaction = activity?.supportFragmentManager?.beginTransaction()
    if (transaction != null) {
        transaction.replace(R.id.ig1_Diag_Layout, target)
        transaction.disallowAddToBackStack()
        transaction.commit()
    }
}

And the text-displaying fragments has custom views - AppCompatTextView - that displays texts.

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.layoutx2, container, false)
}
override fun onStart() {
    super.onStart()
    val scene = getString(R.string.sceneX2)
    tv01.commenceOnLifecycle(SceneX2(), scene, textanimspeed.toLong(), textanim) // the custom view that displays texts, triggers NPE
}

I have three identical text-displaying fragments for now and the first text-displaying fragment shows up okay. But when I try pass value to the main fragment and trigger diagCommence() the app crashes. What was strange is that when I try to get to the second text-displaying fragment it gives me Null Pointer Exception.

According the log the error was invoked in commenceOnLifecycle() method, which calls the AppCompatTextView I made. Moving it to different lifecycles such as onCreate(), onViewCreated() seems not help this problem. I'm wondering what am I missing?

Edit: The layout XML of the SceneX2, which is a text-displaying fragment:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/x2Layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

<com.example.textia1.TypeWriterView
    android:id="@+id/x2Twv01"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textColor="@color/colorAccent" />

<TextView
    android:id="@+id/x2Choice01"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/X2toX1" />

<TextView
    android:id="@+id/x2Choice02"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/X2toX3" />

</LinearLayout>

and the custom TextView that displays text with typewriting animation:

class TypeWriterView: AppCompatTextView {
  constructor(context: Context) : super(context)
  constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)

  suspend fun commence(txt: CharSequence?, delay: Long, animOnOff: Boolean) {
    if (txt != null) {
        if(animOnOff) {
            var index = 0
            while (index < txt.length) {
                text = txt.subSequence(0, ++index)
                delay(delay)
            }
        } else {text = txt}
    } else return
  }
  fun commenceOnLifecycle(owner: LifecycleOwner, txt: CharSequence?, delay: Long, animOnOff: Boolean): Job =
    owner.lifecycleScope.launch { commence(txt, delay, animOnOff) }
}

Upvotes: 0

Views: 543

Answers (3)

lpizzinidev
lpizzinidev

Reputation: 13274

Try to replace your diagCommence function code with

activity?.supportFragmentManager?.beginTransaction()
        ?.replace(R.id.ig1_Diag_Layout, target)
        ?.addToBackStack(null)
        ?.commit()

Also, you should initialize your tv01 View in onCreateView. First, declare a global variable

private var tv01: TypeWriterView? = null

and edit your onCreateView method

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    val view = inflater.inflate(R.layout.layoutx2, container, false)
    
    tv01 = view.findViewById(R.id.x2Twv01)

    val scene = requireActivity().getString(R.string.sceneX2)
    tv01?.commenceOnLifecycle(SceneX2(), scene, textanimspeed.toLong(), textanim) // the custom view that displays texts
    
    return view
}

EDIT

Modify your TypeWriterView declaration as

class TypeWriterView(
    context: Context,
    attrs: AttributeSet
) : AppCompatTextView(context, attrs) {

Upvotes: 1

Krishna Sony
Krishna Sony

Reputation: 1369

Make sure you use childFragmentManager because childFragmentManager is associated with fragments like supportFragmentManager is associated with activity

private fun diagCommence(target: Fragment) {
    val transaction = childFragmentManager.beginTransaction()
    if (transaction != null) {
        transaction.replace(R.id.ig1_Diag_Layout, target)
        transaction.disallowAddToBackStack()
        transaction.commit()
    }
}

Upvotes: 0

sadat
sadat

Reputation: 4352

If you are in activity, you should use fragmentManager. please change this line

    val nextScene = activity?.supportFragmentManager?.beginTransaction()

to

    val nextScene = fragmentManager?.beginTransaction()

Upvotes: 0

Related Questions