currently I have a custom drawer activity that extends AppCompatActivity, which looks like the following:
public class DrawerActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_drawer);
...
}
@Override
public void setContentView(int layoutResID) {
frameLayout = findViewById(R.id.drawer_frame);
LayoutInflater.from(getApplicationContext()).inflate(layoutResID, frameLayout);
super.setContentView(drawerLayout);
}
...
}
As you can see here, I overwrite the setContentView method, so the layout id of the activity that extend this drawer activity will be passed into this method and inflated in the drawer activity's subview. In a simple word, I want all activities that extend this drawer activity will have a navigation drawer. It works well for general acitivity.
But now, I have an activity that contains a fragment container view and extend the drawer activity. When I launch this activity, the application crashes with the following error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.aaa, PID: 10730
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.aaa/com.example.aaa.gym.GymActivity}: android.view.InflateException: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Error inflating class androidx.fragment.app.FragmentContainerView
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3635)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3792)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: android.view.InflateException: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Error inflating class androidx.fragment.app.FragmentContainerView
Caused by: android.view.InflateException: Binary XML file line #29 in com.example.aaa:layout/activity_gym: Error inflating class androidx.fragment.app.FragmentContainerView
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at android.view.LayoutInflater.createView(LayoutInflater.java:858)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1010)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:965)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1127)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)
at android.view.LayoutInflater.inflate(LayoutInflater.java:686)
at android.view.LayoutInflater.inflate(LayoutInflater.java:538)
at android.view.LayoutInflater.inflate(LayoutInflater.java:485)
at com.example.aaa.DrawerActivity.setContentView(DrawerActivity.java:57)
at com.example.aaa.gym.GymActivity.onCreate(GymActivity.java:62)
at android.app.Activity.performCreate(Activity.java:8051)
at android.app.Activity.performCreate(Activity.java:8031)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3608)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3792)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: java.lang.UnsupportedOperationException: FragmentContainerView must be within a FragmentActivity to use android:name="com.example.aaa.gym.PlaceFragment"
E/AndroidRuntime: at androidx.fragment.app.FragmentContainerView.<init>(FragmentContainerView.java:142)
at androidx.fragment.app.FragmentContainerView.<init>(FragmentContainerView.java:120)
... 28 more
As you can see here, the root cause is java.lang.UnsupportedOperationException: FragmentContainerView must be within a FragmentActivity to use android:name="com.example.aaa.gym.PlaceFragment"
. However, the activity should be able to inflate fragments as it extends DrawerActivity and DrawerActivity extends AppCompatActivity, which extends Fragment Activity. I'm very confused about this.
Here is the activity code:
public class GymActivity extends DrawerActivity implements OnMapReadyCallback, GoogleMap.OnMarkerClickListener {
private ActivityGymBinding binding;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
markers = new ArrayList<>();
binding = ActivityGymBinding.inflate(getLayoutInflater());
// here crashes, the actual crash line is the second line of the drawer activity's setContentView
setContentView(binding.getRoot().getSourceLayoutResId());
...
}
...
}
And this is the activity xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@ id/mapLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".gym.GymActivity">
<com.google.android.gms.maps.MapView
android:id="@ id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/title_activity_gym"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView
android:id="@ id/place_fragment"
android:name="com.example.aaa.gym.PlaceFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout="@layout/place_fragment" />
</androidx.constraintlayout.widget.ConstraintLayout>
And the fragment, nearly nothing changed
public class PlaceFragment extends Fragment {
private PlaceViewModel mViewModel;
private PlaceFragmentBinding binding;
public static PlaceFragment newInstance() {
return new PlaceFragment();
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
mViewModel = new ViewModelProvider(requireActivity()).get(PlaceViewModel.class);
mViewModel.getName().observe(getViewLifecycleOwner(), s -> binding.name.setText(s));
mViewModel.getLocation().observe(getViewLifecycleOwner(), s -> binding.location.setText(s));
return inflater.inflate(R.layout.place_fragment, container, false);
}
}
fragment.xml, just a simple framelayout
<?xml version="1.0" encoding="utf-8"?>
<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=".gym.PlaceFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@ id/name"
android:text="Hello" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@ id/location"
android:text="Hello" />
</FrameLayout>
Can anyone see why my application crashes? Any suggestion would be appreciated.
CodePudding user response:
The problem is here:
LayoutInflater.from(getApplicationContext()).inflate(layoutResID, frameLayout);
You are using the application context - that is specifically not your Activity and hence, does not use the Activity's theme, nor does your Application class extend FragmentActivity
. You'll want to use LayoutInflater.from(this)
to actually use your Activity as the layout inflater.
However, while that will fix your crash, your GymActivity
still won't work because of these lines:
binding = ActivityGymBinding.inflate(getLayoutInflater());
// here crashes, the actual crash line is the second line of the drawer activity's setContentView
setContentView(binding.getRoot().getSourceLayoutResId());
Here, you create a binding
object, but never actually add it your Activity. Instead, you call through to your setContentView
override that inflates a brand new instance of that View. This means that any changes to your binding
won't ever be reflected in your UI.
Instead, you'd want to provide an override of your setContentView
that takes a View
and calls addView
on your FrameLayout to add the already inflated View to your layout.
@Override
public void setContentView(View view) {
frameLayout = findViewById(R.id.drawer_frame);
frameLayout.addView(view)
// Note you should never be calling super.setContentView here.
// You've already called it in your own onCreate()
}
Which lets you use it like:
binding = ActivityGymBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());