Reputation: 91
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
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
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
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