I would like to call Activity from Fragment to realize both transitions among fragments and activities simultaneously in an Android app.
I have succeeded to call one specific method on activity file from fragment file, but I have trouble with calling an entire activity from a fragment.
Basic Functions on each page
SampleFragment - MainActivity's fragmentMethod() is called once the button is clicked on the page
Sample1Fragment - FormActivity is called once the button is clicked on the page
FormActivity - FormActivity2 is called once the button is clicked on the page
On Sample1Fragment.kt
, I tried to call Entire FormActivity on the below part.
However the error message is shown and I have no idea to fix it to call the entire activity rather than a method on an activity.
Unresolved reference: AppCompatActivity
The part where I tried to call FromActivity from Sample1Fragment
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
btnClick2.setOnClickListener(object:View.OnClickListener{
// here I would like to move to FormActivity
override fun onClick(v: View?) {
(activity as FormActivity).AppCompatActivity()
}
})
}
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// make the list of fragment
val fragmentList = arrayListOf<Fragment>(
SampleFragment(),
Sample1Fragment()
)
// create instance of adapter
val adapter = SamplePagerAdapter(supportFragmentManager, fragmentList)
/// set adapter
viewPager.adapter = adapter
}
public fun fragmentMethod() {
Toast.makeText(this@MainActivity, "Method called From Fragment", Toast.LENGTH_LONG).show();
}
}
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.fragment_sample.*
class SampleFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_sample, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
btnClick.setOnClickListener(object:View.OnClickListener{
override fun onClick(v: View?) {
(activity as MainActivity).fragmentMethod()
}
})
}
}
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.fragment_sample1.*
class Sample1Fragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_sample1, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
btnClick2.setOnClickListener(object:View.OnClickListener{
// here I would like to move to FormActivity
override fun onClick(v: View?) {
(activity as FormActivity).AppCompatActivity()
}
})
}
}
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
class SamplePagerAdapter(fm: FragmentManager, private val fragmentList: List<Fragment>) :
FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
// control fragment to show
override fun getItem(position: Int): Fragment {
return fragmentList[position]
}
// the size of contents(fragment list) to set viewPager
override fun getCount(): Int {
return fragmentList.size
}
}
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.app.Activity
import android.content.Intent
import android.util.Log
import kotlinx.android.synthetic.main.activity_form.*
class FormActivity : AppCompatActivity() {
companion object {
const val EXTRA_MESSAGE = "com.example.kotlinactivitydatatrans.MESSAGE"
}
private val RESULT_SUBACTIVITY = 1000
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
if (editText.text != null) {
val intent = Intent(applicationContext, FormActivity2::class.java)
val str = editText.text.toString()
Log.d("debug",str)
intent.putExtra(EXTRA_MESSAGE, str)
startActivityForResult(intent, RESULT_SUBACTIVITY)
editText.setText("")
}
}
}
// to get a result form SubActivity
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)
if (resultCode == Activity.RESULT_OK &&
requestCode == RESULT_SUBACTIVITY && intent != null) {
val res = intent.extras?.getString(EXTRA_MESSAGE)?: ""
textView.text = res
}
}
}
I can call MainActivity.fragmentMethod()
from SampleFragment and transition between SampleFragment and Sample1Fragment with the following reference.
Reference: Kotlin-How to call an activity method from fragment in Android App?
Android Studio 3.6.1
It was succeeded to build a project and run the app on the emulator. However, the screen was suddenly shut down when I click the button to call FormActivity on Sample1Fragment.
There is no error on codes so I cannot find any solutions.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.fragact">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".FormActivity"
android:label="@string/app_name"
tools:ignore="WrongManifestParent">
</activity>
<activity android:name=".FormActivity2"
android:label="@string/app_name"
tools:ignore="WrongManifestParent">
</activity>
</application>
</manifest>
fragment_sample1.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Sample1Fragment">
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:text="Sample1Fragment"
android:textSize="36sp" />
<Button
android:id="@+id/btnClick2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_gravity="center"
android:background="@color/colorPrimary"
android:textColor="#FFF"
android:textAllCaps="false"
android:minEms="8"
android:text="Call Activity"/>
</FrameLayout>
Logcat
2020-03-10 18:50:48.622 9917-9917/com.example.fragact W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@f79aa98
2020-03-10 20:50:48.659 9917-9917/com.example.fragact D/AndroidRuntime: Shutting down VM
2020-03-10 20:50:48.661 9917-9917/com.example.fragact E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.fragact, PID: 9917
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.fragact/com.example.fragact.FormActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at com.example.fragact.FormActivity.onCreate(FormActivity.kt:22)
at android.app.Activity.performCreate(Activity.java:7136)
at android.app.Activity.performCreate(Activity.java:7127)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
2020-03-10 20:50:48.679 9917-9917/com.example.fragact I/Process: Sending signal. PID: 9917 SIG: 9
I think the problem here is that you're trying to set onClick listener too early.
Fragments, unlike Activities, has different lifecycle and situations when their View
isn't created yet (or already destroyed) is much more easy to get.
Check out this documentation for more info.
In general - best way to start interacting with views is the onViewCreated
lifecycle callback. Here, and afterward, you can be sure that the view hierarchy is ready.
In your example, you've used onActivityCreated
callback to start interacting with views. In particular, setting onClick listener - btnClick.setOnClickListener(...)
in the SampleFragment
. And in the first fragment, I assume, it worked well because its creation was "aligned" with activity creation.
But in the case of the second fragment - Sample1Fragment
- activity was already created, which probably resulted in wrong method ordering. I assume, onActivityCreated
was called earlier that onViewCreated
(on onCreateView
). And calling btnClick2.setOnClickListener(...)
resulted in crash, cause btnClick2
isn't initialized yet.
My suggestion is to move view related code of your Fragments into onViewCreated
method(s). Having something like this:
override fun onViewCreated(view: View, savedInstanceState: Bundle) {
btnClick2.setOnClickListener(...)
}