【问题标题】:How to get the view of a Fragment outside of OnCreateView()如何在 OnCreateView() 之外获取片段的视图
【发布时间】:2015-04-13 14:28:06
【问题描述】:

我有一个Fragment 和一个TableLayout。表中的数据来自 SQLite 数据库。 SQLite 数据库由AsyncTask 中的MainActivity 中的RESTful Web 服务填充。 Fragment 必须等待任务完成才能填充表。 Fragment 监听要调用的任务 onPostExecute() 方法。如果是,则调用Fragment 中的方法onLoadAndStoreComplete()。这一切都有效。

我需要查看片段的OnCreateView() 方法之外的TableLayout。如果我能在onLoadAndStoreComplete 中获得片段的视图,那我就可以到达那里。

Same code as here. 我从 MainActivity 获得了 mContext,但没有与之关联的 getView() 方法。

我试过了:
- 创建一个类成员rootView并在onCreateView()中分配,但在onLoadAndStoreComplete()中,它是空的。
- 创建一个类成员 tableLayout 并在 onCreateView() 中分配,但在onLoadAndStoreComplete() 中,它为空。
- 在onLoadAndStoreComplete() 中再次调用this.getView(),但它返回null。
- 在onLoadAndStoreComplete() 内调用充气机,这可行,但后来我不知道在.inflate() 调用中对容器使用什么

不明白为什么onLoadAndStoreComplete()中rootView和tableLayout的类成员值为null

public class MyFragment extends Fragment implements OnLoadAndStoreCompleteListener {

private TableLayout tableLayout;
private View rootView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,  Bundle savedInstanceState) {
        mContext = this.getActivity();
        rootView = inflater.inflate(R.layout.fragment_permits, container, false); // this works
        tableLayout = (TableLayout) rootView.findViewById(R.id.main_table); // and this
        ...
    }

    @Override
    public void onLoadAndStoreComplete() {

       // rootView is null, ie not remembered from OnCreateView

        View view =  getView(); // null

        LayoutInflater inflater = LayoutInflater.from(getActivity());
      rootView = inflater.inflate(R.layout.fragment_permits, container, false); // container? don't know what it should be

        // tableLayout is null
        tableLayout.addView(tableRow, new TableLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
    }
    ...
}

【问题讨论】:

  • 什么是OnLoadAndStoreCompleteListener,何时调用?在 Android API 中找不到这个
  • 从未听说过onLoadAndStoreComplete(),您确定尊重Fragment 的生命周期吗?如果你愿意,那么 getView() 将不会返回 null...
  • 还试图从Fragment 之外的Fragment 访问ViewFragment 是非常糟糕的设计......我建议你重新考虑你想做的事情并尝试一个更抽象的解决方案。
  • 如果你看一下我链接到的上一个问题,你就会明白onLoadAndStoreCompleteListener()。我不是在尝试访问 Fragment 之外的视图,而是在 OnCreateView() 之外
  • 您的问题需要包含回答问题所需的所有信息。您不能链接到解释此问题中代码的另一个问题。

标签: android android-fragments


【解决方案1】:

如果我正确理解了您的代码,则说明您的代码不尊重 Fragment 生命周期不同的 Fragment 实例失败有问题。

A.生命周期问题

Android 框架希望您的 Fragment 在 onCreateView() 方法中创建它的视图。 框架调用此方法后视图变得可用。

您的代码可能在onCreateView() 之前调用了onLoadAndStoreComplete(),这导致了问题。

您需要提取方法来使用数据填充实际视图,因为它可以有 2 个入口点。请看下面的代码:

public class MyFragment extends Fragment implements OnLoadAndStoreCompleteListener {
    private TableLayout tableLayout;
    private View rootView;
    private SomeDataClass loadedData;

    @Override
    public View onCreateView(LayoutInflater inflater, 
                             ViewGroup container,
                             Bundle savedInstanceState) {
        mContext = this.getActivity();
        rootView = inflater.inflate(R.layout.fragment_permits, container, false); 
        tableLayout = (TableLayout) rootView.findViewById(R.id.main_table);

        updateUi(); // If onLoadAndStoreComplete() was already called
                    // this will plug newly created view to returned data
    }

    @Override
    public void onLoadAndStoreComplete() {
        loadedData = ...; // Save loaded data here
        updateUi(); // If onCreateView() was already called
                    // this will plug loaded data to the view
    }


    private void updateUi() {
        if (rootView == null) { // Check if view is already inflated
            return; 
        }

        if (loadedData != null) {
            // View is already inflated and data is ready - update the view!
            ...
        }
    }
}

B.不同的实例失败

当您对上述片段的 2 个不同实例进行操作时,可能会发生这种情况。

一开始一切都会井然有序:onCreateView()onLoadAndStoreComplete() 之前调用。因此检查这些:

  • 为您的片段记录/调试默认构造函数调用。您希望在创建/加载流程期间收到 一个 对您的片段的调用。
  • 检查调用onCreateView()的对象是否与调用onLoadAndStoreComplete()的对象完全相同,可以使用System.identityHashCode(this),在这两种方法中获取不同的值是一个不好的迹象

如果发生这种情况,原因可能隐藏得更深一些,如果没有代码,我无法为此提供准确的建议。你如何创建你的片段?通过 XML 或手动然后通过 FragmentManager 或通过 ViewPager 添加?

