0

I have looked at every error of this kind of here, but nothing fixes it. I am trying to programmatically add a fragment to my activity (with the help of other's code) and I run it and get this error:

IllegalStateException, the specified child already has a parent, you must call removeView() on the child's parent first.

I have one activity that simply hosts a fragment, using FrameLayout in its xml, then I code in that activity the FragmentManager to get it started. The details of my fragment's view are in the onCreateView() of my fragment class, ContactsFragment.java. Here, I assign a ListView as my fragment that I hope to view in my activity called AddressBook.java.

Thanks for your help, if you can spot any errors in my code. Activity, fragment, xmls, and logcat are below.

EDIT: When I take out the FragmentManager code, the error goes away, but then I get a blank white activity. Does this help anyone know more about the nature of the error? Thanks.

Code commented out:

        // Begin the transaction
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        // Replace the container with the new fragment
        ft.add(R.id.contacts_fragment, new ContactsFragment());
        // Execute the changes specified
        ft.commit();

AddressBook.java

package org.azurespot.practiceapp.addressbook;

import android.app.Activity;
import android.app.FragmentTransaction;
import android.os.Bundle;

import com.example.practiceapp.R;


public class AddressBook extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_address_book);

        // Begin the transaction
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        // Replace the container with the new fragment
        ft.replace(R.id.contacts_fragment, new ContactsFragment());
        // or ft.add(R.id.your_placeholder, new FooFragment());
        // Execute the changes specified
        ft.commit();
    }

}

activity_address_book.xml

<LinearLayout 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="org.azurespot.practiceapp.addressbook.AddressBook" >

     <FrameLayout
         android:id="@+id/contacts_fragment"
         android:layout_width="0dp"
         android:layout_height="match_parent" >
     </FrameLayout>

</LinearLayout>

ContactsFragment.java

package org.azurespot.practiceapp.addressbook;

import android.app.ListFragment;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;

import com.example.practiceapp.R;

/*
 * Partially from http://stackoverflow.com/questions/18199359/how-to-display-contacts-in-a-listview-in-android-for-android-api-11
 */

public class ContactsFragment extends ListFragment implements 
                                LoaderCallbacks<Cursor>{

    private CursorAdapter listAdapter;
    public Cursor cursor;
    private android.content.Context context;
    public View view;
    public static Uri uri;
    public static ListView listView;

    public static final String[] FROM = new String[]{ 
            ContactsContract.Contacts.PHOTO_THUMBNAIL_URI,
            ContactsContract.Contacts.DISPLAY_NAME,
            ContactsContract.Contacts.HAS_PHONE_NUMBER };

    private static final int[] TO = { R.id.contact_thumbnail, 
        R.id.contact_name, R.id.contact_number };

    // columns requested from the database
    private static final String[] PROJECTION = new String[]{
        ContactsContract.Contacts._ID,
            ContactsContract.Contacts.PHOTO_THUMBNAIL_URI,
            ContactsContract.Contacts.DISPLAY_NAME,
            ContactsContract.CommonDataKinds.Phone.NUMBER,
    };

    // this goes in the CursorLoader parameter list, it filters
    // out only those contacts who have a phone number
    private static final String FILTER = 
            ContactsContract.Contacts.HAS_PHONE_NUMBER  + " LIKE ?";

    private static final int URL_LOADER = 0;

    @Override
    public View onCreateView(LayoutInflater inflater,
             ViewGroup container,Bundle bundle) {

        if(listView != null) {
            return listView; 
        }

        // since using ListFragment, you must inflate your view this way
        View rootView = inflater.inflate(R.layout.fragment_list_view,
                container, false);
        listView = (ListView) rootView
                                .findViewById(android.R.id.list);

        // create a new SimpleCursorAdapter adapter
        context = getActivity();
        Cursor c = null; // there is no cursor yet
        int flags = 0; // no auto-re-query! (My loader re-queries.)
        // put List UI row in adapter, create empty adapter
        listAdapter = new SimpleCursorAdapter(context, 
                R.layout.fragment_list_item, c, FROM, TO, flags);

        // Initializes the CursorLoader. The URL_LOADER value is 
        // eventually passed to onCreateLoader().
        getLoaderManager().initLoader(URL_LOADER, null, this);

        // ListFragment then sets the adapter
        ContactsFragment listFrag = new ContactsFragment();
        listFrag.setListAdapter(listAdapter);

        return listView;

    } // end onCreateView 


    // Empty public constructor, required by the system
    public ContactsFragment() {}

    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

    }

    /*
     * The 3 methods below are standard for the LoaderCallbacks<Cursor> interface.
     * Here, CursorLoader does a query in the background.
     */

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        // load from the "Contacts table"
        Uri contentUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;

        // the query
        return new CursorLoader(getActivity(),
                contentUri,
                PROJECTION,
                FILTER,
                null,
                ContactsContract.Contacts.DISPLAY_NAME + " ASC");
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
         // Once cursor is loaded, give it to adapter
        listAdapter.swapCursor(data);

        // The list should now be shown.
        if (isResumed()) {
            setListShown(true);
        } else {
            setListShownNoAnimation(true);
        }
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        // Delete the reference to the existing Cursor,
        // so it can recycle it
        listAdapter.swapCursor(null);

    }

}

