getActivity()在ActionBar Fragment中返回null

Eri*_* H. 0 android nullpointerexception fragmentpageradapter

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();
    ... 
}
Run Code Online (Sandbox Code Playgroud)

现在,当在列表视图中单击按钮后通过接口调用此相同方法时,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);
    }
};
Run Code Online (Sandbox Code Playgroud)

以及MainActivity中的接口实现:

@Override
public void searchCalled(Long search_id) {
    fragment.execSearch(id);
}
Run Code Online (Sandbox Code Playgroud)

第二次我nullgetActivity()内部得到回应fragment.execSearch(id).

我被困了!! 这是否与来自UI操作的接口调用有关.我是否需要在UI线程上实现mainActivity.searchCalled作为runnable ???

Eri*_* H. 5

由于这是我第一次开发Android应用程序,想要分享我学到的一些东西,以便为其他新的Android开发人员更直观地解决这个问题.我正在开发的背景:

  • 三个片段,允许创建搜索,搜索列表和搜索结果列表.这些都可以自己初始化,但可以从共享接口(即下面的SearchManager接口)发送消息和接收消息.
  • FragmentPagerAdapter其用于分配片段各自突出部并在它们之间移动.这更适合我的设计,因为我只有三个标签可以在它们之间滑动.对于更多的选项卡集,请使用FragmentStatePagerAdapter.
  • SearchManager接口,用于在片段之间进行通信.

为什么我遇到上面的问题和许多类似的问题

基本上我试图做的是保留用户在配置更改中所做的任何更改(这对于新手了解活动创建与应用程序启动不同是有帮助的 - 当应用程序遇到配置更改时,会重复创建活动,或被另一个应用程序取代.这对我来说并不直观.)

有两种方法可以做到这一点.您可以在Fragment.onSaveInstanceState中保存必要的数据,然后在创建新活动时使用保存的数据重新创建片段,也可以在您的活动中调用setRetainInstanceFragment.onCreate.

我最初使用的是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); 
     }
Run Code Online (Sandbox Code Playgroud)

其他有用的事情将有助于了解

片段实例之间的通信

关于如何在片段之间进行通信,我听到了相互矛盾的建议.一方面,我听说你不应该在适配器之外引用你的片段实例.另一方面,您听到片段实例不应该直接相互通信.这意味着使用FragmentPagerAdapter为片段实现通信接口.但是,即使在这种情况下,您也必须聪明地在适配器中获取片段实例(例如,您可以获取片段的适配器标签或更好地利用适配器的instantiateItem方法).

最终,因为任何你削减馅饼的方法,你必须破解以获得你的片段实例,我走出适配器并创建了一个实现我的界面的无头片段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;
}
Run Code Online (Sandbox Code Playgroud)

因为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. 
Run Code Online (Sandbox Code Playgroud)

此外,我在setContext这里调用了我的自定义方法,因为在这一点上,我同时拥有自己的视图和活动.因此,如果有点击监听器需要知道当前的活动是什么,现在我有准备视图,可以创建听众正确它们指向当前的活动.

完成项目

您可以在GitHub上查看已完成的应用程序.