Activity 具有可以采用两个不同片段和设备旋转的片段
Activity with fragment who can take two differents fragments and rotation of device
我对 android、java 几乎是新手,对 fragment 也是新手。
我有一个 activity (A) 显示使用片段 (B) 的列表。选择此列表中的一个项目,同一片段将更改为使用片段 (C) 显示另一个列表。
它可以工作,除非我在显示第二个列表 (C) 时旋转设备。
此外,当它显示第一个列表 (B) 时,如果我旋转设备,它会完美地工作。
错误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>
片段 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>
片段 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>
也许我的问题是因为我使用 1 个片段来显示 2 个不同的片段?在这种情况下,我使用 2 个片段解决它,并交替显示必须显示的那个片段(VISIBILITY.GONE)?
谢谢
EDIT:在XML main activity (A)中,我更改了片段名称-> FragmentB(发问题的时候忘记改了)
N.B:我需要加划线 -> 当 activity (A) 启动时,它会加载 FragmentB (B);然后我 select 一个项目并加载 FragmentC (C);当我在 XML 中旋转时,有 FragmentB(很明显),而 activity (A) 必须加载 FragmentC (C)。不知道Android能不能应付这种情况
求解:
1) LogCat Ortsinton 修改后(创建一个空构造函数,并使用 "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) 以编程方式实例化片段(不是来自 XML):
- 在 XML 中你必须使用 "layout"(我使用了 FrameLayout)而不是 "fragment";
- 在 java 脚本中,您必须使用 "FragmentTransaction";
3) Android 处理 Activity (包含片段)并且 Android 像开始时一样重新创建它,因此您必须实施 onSaveInstanceState(保存数据)和 onRestoreInstanceState(修改 activity 旋转设备时的状态)。
我认为你的错误出在这里,在 ListPoints 片段中:
public ListPoints (int _cod) {
this.cod = _cod;
}
来自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 需要一个空的构造函数,这样它就可以被实例化,你不能重写它。所以如果你需要传递一些参数给它试试这个:
在Activity中写入:
Bundle bundle = new Bundle();
bundle.putInt(key, _cod);
fragment.setArguments(bundle);
阅读片段:
Bundle arguments = this.getArguments();
this.cod = arguments.getInt(key);
不要忘记覆盖 onSaveInstanceState 函数,这样如果应用程序在后台并被终止,它将能够正确恢复状态。否则你的变量将没有价值
我对 android、java 几乎是新手,对 fragment 也是新手。 我有一个 activity (A) 显示使用片段 (B) 的列表。选择此列表中的一个项目,同一片段将更改为使用片段 (C) 显示另一个列表。 它可以工作,除非我在显示第二个列表 (C) 时旋转设备。 此外,当它显示第一个列表 (B) 时,如果我旋转设备,它会完美地工作。
错误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>
片段 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>
片段 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>
也许我的问题是因为我使用 1 个片段来显示 2 个不同的片段?在这种情况下,我使用 2 个片段解决它,并交替显示必须显示的那个片段(VISIBILITY.GONE)?
谢谢
EDIT:在XML main activity (A)中,我更改了片段名称-> FragmentB(发问题的时候忘记改了)
N.B:我需要加划线 -> 当 activity (A) 启动时,它会加载 FragmentB (B);然后我 select 一个项目并加载 FragmentC (C);当我在 XML 中旋转时,有 FragmentB(很明显),而 activity (A) 必须加载 FragmentC (C)。不知道Android能不能应付这种情况
求解:
1) LogCat Ortsinton 修改后(创建一个空构造函数,并使用 "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) 以编程方式实例化片段(不是来自 XML): - 在 XML 中你必须使用 "layout"(我使用了 FrameLayout)而不是 "fragment"; - 在 java 脚本中,您必须使用 "FragmentTransaction";
3) Android 处理 Activity (包含片段)并且 Android 像开始时一样重新创建它,因此您必须实施 onSaveInstanceState(保存数据)和 onRestoreInstanceState(修改 activity 旋转设备时的状态)。
我认为你的错误出在这里,在 ListPoints 片段中:
public ListPoints (int _cod) {
this.cod = _cod;
}
来自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 需要一个空的构造函数,这样它就可以被实例化,你不能重写它。所以如果你需要传递一些参数给它试试这个:
在Activity中写入:
Bundle bundle = new Bundle();
bundle.putInt(key, _cod);
fragment.setArguments(bundle);
阅读片段:
Bundle arguments = this.getArguments();
this.cod = arguments.getInt(key);
不要忘记覆盖 onSaveInstanceState 函数,这样如果应用程序在后台并被终止,它将能够正确恢复状态。否则你的变量将没有价值