0

I'm almost new about android, java, ... and new about fragment. I have an activity (A) that shows a list using fragment (B). Selecting an item of this list, the same fragment is changed to show another lis using fragment (C). It works except when i rotate the device while it shows the second list (C). Also when it is shown the first list (B), if i rotate the device, it works perfectly.

Error LogCat:

    04-26 11:30:32.769: E/AndroidRuntime(2882): FATAL EXCEPTION: main
    04-26 11:30:32.769: E/AndroidRuntime(2882): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.traffic/com.example.traffic.ListTriplines}: android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.example.traffic.ListPoints: make sure class name exists, is public, and has an empty constructor that is public
(...)
    04-26 11:30:32.769: E/AndroidRuntime(2882): Caused by: android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.example.traffic.ListPoints: make sure class name exists, is public, and has an empty constructor that is public
(...)
    04-26 11:30:32.769: E/AndroidRuntime(2882): Caused by: java.lang.InstantiationException: com.example.traffic.ListPoints

Activity A (java + XML):

public class MainA extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list_triplines);
    }

    @Override
    public void onTripSelected(int cod) {
        Fragment pointsFragment = new ListPoints(cod);
        FragmentTransaction fTransaction = fManager.beginTransaction();
        fTransaction.replace(R.id.trips_points_fragment, pointsFragment);
        fTransaction.addToBackStack(null);
        fTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        fTransaction.commit();
    }

    @Override
    public void onPointSelected(int pos) {
        //do some stuff (using pos)
        (...)
    }
}



<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <TextView
        android:id="@+id/main_title"
        style="@style/HelpTitle"
        android:text="@string/listtrips_title"
        />

    <fragment
        android:id="@+id/trips_points_fragment"
        android:name="com.example.traffic.FragmentB"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        />
</RelativeLayout>

Fragment B (java + XML) :

(...)
import android.support.v4.app.Fragment;
(...)
public class FragmentB extends Fragment {

    private OnTripSelectedListener tSelected;
    private static ListTriplines listtriplines;
    private Context context;
    private LayoutInflater inflater;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        //CHECK che nell'ACTIVITY sia IMPLEMENT il listener
        try {
            tSelected = (OnTripSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnTripSelectedListener");
        }

        listtrips = (ListTriplines) activity;
        context = activity.getApplicationContext();
        inflater = LayoutInflater.from(activity);
    }

    @Override
    public View onCreateView (LayoutInflater inflater, ViewGroup vG, Bundle savedInstanceState) {
        //Inflate the layout for this fragment
        final View viewResult = inflater.inflate(R.layout.list_trips, vG, false);

        listview_trips = (ListView) viewResult.findViewById(R.id.trips_listview);

        return viewResult;
    }

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

        listAdapter = new CursorAdapter(...);

        listview_trips.setAdapter(listAdapter);
        listview_trips.setOnItemClickListener(onItemClickListener);
        listview_trips.setOnItemLongClickListener(onItemLongClickListener);
    }


public interface OnTripSelectedListener {
    public void onTripSelected(int cod);
}

    private OnItemClickListener onItemClickListener = new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> av, View v, int pos, long id) {
            tSelected.onTripSelected(id);
        }

    };
}



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
    <ExpandableListView
        android:id="@+id/trips_explistview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@color/gray"
        />
</LinearLayout>

Fragment C (java + XML) :

(...)
import android.support.v4.app.Fragment;
(...)
public FragmentC extends Fragment {

    private OnPointSelectedListener pListener;
    private static ListTriplines listtrips;
    private Context context;
    private int cod;

    public ListPoints (int _cod) {
        this.cod = _cod;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            pListener = (OnPointSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnPointSelectedListener");
        }

        listtrips = (ListTriplines) activity;
        context = activity.getApplicationContext();
inflater = LayoutInflater.from(activity);
    }

    @Override
    public View onCreateView (LayoutInflater inflater, ViewGroup vG, Bundle savedInstanceState) {
        //Inflate the layout for this fragment
        final View viewResult = inflater.inflate(R.layout.list_points, vG/*null*/, false);

        listview_points = (ListView) viewResult.findViewById(R.id.points_listview);

        return viewResult;
    }

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

        pointsAdapter = new CursorAdapter(...);
        listview_points.setAdapter(pointsAdapter);
        listview_points.setOnItemClickListener(onItemClickListener);

    }

