Android - Nested Fragments

663 views Asked by At

I am trying to have few Tabs and within each tab I would like to have a list view that will lead me to another Fragment (another listview)

It crashes whenever I press one of the elements on the ListView in Android.java file.

Android.java

package com.example.tabs;

import java.util.ArrayList;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class Android extends Fragment {

private ArrayList<String> listOfElements;
private ListView trackView;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    final View android = inflater.inflate(R.layout.android_frag, container, false);
    trackView = (ListView) android.findViewById(R.id.android_list);
    listOfElements = new ArrayList<String>();
    listOfElements.add("Scott");
    listOfElements.add("james");
    listOfElements.add("mike");

    TrackAdapter trackAdapter = new TrackAdapter(getActivity(), listOfElements);
    trackView.setAdapter(trackAdapter);
    trackView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            // _mClickListener.onViewSelected(position, listOfTracks);
            try {
                Fragment videoFragment = new NestedAndroidFragment();
                FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
                transaction.addToBackStack(null);
                transaction.replace(android.getId(), videoFragment);
                transaction.commit();

            } catch (Exception e) {
                Log.d("TabsApp", e.toString());
            }
        }
    });

    return android;
}
}

Here is how I started:

android_frag.xml

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

    <ListView
        android:id="@+id/android_list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >
    </ListView>

NestedAndroidFragment.java

package com.example.tabs;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class NestedAndroidFragment extends Fragment {
private ListView trackView;
public NestedAndroidFragment() {

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.nested_frag, container, false);
    trackView = (ListView) root.findViewById(R.id.nested_android_list);
    return root;
}

/*  private void destroyFragment() {
    getChildFragmentManager().beginTransaction().hide(this).commit();
}*/  
}

nested_fragment.xml (I put pretty much the same as for the android fragment - coulnd't find a good example for that)

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

    <ListView
        android:id="@+id/nested_android_list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >
    </ListView>
</FrameLayout>

Has anyone done something similar before? I would appreciate your help.

Here is the stacktrace

11-11 21:35:51.808: E/AndroidRuntime(28384): FATAL EXCEPTION: main 11-11 21:35:51.808: E/AndroidRuntime(28384): android.content.res.Resources$NotFoundException: Unable to find resource ID #0xffffffff 11-11 21:35:51.808: E/AndroidRuntime(28384): at android.content.res.Resources.getResourceName(Resources.java:2639) 11-11 21:35:51.808: E/AndroidRuntime(28384): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:919) 11-11 21:35:51.808: E/AndroidRuntime(28384): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104) 11-11 21:35:51.808: E/AndroidRuntime(28384): at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682) 11-11 21:35:51.808: E/AndroidRuntime(28384): at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1460) 11-11 21:35:51.808: E/AndroidRuntime(28384): at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:440) 11-11 21:35:51.808: E/AndroidRuntime(28384): at android.os.Handler.handleCallback(Handler.java:730) 11-11 21:35:51.808: E/AndroidRuntime(28384): at android.os.Handler.dispatchMessage(Handler.java:92) 11-11 21:35:51.808: E/AndroidRuntime(28384): at android.os.Looper.loop(Looper.java:137) 11-11 21:35:51.808: E/AndroidRuntime(28384): at android.app.ActivityThread.main(ActivityThread.java:5419) 11-11 21:35:51.808: E/AndroidRuntime(28384): at java.lang.reflect.Method.invokeNative(Native Method) 11-11 21:35:51.808: E/AndroidRuntime(28384): at java.lang.reflect.Method.invoke(Method.java:525) 11-11 21:35:51.808: E/AndroidRuntime(28384): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1187) 11-11 21:35:51.808: E/AndroidRuntime(28384): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003) 11-11 21:35:51.808: E/AndroidRuntime(28384): at dalvik.system.NativeStart.main(Native Method)

1

There are 1 answers

1
ucsunil On BEST ANSWER

I see what you've done - you are trying to replace the contents of the view in android_frag.xml with an instance of NestedAndroidFragment. There are two errors that you have here actually - the crash is the first one. I'll explain the second one shortly.

What you are trying to do in the following line is wrong.

transaction.replace(android.getId(), videoFragment); // you are trying to get the ID of the entire inflated view
// you should be just using the id of the FrameLayout here

In fragments, you should be replacing the contents of a root level container with a fragment. What you are trying to do is ask fragment B to reside inside fragment A but replace all of A. Do you see how this paradigm is broken? You cannot ask B to reside inside A while at the same time replacing A. And this is the reason for your crash. Fixing this is pretty simple. In android_frag.xml, add an id attribute for the FrameLayout as follows:

android:id="@+id/container"

Now go back to your Android.java and replace line where you make the replace transaction with:

transaction.replace(R.id.container, videoFragment); // you can directly reference the id here

This will fix the issue of your crash but this is where you will notice another issue - the contents of the videoFragment will overlay the contents of your ListView. The reason for this is that while using fragments, Android expects you to fill the containers as and when you need them. But you have a ListView defined in the xml. Once a widget is defined in xml, you cannot keep replacing it or removing it as you would with fragments. Once it is in xml, it will always be in every view where that xml is. The best design in your case would be to have your Android class extend a ListFragment and then take things from there (but this is only from looking at what you have put up now. I am not sure how this would interact with your other code and hence only take my statement as a suggestion if it works for you).

As a workaround to deal with what you have right now, you need to hide your ListView before replacing the with the fragment. Hence add this following line to your onItemClick() method.

trackView.setVisibility(View.GONE); // put this just before the replace statement
// you can have it after the replace but in theory it would mean that the ListView would show up in the background for a fraction of a moment.
// Might not be noticeable but just good to do it this way