Prejith P Prejith P - 5 months ago 33
Android Question

Drawing fragment over main activity

I am new to developing android applications, so please pardon any stupid mistakes that I may have made. On to the question:
I am making an application which has its main activity layout screen containing some stuff and have a navigation drawer wired in. Now I am trying to use the entries in the navigation drawer to call up various fragments and display them over my main activity. However when I run my piece of code, I am getting an Activity Destroyed error I have attached my code below for reference. I think the problem is with what I have done with the container. To make the fragment cover the whole screen of my main activity, I gave the fragment container as the root layout as I was unsure how to go about this.

Code which handles item clicks from navigation drawer

class DrawerItemClickListener extends FragmentActivity implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}

private void selectItem(int position) {
Fragment mFragment = null;
switch(position) {
case 0:
mFragment = new AnnouncementFragment();
Bundle args = new Bundle();
args.putInt(null, position);
break;
case 1:
break;
case 2:
break;
case 3:
break;
default:
break;
}
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction().replace(R.id.drawer_layout, mFragment);
transaction.commit();
transaction.addToBackStack(null);
}


Code for fragment

public class AnnouncementFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_announcement, container, false);
}


XML layout of main activity

<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">

<RelativeLayout
android:id = "@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/screen_login"
android:gravity="center"
android:textColor="@android:color/holo_blue_light"
android:id="@+id/login_tv"
android:layout_alignParentTop="true"
android:layout_marginTop="105dp"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:textSize="40sp" />

<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/username_et"
android:layout_below="@id/login_tv"
android:layout_alignParentStart="true"
android:layout_marginTop="45dp"
android:layout_alignParentEnd="true"
android:hint="@string/username"/>

<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:ems="10"
android:id="@+id/editText2"
android:layout_below="@id/username_et"
android:layout_alignParentStart="true"
android:layout_marginTop="30dp"
android:layout_alignParentEnd="true"
android:hint="@string/password" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login"
android:id="@+id/login_btn"
android:layout_below="@+id/editText2"
android:layout_centerHorizontal="true"
android:layout_marginTop="25dp" />

</RelativeLayout>

<ListView
android:id="@+id/drawer_list"
android:layout_width="200dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#ffeeeeee"/>



Error

java.lang.IllegalStateException: Activity has been destroyed
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1399)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:637)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:616)
at com.it.learnera.DrawerItemClickListener.selectItem(DrawerItemClickListener.java:40)
at com.it.learnera.DrawerItemClickListener.onItemClick(DrawerItemClickListener.java:19)


I am wondering how I can make the fragment draw over the main activity layout without causing a mess

Cheers and Thanks in advance

Answer
    FragmentTransaction transaction = getSupportFragmentManager()
              .beginTransaction()
              .replace(R.id.drawer_layout, mFragment);   // < - first problem ( see ad. 1)
              .commit()  // <-  third problem (see ad. 3)

        // second problem ( see ad. 2)
        mFragment = new AnnouncementFragment();
        Bundle args = new Bundle();
        args.putInt(null, position);

ad 1. You replacing DrawerLayout instead of its container

<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- The main content view -->
<FrameLayout
    android:id="@+id/content_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
<!-- The navigation drawer -->
<ListView android:id="@+id/left_drawer"
    android:layout_width="240dp"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:choiceMode="singleChoice"
    android:divider="@android:color/transparent"
    android:dividerHeight="0dp"
    android:background="#111"/>

drawer_layou - this is a view which holds:

  • FrameLayout / with id - >content_frame

           ( container capable to hold "only" one element: fragment / view) 
    
  • ListView / with id - > left_drawer

          ( list view for example with  menu entries  )
    

so you need replace content_frame

enter image description here

ps to better see purpose of transaction you should call it in one statement - in "chain / sequence "

    FragmentTransaction transaction = getSupportFragmentManager()
              .beginTransaction()
              .replace(R.id.frame_layout, mFragment)
              .addToBackStack(null)   // add to manager " will remember this fragment  - for navigation purpose"
              .commit()   // commit mean do everything from top to bottom on next pass 

ad 2. you need also put args in fragment:

        mFragment = new AnnouncementFragment();
        Bundle args = new Bundle();
        args.putInt(null, position);
        mFragment.setArguments(args)

ad 3. You calling commit when activity is after onSaveInstanceState

Note: A fragment transaction can only be created/committed prior to an activity saving its state.

If you try to commit a transaction after:

  • Activity.onSaveInstanceState()

and prior to a following

  • Activity.onStart()
  • Activity.onResume()

you will get an error. This is because the framework takes care of saving your current fragments in the state, and if changes are made after the state is saved then they will be lost.

After FragmentTransaction is committed with FragmentTransaction.commit() is scheduled to be executed asynchronously on the process's main thread.

If you want to immediately executing any such pending operations, you can call this function (only from the main thread) to do so. Note that all callbacks and other related behavior will be done from within this call, so be careful about where this is called from.

boolean executePendingTransactions();

  • which return true if there were any pending transactions to be executed.

btw look at this docs which refers to extending an FragmentActivity:

https://developer.android.com/reference/android/support/v4/app/FragmentActivity.html

i don't know which api you target but you may also check this bug:

http://johnfeng.github.io/blog/2015/05/31/fragment-activity-has-been-destoryed-problem/

( problem with child fragment which state is not being reset )

Hi, appreciate the detailed explanation but it doesnt work – Prejith P

and this article about state loss which is i think related to your problem

http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html

so you can try call instead of commit() (but this is not solution):

 commitAllowingStateLoss()

Hi, I fixed it myself just now. I changed the implementation of my navigation drawer a bit and it just started working. I have no idea how it got fixed, which is why I haven't posted an answer here – Prejith P

YES - the problem was your implementation you have tried to call commit() after an activity instance was destroyed what is indicating by stack trace

java.lang.IllegalStateException: Activity has been destroyed
 at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1399)
 at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:637)
 at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:616)
 at com.it.learnera.DrawerItemClickListener.selectItem(DrawerItemClickListener.java:40)
 at com.it.learnera.DrawerItemClickListener.onItemClick(DrawerItemClickListener.java:19)
                                                 `

when You call commit() you should ensure that:

  • activity is attached
  • activity is visible ( before onSaveInstanceState )
Comments