public interface OnPointSelectedListener {
    public void onPointSelected(int pos);
}

    private OnItemClickListener onItemClickListener = new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> av, View v, int pos, long id) {
            pListener.onPointSelected(pos);
        }
    };

}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
    <ListView
        android:id="@+id/points_listview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
android:background="@color/gray"
        />
</LinearLayout>

Maybe my problem is because i use 1 fragment to show 2 different fragments ? In this case, i solve it using 2 fragments and alternate that one who must be shown (with VISIBILITY.GONE) ?

Thanks

EDIT: in the XML main activity (A), i changed the fragment name -> FragmentB (i forgot to change it when i posted the question).

N.B: i need to uderline this -> when the activity (A) starts, it loads FragmentB (B); then i select an item and it loads FragmentC (C); when i rotate in the XML there is FragmentB (obviusly) while the activity (A) must loads FragmentC (C). I dont know if Android can handle this situation.


TO SOLVE:

1) LogCat after Ortsinton modification (creating an empty constructor, and passing some arguments with "BUNDLE") :

04-26 14:24:46.809: E/AndroidRuntime(11558): FATAL EXCEPTION: main
04-26 14:24:46.809: E/AndroidRuntime(11558): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.traffic/com.example.traffic.ListTriplines}: android.view.InflateException: Binary XML file line #23: Error inflating class fragment
04-26 14:24:46.809: E/AndroidRuntime(11558):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1659)
(...)
04-26 14:24:46.809: E/AndroidRuntime(11558): Caused by: android.view.InflateException: Binary XML file line #23: Error inflating class fragment
(...)
04-26 14:24:46.809: E/AndroidRuntime(11558): Caused by: java.lang.IllegalStateException: Fragment com.example.traffic.ListTrips did not create a view.

2) Instantiate the Fragment programmatically (not from XML): - in the XML you must use a "layout" (i used FrameLayout) not "fragment"; - in java script, you must use "FragmentTransaction";

3) Android handles the Activity (that contains the fragments) and Android recreates it as it was at the beginning, so you must implementate onSaveInstanceState (saves the data) and onRestoreInstanceState (to modify the activity as it was when you rotated the device).

Adamz
  • 13
  • 4

1 Answers1

1

I think your error lies here, in ListPoints Fragment:

 public ListPoints (int _cod) {
    this.cod = _cod;
}

From Fragment class reference by Android

All subclasses of Fragment must include a public no-argument constructor. The framework will often re-instantiate a fragment class when needed, in particular during state restore, and needs to be able to find this constructor to instantiate it. If the no-argument constructor is not available, a runtime exception will occur in some cases during state restore.

Fragments need an empty constructor so it can be instantiated, you cannot cannot override it. So if you need to pass some arguments to it try this:

Writing in the Activity:

Bundle bundle = new Bundle();
bundle.putInt(key, _cod);
fragment.setArguments(bundle);

Reading in the fragment:

Bundle arguments = this.getArguments();
this.cod = arguments.getInt(key);

Don't forget to override onSaveInstanceState function so if the app is in background and killed, it will be able to restore the state correctly. Otherwise your variables will not have value

Ortsinton
  • 83
  • 9
  • Thanks for the reply :) ... I tried what you suggested (in the fragment i retrieve the bundle-data in the onAttach cycle), now the logcat changed (i posted it). – Adamz Apr 26 '15 at 12:38
  • It seems there's an error inflating the layout of the fragment. Can u post the xml file of the fragment com.example.traffic.FragmentB? – Ortsinton Apr 26 '15 at 17:34
  • I added the XML files of the two fragments. – Adamz Apr 26 '15 at 22:07
  • is gray defined in color.xml file? – Ortsinton Apr 26 '15 at 22:16
  • Look at this guy problem, it seems to be very similar to yours http://stackoverflow.com/questions/7707032/illegalstateexception-when-replacing-a-fragment Try to instantiate the first fragment programatically by replacing a fragment container instead of instantiating it in the xml. This may solve the problem when going landscape. – Ortsinton Apr 26 '15 at 22:52
  • I followed that solution and now it works ... only one thing (i hope last one), now when i rotate the device, it show fragmentB instead of fragmentC. It is normal and i must implementate the change or there is something to do ? thx very much – Adamz Apr 29 '15 at 17:55
  • As explained here: http://developer.android.com/training/basics/activity-lifecycle/recreating.html if you rotate the device, the activity is destroyed and then is recreated in its original state. You just need to save the id of the row clicked in fragment B, and perform the fragment transaction manually. To save data use the onSaveInstanceState callback functions. – Ortsinton Apr 30 '15 at 11:47