fragment_list_view.xml

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

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1" >
    </ListView>

    <TextView 
        android:id="@id/android:empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#FF0000"
        android:text="No data"/>

</LinearLayout>

fragment_list_item.xml

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

    <ImageView 
        android:id="@+id/contact_thumbnail"
        android:layout_width="50dp"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/contact_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:ems="10" >

    </TextView>

    <TextView
        android:id="@+id/contact_number"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:ems="10" >

    </TextView>

</LinearLayout>

Logcat

12-27 22:37:17.145: I/PersonaManager(12130): getPersonaService() name persona_policy
12-27 22:37:17.165: D/skia(12130): GFXPNG PNG bitmap created width:48 height:48 bitmap id is 270 
12-27 22:37:17.175: E/MoreInfoHPW_ViewGroup(12130): Parent view is not a TextView
12-27 22:37:17.185: D/skia(12130): GFXPNG PNG bitmap created width:72 height:72 bitmap id is 271 
12-27 22:37:17.185: D/skia(12130): GFXPNG PNG bitmap created width:144 height:144 bitmap id is 272 
12-27 22:37:17.205: D/skia(12130): GFXPNG PNG bitmap created width:144 height:144 bitmap id is 273 
12-27 22:37:17.295: I/Adreno-EGL(12130): <qeglDrvAPI_eglInitialize:410>: EGL 1.4 QUALCOMM build:  ()
12-27 22:37:17.295: I/Adreno-EGL(12130): OpenGL ES Shader Compiler Version: E031.24.00.08+13
12-27 22:37:17.295: I/Adreno-EGL(12130): Build Date: 03/20/14 Thu
12-27 22:37:17.295: I/Adreno-EGL(12130): Local Branch: 0320_AU200_patches
12-27 22:37:17.295: I/Adreno-EGL(12130): Remote Branch: 
12-27 22:37:17.295: I/Adreno-EGL(12130): Local Patches: 
12-27 22:37:17.295: I/Adreno-EGL(12130): Reconstruct Branch: 
12-27 22:37:17.325: D/OpenGLRenderer(12130): Enabling debug mode 0
12-27 22:37:18.705: I/PersonaManager(12130): getPersonaService() name persona_policy
12-27 22:37:18.715: E/MoreInfoHPW_ViewGroup(12130): Parent view is not a TextView
12-27 22:37:18.735: D/skia(12130): GFXPNG PNG bitmap created width:12 height:12 bitmap id is 274 
12-27 22:37:18.735: D/AbsListView(12130): Get MotionRecognitionManager
12-27 22:37:18.745: D/AndroidRuntime(12130): Shutting down VM
12-27 22:37:18.745: W/dalvikvm(12130): threadid=1: thread exiting with uncaught exception (group=0x41737da0)
12-27 22:37:18.745: E/AndroidRuntime(12130): FATAL EXCEPTION: main
12-27 22:37:18.745: E/AndroidRuntime(12130): Process: com.example.practiceapp, PID: 12130
12-27 22:37:18.745: E/AndroidRuntime(12130): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.practiceapp/org.azurespot.practiceapp.addressbook.AddressBook}: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2395)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2453)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.app.ActivityThread.access$900(ActivityThread.java:173)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.os.Handler.dispatchMessage(Handler.java:102)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.os.Looper.loop(Looper.java:136)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.app.ActivityThread.main(ActivityThread.java:5579)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at java.lang.reflect.Method.invokeNative(Native Method)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at java.lang.reflect.Method.invoke(Method.java:515)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at dalvik.system.NativeStart.main(Native Method)
12-27 22:37:18.745: E/AndroidRuntime(12130): Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.view.ViewGroup.addViewInner(ViewGroup.java:3771)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.view.ViewGroup.addView(ViewGroup.java:3624)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.view.ViewGroup.addView(ViewGroup.java:3569)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.view.ViewGroup.addView(ViewGroup.java:3545)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:901)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1062)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.app.BackStackRecord.run(BackStackRecord.java:684)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1453)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.app.Activity.performStart(Activity.java:5460)
12-27 22:37:18.745: E/AndroidRuntime(12130):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2368)
12-27

22:37:18.745: E/AndroidRuntime(12130): ... 11 more

Azurespot
  • 3,066
  • 3
  • 45
  • 73

1 Answers1

1

In 'ContactsFragment.java' class you are not returning the inflated view. Instead the listview is returned. Try change the code as below

