2015-04-13 3 views
8

У меня есть Fragment с TableLayout. Данные в таблице взяты из SQLite db. SQLite db заполняется из веб-службы RESTful в AsyncTask в MainActivity. Fragment должен дождаться завершения задачи перед заполнением таблицы. Fragment прослушивает задание onPostExecute(). Когда это так, вызывается метод onLoadAndStoreComplete() в Fragment. Все это работает.Как получить представление фрагмента вне OnCreateView()

Мне нужно получить TableLayout за пределами OnCreateView() метод фрагмента. Если бы я мог получить вид фрагмента в onLoadAndStoreComplete, который бы доставил меня туда.

Same code as here. Я получил mContext от MainActivity, но это не имеет никакого getView() метод, связанный с ним.

Я пробовал:
- сделать rootView члена класса и назначения в onCreateView(), но в onLoadAndStoreComplete(), она равна нулю.
- создание таблицы-члена классаLayout и назначение в onCreateView(), но в onLoadAndStoreComplete() оно равно null.
- вызов this.getView() снова в onLoadAndStoreComplete(), но он возвращает null.
- вызов инфлятором внутри onLoadAndStoreComplete(), который работает, но тогда я не знаю, что использовать для контейнера в .inflate() вызова

Я не понимаю, почему значения членов класса из rootView и TableLayout равны нулю в onLoadAndStoreComplete()

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)); 
    } 
    ... 
} 
+0

Что такое 'OnLoadAndStoreCompleteListener' и когда он вызывается? Не удается найти это в Android API – m0skit0

+0

Никогда не слышал о 'onLoadAndStoreComplete()' вы уверены, что соблюдаете жизненный цикл «Фрагмента»? Если бы вы тогда 'getView()' не вернули бы 'null' ... –

+1

Также попытка получить доступ к' View' из 'Fragment' за пределами указанного« Fragment »является очень плохим дизайном ... Я предлагаю вам переосмыслить, что вы хотите сделать и попробовать более абстрактное решение. –

ответ

6

Если я понимаю ваш код правильно, есть что-то не так с вашим кода не уважая фрагмент жизненного цикла или непринятие разного Фрагмент экземпляра ,

A. Lifecycle проблема

Android рамки ожидает ваш фрагмент, чтобы создать свою точку зрения внутри onCreateView() метода. Просмотреть становится доступным после framework вызывает этот метод.

Возможно, ваш код под названием onLoadAndStoreComplete() до onCreateView(), и это вызвало проблему.

Вам необходимо извлечь метод для заполнения фактических представлений данными, так как он может иметь 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?

+0

Это, в конечном счете, моя проблема. Я думал, что «во Фрагменте» означает в моем классе Фрагмент, т. Е. Какой-либо метод в классе, будь он унаследован от «Фрагмента» или того, который я создал сам. Это неверно. Это означает только методы, наследованные от 'Fragment'. –

5

getView() дает rootView фрагмента, который возвращается из onCreatedView(). Поэтому, если onLoadAndStoreComplete() вызывается до завершения onCreatedView() (который он не может вернуть rootView), вы получаете null, так как пока не создано показ.


Я пытался дозвониться getView() внутри onViewCreated() который НЕ является пустым:

@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 
     } 
    } 
+0

Я вижу вашу точку, но даже вызов' getView() 'в' onStart() 'возвращает null –

+0

Что такое onStart()? –

+0

Это метод из «Фрагмента». Я добавил его (т.е. @Override) в MyFragment в качестве эксперимента. –

1

Мне нужно, чтобы получить вид TableLayout вне OnCreateView() методом фрагмента. Если бы я мог получить вид фрагмента в onLoadAndStoreComplete, который бы доставил меня туда.

Попробуйте изменить определение onLoadAndStoreComplete, чтобы получить представление, а затем передать его в виде фрагмента.

+0

Я узнал, что, поскольку фрагмент может быть убит и восстановлен системой, если данные переданы через сеттеры или конструкторы они не будут восстановлены и, следовательно, могут стать нулевыми при некоторых обстоятельствах. –

+0

@AlLelopath Фрагменты не отличаются от действий: используйте 'onSaveInstanceState', чтобы сохранить ваши данные в' Bundle'. В 'onViewCreated' вы можете восстановить данные снова. – tiguchi

1

Вы уверены, что onLoadAndStoreComplete() вызывается после onCreateView()? Единственная причина, по которой переменные экземпляра имеют значение null, заключается в том, что метод, который их инициализирует, не вызывается.

Я предлагаю вам позвонить в Log.d в onAttach, onDetach, onCreate, onCreateView, onStart, onStop, onLoadAndStoreComplete, чтобы увидеть, в каком порядке вызывается. После этого обновите свой вопрос с помощью вывода журнала, чтобы узнать, какая проблема может возникнуть, и, возможно, я смогу дать вам более краткий ответ.

2

Из вашего фрагмента кода, если поля rootView и tableLayout инициализируются после вызова onCreateView(..), если оба поля не выставиться обнулить после этого, то они должны быть не нулевым, когда вы звоните onLoadAndStoreComplete(..).

Сказал, что, я думаю, вы вызываете этот метод в другом экземпляре вашего фрагмента. Просмотрите свой код активности и убедитесь, что вы используете один и тот же экземпляр из фрагмента все время.

1

Вы можете сделать несколько исправлений:

Если задача асинхронной вызываются до фрагмента запускается, то ваш обратный вызов onLoadAndStoreComplete может быть вызван до onCreateView (и все ваши взгляды не завышены)

Вы может выполнить AsyncTask из onCreateView, но вы должны показать загрузчик, чтобы сообщить пользователю, что приложение синхронизируется

вы также можете использовать 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?)