Al Lelopath
Al Lelopath

Reputation: 6778

How to get the view of a Fragment outside of OnCreateView()

I have a Fragment with a TableLayout. The data in the table is from a SQLite db. The SQLite db is populated from a RESTful webservice in an AsyncTask in the MainActivity. The Fragment must wait for the task to complete before populating the table. The Fragment listens for the task onPostExecute() method to be called. When it is, the method onLoadAndStoreComplete() in the Fragment is called. This all works.

I need to get a view of a TableLayout outside the OnCreateView() method of a Fragment. If I could get the View of the fragment in onLoadAndStoreComplete that would get me there.

Same code as here. I've got mContext from the MainActivity, but that has no getView() method associated with it.

I've tried:
- making a class member rootView and assigning in onCreateView(), but in onLoadAndStoreComplete(), it is null.
- making a class member tableLayout and assigning in onCreateView(), but in onLoadAndStoreComplete(), it is null.
- calling this.getView() again in onLoadAndStoreComplete(), but it returns null.
- calling the inflator inside onLoadAndStoreComplete(), which works, but then I don't know what to use for container in the .inflate() call

I don't understand why the class member values of rootView and tableLayout are null in 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));
    }
    ...
}

Upvotes: 16

Views: 22268

Answers (6)

jskierbi
jskierbi

Reputation: 1635

If I understand your code correctly, there is something wrong either with your code not respecting Fragment lifecycle or a different Fragment instance failure.

A. Lifecycle problem

Android framework expects your Fragment to create its view inside onCreateView() method. View becomes available after framework calls this method.

It is possible that your code called onLoadAndStoreComplete() before onCreateView(), and this has caused the problem.

You need to extract method for populating actual views with data, as it can have 2 entry points. Please see code below:

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. Different instance failure

This may occur when you're operating on 2 different instances of before mentioned fragment.

At first everything will look in order: onCreateView() called before onLoadAndStoreComplete(). Therefore check these:

  • Log/debug default constructor calls for your Fragment. You are expecting to get one call for your fragment during create/load flow.
  • Check whether object that called onCreateView() is the exact same object that called onLoadAndStoreComplete(), you can use System.identityHashCode(this), getting different values within this two methods is a bad sign

If this is what happens, cause is probably hidden a bit deeper, and without code I cannot give you accurate advice on this. How do you create your fragment? Via XML or manually then adding via FragmentManager or via ViewPager?

Upvotes: 11

juanhl
juanhl

Reputation: 1170

From your code fragment, if fields rootView and tableLayout are initialized after calling onCreateView(..), if both fields are not setted to null after that then they must be not null when you call onLoadAndStoreComplete(..).

Said that, I think you are calling that method in a different instance of your fragment. Review your activity code and check that you are using the same instance from the fragment all the time.

Upvotes: 2

Apollo
Apollo

Reputation: 196

You can make several fixes :

If the async task is called before the fragment is launched, then your callback onLoadAndStoreComplete may be called before the onCreateView (and all your views are not inflated)

You can execute the asyncTask from onCreateView, but you have to display a loader to tell user that app is syncing

You can also use a listview and not a 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;
        }
    }

}

You need to modify your tableLayout to a ListView (inside RelativeLayout?)

Upvotes: 1

Adrian
Adrian

Reputation: 719

Are you sure that onLoadAndStoreComplete() is called after onCreateView()? The only reason for instance variables to be null is that the method which initializes them is not being called.

I suggest you to put a call to Log.d in onAttach, onDetach, onCreate, onCreateView, onStart, onStop,onLoadAndStoreComplete to see the in which order are called. After that, update your question with the log output to see which could be the problem and probably I could give you a more concise answer.

Upvotes: 1

Jemshit
Jemshit

Reputation: 10038

getView() gives rootView of Fragment which is returned from onCreatedView(). So if onLoadAndStoreComplete() gets called before onCreatedView() is finished (which it can't return your rootView), you get null since there is not view created yet.


I have tried calling getView() inside onViewCreated() which is NOT null:

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

Upvotes: 8

Joshua Byer
Joshua Byer

Reputation: 519

I need to get a view of a TableLayout outside the OnCreateView() method of a Fragment. If I could get the View of the fragment in onLoadAndStoreComplete that would get me there.

Try changing the definition of onLoadAndStoreComplete to take a View and then passing in a view of the fragment.

Upvotes: 1

Related Questions