getActivity()
之前调用,片段中的 null
将返回fragment.onAttach(activity)
。
fragment.onActivityCreated(savedInstanceState)
之后调用 fragment.onAttach(activity)
。有关片段生命周期的信息,请参阅developer.android。
我的片段中有一个尝试实例化数据库连接的方法。最初调用该方法时,它可以正常工作。
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
Log.d("MyTag", "Activity being created");
if (savedInstanceState == null){
listAdapter.setContext(getActivity());
execSearch(0);
} else { listAdapter.setContext(getActivity()); }
}
public void execSearch(Long searchId) {
MySQLiteHelper dbHelper = new MySQLiteHelper(getActivity());
SQLiteDatabase database = dbHelper.getReadableDatabase();
...
}
现在,当在列表视图中点击按钮后通过界面调用相同的方法时,getActivity()
会返回null
(在片段中。
以下是ListAdapter中的按钮回调:
private final View.OnClickListener editSearchListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Long search_id = (Long) v.getTag();
((MyInterface) context).searchCalled(search_id);
}
};
MainActivity中的接口实现:
@Override
public void searchCalled(Long search_id) {
fragment.execSearch(id);
}
第二次我在null
内得到getActivity()
的{{1}}回复。
我被困了!!这是否与来自UI操作的接口调用有关。我是否需要在UI线程???
上实现mainActivity.searchCalled作为runnable答案 0 :(得分:5)
由于这是我第一次开发Android应用程序,想要分享我学到的一些东西,以便为其他新的Android开发人员更直观地解决这个问题。我正在开发的背景:
基本上我要做的是保留用户在configuration changes之间所做的UI的任何更改(这对于新手了解活动创建与应用启动不同是有帮助的 - 重复创建活动当应用程序遇到配置更改或被其他应用程序替换时。这对我来说并不直观。)
有两种方法可以做到这一点。您可以在Fragment.onSaveInstanceState中保存必要的数据,然后在创建新活动时使用保存的数据重新创建片段,也可以在Fragment.onCreate
中致电setRetainInstance。
我最初使用的是savedInstanceState方法。由于这不会保留我的片段实例,因此只要我得到getActivity() == null
,就是因为代码是从未附加到活动的旧片段实例运行的。 当从onClickListener调用一个接口时,可能会发生这种情况,该接口尚未更新为指向当前活动实例(即附加到当前活动的实例)。即监听器是在之前创建的配置更改,并在配置更改后调用,而不会被告知已创建新的Activity实例。
我最终选择了setRetainInstance(true)
路线。这意味着:
onCreate
。如果活动被破坏,则不会调用onDestroy
。savedInstanceState
中的onActivityCreated
将始终为空这使事情变得简单而困难。基本上我推荐这条路线,如果你有很多属性/数据不依赖于知道当前活动是什么(例如表格值,要显示的列表)。但是,在这种情况下,必须在创建新活动时告知需要了解当前活动的任何属性。您可以按如下方式执行此操作:
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.activity = (MainActivity) activity;
// custom method on list adapter, so any calls to SearchManager interface
// are properly routed to current activity
myListAdapter.setContext(activity);
}
片段实例之间的通信
我听说过有关如何在片段之间进行通信的相互矛盾的建议。一方面,我听说你shouldn't have references to your fragment instances outside of the adapter。另一方面,您会听到fragment instances should not communicate directly with each other。这意味着使用FragmentPagerAdapter为片段实现通信接口。但是,即使在这种情况下,您也必须聪明地在适配器中获取片段实例(例如,您可以get the adapter's tag for your fragments或更好leverage the adapter's instantiateItem method)。
最终,因为你削减馅饼的任何方式你必须破解以获得你的片段实例,我走出适配器并创建了一个headless fragment来实现我的SearchManager
接口。这使得接口与适配器分离,避免了我的功能片段与MainActivity之间的通信。
跨配置更改保留视图
最后一点。当我创建新的搜索表单时,我注意到所有输入都被删除了我的配置更改(即附加/创建新片段时)。这是因为即使setRetainInstance
设置为true,仍会调用片段的onCreateView
方法,要求您对视图进行充气并将其返回。正是这个过程消除了对视图的任何用户操作。所以我在我的所有片段的onCreateView
方法中都执行了以下操作:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (view == null){
view = inflater.inflate(R.layout.doc_list_fragment,
container, false);
} else {
((ViewGroup) view.getParent()).removeView(view);
}
setContext();
return view;
}
因为setRetainInstance
为真,所以视图在第一次充气后保留在片段实例中。 removeView
业务可以防止此错误:
FATAL EXCEPTION: main java.lang.IllegalStateException:
The specified child already has a parent. You must call removeView() on the child's parent first.
此外,我在此处调用了我的自定义setContext
方法,因为此时我既有我的观点又有我的活动。因此,如果任何点击监听器需要知道当前活动是什么,我现在有了我准备好的视图,并且可以创建监听器以正确地将它们指向当前活动。
您可以在GitHub上查看已完成的应用。