@Override
public View onCreateView(LayoutInflater inflater,
             ViewGroup container,Bundle bundle) {

       // since using ListFragment, you must inflate your view this way
        View rootView = inflater.inflate(R.layout.fragment_list_view,
                container, false);

        return rootView;
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
       super.onActivityCreated(savedInstanceState);
         listView = (ListView)getListView();

        // create a new SimpleCursorAdapter adapter
        context = getActivity();
        Cursor c = null; // there is no cursor yet
        int flags = 0; // no auto-re-query! (My loader re-queries.)
        // put List UI row in adapter, create empty adapter
        listAdapter = new SimpleCursorAdapter(context, 
                R.layout.fragment_list_item, c, FROM, TO, flags);

        // Initializes the CursorLoader. The URL_LOADER value is 
        // eventually passed to onCreateLoader().
        getLoaderManager().initLoader(URL_LOADER, null, this);

        setListAdapter(listAdapter);

}

Use the same replace() method is Activity

       // Begin the transaction
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        // Replace the container with the new fragment
        ft.replace(R.id.contacts_fragment, new ContactsFragment());
        // or ft.add(R.id.your_placeholder, new FooFragment());
        // Execute the changes specified
        ft.commit();

fragment_list_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#FF0000"
    android:layout_height="match_parent" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1" >
    </ListView>

    <TextView 
        android:id="@id/android:empty"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#FF0000"
        android:text="No data"/>

</LinearLayout>

activity_address_book.xml

<LinearLayout 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="org.azurespot.practiceapp.addressbook.AddressBook" >

     <FrameLayout
         android:id="@+id/contacts_fragment"
         android:layout_width="match_parent"
         android:layout_height="match_parent" >
     </FrameLayout>

</LinearLayout>
droid kid
  • 7,569
  • 2
  • 32
  • 37
  • Thanks droidkid, but I need a custom layout, because I want to pull a photo thumbnail, a name, and phone number from my list. The `listView` is basically the same as `rootView`, because it gets assigned to the `rootView.findViewById(android.R.id.list)` method. But I did check, by replacing it with `rootView` and I still got a white page. In the ListFragment docs, I got instructions for the `onCreateView()`, unless, something else in there is wrong. – Azurespot Dec 28 '14 at 08:10
  • One thing I did, was add this to my `AddressBook` activity: `FragmentManager fm = getFragmentManager(); if (fm.findFragmentById(R.id.contacts_fragment) == null){fm.beginTransaction().add(R.id.contacts_fragment, new ContactsFragment());}` and the error went away, but my list is still blank, which tells me the `ListView` was not `null`. I don't know why it's not `null` though, and yet still shows nothing. :( – Azurespot Dec 28 '14 at 08:12
  • Your FrameLayout width is given as '0dp'. May be an issue with the visibility – droid kid Dec 28 '14 at 08:22
  • Also, I noticed `Fragment` class has no `setAdapter` method, or anything similar. But other posts suggest to set the adapter on the `ListView`, I tried that too, but still, blank white activity. :( – Azurespot Dec 28 '14 at 08:26
  • Thanks for looking... I tried making that bigger, didn't help. I noticed a lot of tutorials use that `0dp` for the width, not sure why. Still blank screen though, when I put it to `match_parent`. – Azurespot Dec 28 '14 at 08:28
  • ok, 0dp is used when you put layout_weight property. My suggestion is comment other code inside onCreateView method except the inflating and return part and check the views are getting loaded – droid kid Dec 28 '14 at 08:31
  • What will changing to `Fragment` do? As mentioned above, I did try that, which meant I had to change to `listView.setAdapter()` instead of the `setListViewAdapter()` called on the fragment class itself. And still a blank screen. Also, I do already do a null check in my `onCreateView()`. I tried taking it out, but no change. – Azurespot Dec 28 '14 at 08:34
  • I tried the new one, still blank. :( I even uninstalled my app on my phone, then retried to run but nothing. – Azurespot Dec 28 '14 at 09:19
  • I think I may have fixed the view problem (although now I have a query problem, new error). My `FragmentManager` call was wrong, it should have been: `FragmentManager fm = getFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); ft.replace(R.id.contacts_fragment, new ContactsFragment()); ft.commit();`. I think this is right, because before it crashed (from new error), the background changed to orange, which means it's getting my `ListView` now. Really happy, although now I have to work on the query! Thanks for your replacement code though! I think it helped. – Azurespot Dec 28 '14 at 09:43
  • 1
    OK, Glad it worked out. Your prblm was in the onCreateView() method, there you created another instance of ContactsFragment and set adapter values to that. Also this is done before attaching the fragment layout. If you got your solution then accept the answer – droid kid Dec 28 '14 at 10:40
  • Thanks for explaining that, it really helped a ton. I realized it wasn't the whole solution, but your help was really key, so I will accept as an answer. If you wanted to include the `FragmentManager` code I changed as part of the answer, that might help others too. Thanks again! I was really hitting my head for days. :) – Azurespot Dec 28 '14 at 10:48
  • 1
    your old FragmentTranscation code is correct. check – droid kid Dec 28 '14 at 10:53
  • 1
    Hey you're right, I must have done that in a different order than I thought. Well, thanks so much. Your answer really was a life saver. :) – Azurespot Dec 28 '14 at 10:57