【讨论】:

  • 这最终是我的问题。我认为“片段中”的意思是在我的片段类中,即类中的任何方法,无论是从Fragment 继承的方法还是我自己创建的方法。这是不正确的。这意味着只有从Fragment继承的方法。
  • 使用变体 B 节省我的时间。
【解决方案2】:

getView() 给出从onCreatedView() 返回的 Fragment 的 rootView。所以如果onLoadAndStoreComplete()onCreatedView() 完成之前被调用(它不能返回你的rootView),你会得到null,因为还没有创建视图。


我尝试在onViewCreated() 中调用getView(),这不是空的:

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        View viewer=getView();
        if(viewer==null){
            Itu.showToast("null",getActivity(), Toast.LENGTH_LONG);
        }else{
            Itu.showToast(viewer.toString(),getActivity(), Toast.LENGTH_LONG);//This is called
        }
    }

【讨论】:

  • 我明白你的意思,但即使在 onStart() 中调用 getView() 也会返回 null
  • 这是来自Fragment的方法。我在 MyFragment 中添加了它(即@Override)作为实验。
  • 在 onViewCreated() 里面试试,它在 onCreateView() 之后被调用
  • 我做了同样的事情,所以我尝试了onStart(),它在onViewCreated()之后。即便如此,我知道这可能不是要走的路,因为“如果你通过 setter 或构造函数传递数据,它们将不会被恢复,因此在某些情况下可能会变为 null。”
  • @AllLelopath 我已经尝试在onViewCreated() 内调用getView(),如上面编辑的代码,它不会给出null
【解决方案3】:

从您的代码片段来看,如果在调用 onCreateView(..) 之后初始化了字段 rootViewtableLayout,如果这两个字段之后都没有设置为 null,那么当您调用 onLoadAndStoreComplete(..) 时它们必须不为 null。

也就是说,我认为您是在片段的不同实例中调用该方法。检查您的活动代码并检查您是否始终使用片段中的同一实例。

【讨论】:

    【解决方案4】:

    我需要在 OnCreateView() 之外查看 TableLayout 片段的方法。如果我能得到片段的视图 onLoadAndStoreComplete 可以让我到达那里。

    尝试更改 onLoadAndStoreComplete 的定义以获取 View,然后传入片段的视图。

    【讨论】:

    • 我了解到,由于 Fragment 可以被系统杀死和恢复,如果通过 setter 或构造函数传递数据,它们将无法恢复,因此在某些情况下可能会变为 null。
    • @AllLelopath 片段与活动没有什么不同:使用onSaveInstanceState 将您的数据保存到Bundle。在onViewCreated可以再次恢复数据。
    【解决方案5】:

    你确定在 onCreateView() 之后调用 onLoadAndStoreComplete() 吗?实例变量为空的唯一原因是没有调用初始化它们的方法。

    我建议您在 onAttach、onDetach、onCreate、onCreateView、onStart、onStop、onLoadAndStoreComplete 中调用 Log.d 以查看调用顺序。之后,用日志输出更新你的问题,看看哪个可能是问题所在,也许我可以给你一个更简洁的答案。

    【讨论】:

      【解决方案6】:

      你可以做几个修复:

      如果在启动片段之前调用异步任务,那么您的回调 onLoadAndStoreComplete 可能会在 onCreateView 之前调用(并且您的所有视图都不会膨胀)

      您可以从 onCreateView 执行 asyncTask,但您必须显示一个加载器来告诉用户应用正在同步

      你也可以使用 listview 而不是 tableLayout:

      public class MyFragment extends Fragment implements OnLoadAndStoreCompleteListener {
      
      private ListView listView;
      private View rootView;
      private MyAdapter adapter;
      private List<T> mDatas; /// This one is populated like you want, from onLoadAndStoreComplete
      
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container,  Bundle savedInstanceState) {
          mContext = this.getActivity();
          rootView = inflater.inflate(R.layout.fragment_permits, container, false); // this works
          listView = (ListView) rootView.findViewById(R.id.main_table); // and this
          mDatas = new List<>();
          adapter = new MyAdapter();
          tableLayout.setAdapter(adapter);
      }
      
      @Override
      public void onLoadAndStoreComplete() {
      
         adapter.notifyDataSetChanged();
      }
      
      
      private class MyAdapter extends BaseAdapter{
      
          @Override
          public int getCount() {
              // TODO Auto-generated method stub
              return mDatas.size() == 0 ? 1 : mDatas.size();
          }
      
          @Override
          public Object getItem(int position) {
              // TODO Auto-generated method stub
              return mDatas.get(position);
          }
      
          @Override
          public long getItemId(int position) {
              // TODO Auto-generated method stub
              return 0;
          }
      
          @Override
          public View getView(int position, View convertView, ViewGroup parent) {
              if (mDatas.size()==0){
                  // inflate new view with only a progressbar indefinite 
                 LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                 convertView = inflater.inflate(R.layout.row_loading, parent, false);
                 return convertView;
              }
              else{
                   // list is populated
                 LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                 convertView = inflater.inflate(R.layout.row_datas, parent, false);
                 // do inflating view stuff
                 return convertView;
              }
          }
      
      }
      

      你需要将你的tableLayout修改为ListView(在RelativeLayout里面?)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-03
        相关资源
        最近更新 更多