I've got Drawer Layout that has four Fragments
One of it is Fragment is called Debtors. Debtors has FragmentViewPager that should return FragmentDebtorsForMe or FragmentDebtorsMeToOther.
The way I create CategoryAdapterDebtors in Debtors.class (Debtors extends Fragment)
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ViewPager viewPager = (ViewPager) view.findViewById(R.id.debtors_viewpager);
TabLayout tabLayout = (TabLayout) view.findViewById(R.id.debtors_tabs);
CategoryAdapterDebtors categoryAdapterDebtors = new CategoryAdapterDebtors
(getChildFragmentManager());
viewPager.setAdapter(categoryAdapterDebtors);
tabLayout.setupWithViewPager(viewPager);
}
CategoryAdapterDebtors that extends FragmentPagerAdapter:
public class CategoryAdapterDebtors extends FragmentPagerAdapter {
public CategoryAdapterDebtors(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
if(position==0)
return new FragmentDebtorsForMe();
if(position==1)
return new FragmentDebtorsMeToOther();
return null;
}
#Override
public CharSequence getPageTitle(int position) {
if (position == 0) {
return "For me";
} else if (position == 1) {
return "Me to other";
}
return null;
}
#Override
public int getCount() {
return 2;
}
I will paste one of two fragment that should appear in ViewPager because they have both the same problem and at the beggining those class have the same content.
public class FragmentDebtorsForMe extends Fragment{
private static final String TAG = FragmentDebtorsForMe.class.getSimpleName();
DatabaseClients dbClients;
List<Client> listOfClients;
public FragmentDebtorsForMe() {
Log.i(TAG, "FragmentDebtorsForMe: START");
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate: start");
super.onCreate(savedInstanceState);
listOfClients = getAllClientsFromDatabase();
Log.i(TAG, "onCreate: end");
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
Log.i(TAG, "onCreateView: START");
View rootView = inflater.inflate(R.layout.recycler, container, false);
Log.i(TAG, "onCreateView: END");
return rootView;
}
#Override
public void onAttach(Context context) {
Toast.makeText(context, "halo", Toast.LENGTH_SHORT).show();
Log.i(TAG, "onAttach: START");
super.onAttach(context);
}
#Override
public void onDetach() {
Log.i(TAG, "onDetach: START");
super.onDetach();
}
public List<Client> getAllClientsFromDatabase() {
dbClients = new DatabaseClients(getContext());
List<Client> clients = dbClients.getAllClient();
for(Client c: clients)
Log.i(TAG, "getAllClientsFromDatabase: " + c.toString());
return clients;
}
}
Problem is that when I am swiping between Fragments any of my Log.i don't respond.
Other strange things is when on my Navigation Drawer I click debtors then Logs are called both from FragmentDebtorsForMe and FragmentDebtorsMeToOther but only once and no more while swiping between ViewPager.
When I create random view for example for FragmentDebtorsMeToOther it show it view properly(onCreateView work) but Log.i still doesn't work
I don't know what could cause this problem.
Both fragments are created at the same time, that's why you see all logs at once. This is because by default, the ViewPager preloads the next and previous pages. The slowdown you are seeing is caused by the fact that you are accessing your database on the main thread in onCreate(), instead of doing it on a background thread. The main thread must never be blocked because it is responsible for responding to UI events.
Related
My app has a tablayout with a view pager. Each page has a fragment. There are 4 different fragments, three of them are basically the same for now (I'm in the development phase right now). One of them has a RecyclerView with a basic list.
I am implementing the Two-pane template in the fragment with the RecyclerView.
Everything seems to be works]ing well. While I move across the tabs the fragments are loaded fine.
But, when I rotate the device and tap on the first tab, and then go back to the one with the recyclerview, I can see the previous intance below. See attached images.
I decided to use static final instances of the fragments in the page adapter and in the recyclerview fragment.
How can I get rid of this problem?
Thanks in advance stackers!
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
TabLayout tabLayout = findViewById(R.id.tab_layout);
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.icon1).setText(R.string.dashboard));
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.icon2).setText(R.string.fragment2));
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.icon3).setText(R.string.fragmentDualPane));
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.icon4).setText(R.string.frag4));
final ViewPager viewPager = findViewById(R.id.pager);
final PagerAdapter pageAdapter = new TabPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pageAdapter);
tabLayout.setupWithViewPager(viewPager);
} // protected void onCreate
} // public class MainActivity
TabPagerAdapter has static final intances of the fragments
public class TabPagerAdapter extends FragmentPagerAdapter {
static final Fragment tabs[] = {new DashboardFragment(),
new Fragment2(),
new ExpensesFragment(),
new Fragment4()
};
public TabPagerAdapter(#NonNull FragmentManager fm) {
super(fm, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
} // public TabPagerAdapter
#NonNull
#Override
public Fragment getItem(int position) {
if (position<tabs.length)
return tabs[position];
else
return null;
} // public Fragment getItem
#Override
public int getCount() {
return this.tabs.length;
} // public int getCount
} // class TabPagerAdapter
General fragment template for dashboard, fragment2, and fragment4
public class DashboardFragment extends Fragment {
public DashboardFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
} // onCreate
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_dashboard, container, false);
}
}
This is the code for the fragment with the dual pane. Look that it uses the OnItemSelected implementation of fragments communications.
This fragment loads another fragment with the recyclerview.
public class ExpensesFragment extends Fragment
implements IOnItemSelected {
#Override
public void onAccountSelected(Account item) {
System.out.println("Clicking on " + item.getTitle() + ", and isTwoPane=" + isTwoPane);
} // public void onAccountSelected
public static final String TAG="Expenses Fragment";
private boolean isTwoPane = false; // Let's assume we're on a phone
private FragmentManager fragmentManager;
private View fragmentView = null;
public ExpensesFragment() {
// Required empty public constructor
} // ExpensesFragment()
public static final ExpensesListFragment lListFragment = new ExpensesListFragment();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
} // onCreate
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
fragmentView = inflater.inflate(R.layout.fragment_expenses, container, false);
isTwoPane = fragmentView.findViewById(R.id.expensesDetailContainer) != null;
fragmentManager = getChildFragmentManager();
if (savedInstanceState==null) {
if ( !lListFragment.isAdded() ) {
fragmentManager.
beginTransaction().
add(R.id.expensesListContainer,lListFragment).
commit();
} // if ( !lListFragment.isAdded() )
} // if (savedInstanceState==null)
if ( isTwoPane ) {
fragmentManager.
beginTransaction().
replace(R.id.expensesDetailContainer,new EmptyFragment()).
commit();
} // if ( isTwoPane )
return fragmentView;
} // onCreateView
} // ExpensesFragment
And this is the fragment with the recyclerview:
public class ExpensesListFragment extends Fragment {
private IOnItemSelected mCallback;
private RecyclerView rv;
private RecyclerView.LayoutManager rvlm;
private RecyclerAdapterAccounts rva;
public ExpensesListFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCallback = (IOnItemSelected)getParentFragment();
} // onCreate
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View fragmentView = inflater.inflate(R.layout.fragment_expenses_list, container, false);
if ( isVisible() ) return fragmentView;
FragmentManager fragmentManager = getChildFragmentManager();
// Setting the recyclerview environment
rv = fragmentView.findViewById(R.id.expensesRV); // recycler view
rvlm = new LinearLayoutManager(getActivity());
rv.setLayoutManager(rvlm);
rva = new RecyclerAdapterAccounts();
rva.setCallBackFunction(mCallback);
rv.setAdapter(rva);
// Setting the floating action button and snackbar
FloatingActionButton fab = fragmentView.findViewById(R.id.fabAdd);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Load a Create Item frag", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
return fragmentView;
} // onCreateView
} // public class ExpensesListFragment
The RecyclerAdapterAccounts creates a generic set of data:
public class RecyclerAdapterAccounts extends
RecyclerView.Adapter<RecyclerAdapterAccounts.ViewHolderAccounts> {
private IOnItemSelected callBackFunction;
public IOnItemSelected getCallBackFunction() {return callBackFunction;}
public void setCallBackFunction(IOnItemSelected callBackFunction) {this.callBackFunction = callBackFunction;}
class ViewHolderAccounts extends RecyclerView.ViewHolder {
ImageView icon, isRepeating, isAlert;
TextView title, total;
public Account getAccount() {return account;}
public void setAccount(Account account) {this.account = account;}
Account account;
public ViewHolderAccounts(View itemView) {
super(itemView);
icon = itemView.findViewById(R.id.list_item_ico_account);
isRepeating = itemView.findViewById(R.id.list_item_isrepeating);
isAlert = itemView.findViewById(R.id.list_item_isalert);
title = itemView.findViewById(R.id.list_item_title_account);
total = itemView.findViewById(R.id.list_item_desc_account);
account = null; // The account needs to be set using the setter/getter method
} // ViewHolderAccounts
} // class ViewHolderAccounts
List<Account> accts = new ArrayList<Account>();
ViewGroup parent;
#NonNull
#Override
public ViewHolderAccounts onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_account,parent,false);
this.parent = parent;
ViewHolderAccounts vh = new ViewHolderAccounts(v);
return vh;
} // onCreateViewHolder
#Override
public void onBindViewHolder(#NonNull ViewHolderAccounts holder, int position) {
// Look into the list the item with id=position
Optional<Account> la = accts.stream()
.filter(ac->ac.getId()==(long)position)
.findFirst();
if ( la.isPresent() ) {
int res = parent.getResources().getIdentifier(la.get().getIcon(), "drawable", "com.almonisolutions.elgddt");
holder.icon.setImageResource(res);
holder.isRepeating.setImageResource(R.drawable.automatic);
holder.isAlert.setImageResource(R.drawable.notifications);
holder.title.setText(la.get().getTitle());
holder.total.setText(la.get().getDescription());
holder.setAccount(la.get());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (holder.getAccount() != null) {
callBackFunction.onAccountSelected(holder.getAccount());
} // if (account != null)
} // public void onClick
});
} // if
} // onBindViewHolder
#Override
public int getItemCount() {
return accts.size();
} // getItemCount
RecyclerAdapterAccounts() {
super();
for(int i=0;i<16;i++) {
Account la = new Account();
la.setId((long) i);
la.setTitle("The item number " + i);
la.setDescription("$" + (1000*i));
switch(i%3) {
case 0: la.setIcon("imaged"); break;
case 1: la.setIcon("person_old"); break;
case 2: la.setIcon("pet"); break;
default: la.setIcon("add");
} // switch
accts.add(la);
} // for
} // RecyclerAdapterAccounts
} // class RecyclerAdapterAccounts
At first, In the ExpensesFragment I was getting an Exception that throw the message "Fragment already added". When I changed the ExpensesListFragment to static final, that error was gone.
Again, to recreate the error, you need to run in portrait mode, move through the tabs. Finish on anyone but the first one. Them rotate the device. Tap on the first tab. Then tap on the 3rd one, the one with the recyclerview. Swipe through the list and you will see it is double.
Any help will be appreciated.
Thanks in advance!!!
So I found the answer. ADM (see comment above) sent me to a previous article where part of the solution was to extend ViewPager and override instantiateItem. But I did not want to extend ViewPager.
However, in the same article was another link to this other article where there was the following explanation:
Blockquote By default, [FragmentPagerAdapter] will only preload one Fragment in front and behind the current position (although it does not destroy them unless you are using FragmentStatePagerAdapter).
So, I made TabPagerAdapter extend FragmentStatePagerAdapter instead of FragmentPageAdapter... and that was it!!
Thanks ADM for pointing to the right series of articles.
Activity
public class GroupesActivity extends BaseActivity {
SelectedBundle selectedBundle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_groupes);
sectionsPagerAdapter = new GroupPagerAdapter(this, getSupportFragmentManager());
viewPager.setAdapter(sectionsPagerAdapter);
tabs.setupWithViewPager(viewPager);
}
private void getAllGroup() {
// api call retrive data
// send data using interface on response
// set data selectedBundle.onBundleSelect(isVisible,calanderModelList,groupModelList,eventModelList);
}
public void setOnBundleSelected(SelectedBundle selectedBundle) {
this.selectedBundle = selectedBundle;
}
public interface SelectedBundle {
void onBundleSelect(boolean isVisible, List<CalanderModel> calanderModelList, List<GroupModel> groupModelList, List<EventModel> eventModelList);
}
}
Frgment
public class FragmentOne extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_all_groups_fragments, container, false);
// get data only once on oncreateview
((GroupesActivity) getActivity()).setOnBundleSelected(new GroupesActivity.SelectedBundle() {
#Override
public void onBundleSelect(boolean isVisible, List<CalanderModel> calanderModelListtt, List<GroupModel> groupModelList, List<EventModel> eventModelList) {
Log.e("retrive data","data")
}
});
return root;
}
}
GroupPagerAdapter
public class GroupPagerAdapter extends FragmentPagerAdapter {
#StringRes
private static final int[] TAB_TITLES = new int[]{R.string.tab_text_1, R.string.tab_text_2, R.string.tab_text_3};
private final Context mContext;
public GroupPagerAdapter(Context context, FragmentManager fm) {
super( fm);
this.mContext=context;
}
#NonNull
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new AllGroupsFragments();
case 1:
return new HostFragments();
case 2:
return new GuestFragments();
}
return null;
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return mContext.getResources().getString(TAB_TITLES[position]);
}
#Override
public int getCount() {
return 3;
}
}
problem
i have three fragments in tabview when i swipe this obove fragment
and then come again in this fragmnet that interface not called and i'm
not getting data from activity
How to get that data again from its parent activity, i need to only
retrive that data from activity on each time of fragments oncreateView
Thanks in adavance ;)
Use setMenuVisibility() method in Fragment to fix the issue.
//In fragments
#Override
public void setMenuVisibility(final boolean isVisible) {
super.setMenuVisibility(isVisible);
if (isVisible) {
//visible to user- do ur stuff
((GroupesActivity) getActivity()).setOnBundleSelected(new
GroupesActivity.SelectedBundle() {
#Override
public void onBundleSelect(boolean isVisible, List<CalanderModel>
calanderModelListtt, List<GroupModel> groupModelList, List<EventModel>
eventModelList) {
Log.e("retrive data","data")
}
});
}
}
the following link may help if you face any issues:
How to determine when Fragment becomes visible in ViewPager
I've been going around in circles trying to do something that seems pretty basic. I have a DialogFragment that accepts a users input, then, on submission, refreshes a ListView in a Fragment that is part of a ViewPager.
I have everything working except the Fragment with the ListView does not refresh itself. It's a little confusing though, because it does refresh the data, but I have to swipe a couple views, then back again to see the updated data.
After doing some research, I'm supposed to use getItemPosition and notifyDataSetChanged on the ViewPager and it should work. The problem is that calling notifyDataSetChanged results in a Recursive entry to executePendingTransactions exception being thrown:
Main Activity
public class Main extends SherlockFragmentActivity implements MyListFragment.OnRefreshAdapterListener, DialogConfirmation.OnRefreshKeywordsListener //Updated Code
{
private static List<Fragment> fragments;
#Override
public void onCreate(final Bundle icicle)
{
setContentView(R.layout.main);
}
#Override
public void onResume()
{
mViewPager = (ViewPager)findViewById(R.id.viewpager);
fragments = new ArrayList<Fragment>();
fragments.add(new MyListFragment()); //fragment with the ListView
fragments.add(MyDetailFragment.newInstance(0));
fragments.add(MyDetailFragment.newInstance(1));
fragments.add(MyDetailFragment.newInstance(2));
mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mMyFragmentPagerAdapter);
}
private static class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
return fragments.get(index);
}
#Override
public int getCount() {
return 4;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
#Override
public void onRefreshAdapterListener() {
this.mMyFragmentPagerAdapter.notifyDataSetChanged();
}
//Updated Code
#Override
public void onRefreshTextListener() {
MyListFragment tf = (MyListFragment)getSupportFragmentManager().findFragmentById(R.id.fragmentText);
if (tf == null)
tf = (MyListFragment)this.fragments.get(0);
tf.RefreshText();
}
}
ListFragment
public class MyListFragment extends SherlockListFragment
{
OnRefreshAdapterListener mRefreshAdapter;
#Override
public void onActivityCreated(Bundle savedState) {
adapter = new CustomAdapter();
/*code to add items to adapter */
this.setListAdapter(adapter);
adapter.notifyDataSetChanged();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (getArguments() != null && getArguments().getString("text").length() > 0)
{
SaveText(getArguments().getString("text"));
this.mRefreshAdapter.onRefreshAdapterListener(); //this line causes a "java.lang.IllegalStateException: Recursive entry to executePendingTransactions" exception
}
return inflater.inflate(R.layout.listing, container, false);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mRefreshAdapter = (OnRefreshAdapterListener)activity;
}
public interface OnRefreshAdapterListener {
public void onRefreshAdapterListener();
}
#Override
public void onDialogTextAdd(final String text) {
}
}
DialogFragment
public class DialogTextAdd extends DialogFragment implements OnEditorActionListener {
private EditText mText;
OnRefreshTextListener mTextKeywords; //Updated Code
public interface DialogTextAddListener {
void onDialogTextAdd(final String inputText);
}
public DialogTextAdd() {
// Empty constructor required for DialogFragment
}
//Updated Code
#Override
public void onAttach(Activity act) {
super.onAttach(act);
mTextKeywords = (OnRefreshTextListener)act;
}
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.dialog_edit, container);
mText = (EditText)view.findViewById(R.id.text_add);
getDialog().setTitle("Add Text");
// Show soft keyboard automatically
mText.requestFocus();
getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
mText.setOnEditorActionListener(this);
return view;
}
#Override
public boolean onEditorAction(final TextView v, final int actionId, final KeyEvent event) {
if (EditorInfo.IME_ACTION_DONE == actionId) {
MyListFragment mf = new MyListFragment();
Bundle args = new Bundle();
args.putString("text", mText.getText().toString());
mf.setArguments(args);
//this seems to be intefering with the notifyDataSetChanged in the listing fragment
getActivity().getSupportFragmentManager().beginTransaction().add(mf, "my_fragment").commit();
mTextKeywords.onRefreshTextListener(); //Updated Code
this.dismiss();
return true;
}
return false;
}
}
I have everything working except the Fragment with the ListView does
not refresh itself.
There is no point on creating and adding to the FragmentActivity a new instance of MyListFragment. From your code it appears that you store the fragments that you use in a list so you have references to them(also, just out of curiosity, did you setup the fragments in portrait, did a rotation of the phone and retried to use the DialogFragment?). Having references to those fragment means you could always get them from the list and use them to call a refresh/update method.
In order to focus on the problem I ended with the following test app which contains one activity and two fragments which are connected to FragmentStatePagerAdapter / ViewPager. I also have Runnable and Handler printing getView() every second (in production version it is used to show time in fragment's TextView).
When I start app, getView() shows maybe few nulls (I suppose due to fragment life cycle) and then I get some non nulls which is ok...
But if I suspend and wake up my device LogCat shows nulls all the time.
public class MainActivity extends SherlockFragmentActivity {
private SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
private Fragment myFragment1 = new MyFragment1();
private Fragment myFragment2 = new MyFragment2();
private Handler handler = new Handler();
private Runnable timer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(sectionsPagerAdapter);
}
#Override
protected void onResume() {
super.onResume();
timer = new Runnable() {
public void run() {
System.out.println(myFragment1.getView()); // HERE getView gives null
handler.postDelayed(this, 1000); // refreshing every 1 sec
}
};
handler.removeCallbacks(timer);
handler.postDelayed(timer, 0);
}
#Override
protected void onPause() {
super.onPause();
handler.removeCallbacks(timer);
}
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment;
if (position == 0) {
fragment = myFragment1;
} else {
fragment = myFragment2;
}
return fragment;
}
#Override
public int getCount() {
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Fragment 1";
case 1:
return "Fragment 2";
}
return null;
}
}
}
My fragment
public class MyFragment1 extends SherlockFragment {
private TextView textView2;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_layout, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((TextView) getView().findViewById(R.id.textView1)).setText("Fragment 1");
textView2 = (TextView) this.getView().findViewById(R.id.textView2);
}
public void updateTextView2(String text) {
textView2.setText(text);
}
Ok I solved my problem simply by adding:
if (myFragment1.getView() == null) {
viewPager.setAdapter(sectionsPagerAdapter);
}
I'm not happy about this solution but it works...
I have a fragment activity that uses a ViewPager to display a set of fragments. On the fragment activity I have a button that when clicked, it sends a message to the current fragment to refresh its contents. Everything works ok (activity / current fragment communication) except the fact that I cannot refresh the fragment's view. Accessing the current view by getView() does not work as this function returns null; it seems that after the fragment is created (on ViewCreated is called) getView gets destroyed. Am I missing something here? How to I cause a fragment to redraw its contents programmatically? It seems that the only way this works is when the fragment is created from the parent activity. Do I have to remove and re-add the fragment again to do this?
Here is the code:
The main activity:
public class MainActivity extends FragmentActivity {
private MyAdapter mAdapter;
private static ViewPager mPager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupViewPager();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
return super.onPrepareOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_test:
updateFragment();
return true;
default: return true;
}
}
private void updateFragment() {
for (int i=0; i< mAdapter.getCount(); i++) {
SampleFragment fragment = (SampleFragment) mAdapter.getItem(i);
fragment.update();
}
}
private void setupViewPager() {
try {
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(this.mAdapter);
} catch (Exception e) {
e.printStackTrace();
}
}
public class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
SampleFragment fragment = new SampleFragment(position);
return fragment;
}
#Override
public int getCount() {
return 5;
}
}
}
and the fragment class:
public class SampleFragment extends Fragment{
private int myPosition = -1;
public SampleFragment(int position) {
this.myPosition = position;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment, container, false);
update(view, "Updated from onCreateView");
return view;
}
#Override
public void onActivityCreated(Bundle bundle) {
super.onActivityCreated(bundle);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.findViewById(R.id.textTitle).setOnClickListener(myClickListener);
}
private OnClickListener myClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.textTitle:
break;
}
}
};
public void update() {
update(getView(), "Updated from main");
}
private void update(View view, String subtitleText) {
TextView title = (TextView) view.findViewById(R.id.textTitle);
TextView subtitle = (TextView) view.findViewById(R.id.textSubtitle);
title.setText("Fragment " + myPosition);
subtitle.setText(subtitleText);
}
}
The error happens on view.FindViewById (view is null) when called from the menu item in the main activity.
You can take a look at this article which explains how to keep references to the fragments in your ViewPager.
There are two methods described on the page. The first one involves setting a tag when you add the fragment using the FragmentManager. Then you can retrieve the fragment using findFragmentByTag(). However, I did not see how to make this work using FragmentPagerAdapter or FragmentStatePagerAdapter, since these implementations add the fragments for you. If you are using your own custom PagerAdapter, this may work for you.
The other method, which does work for FragmentPagerAdapter or FragmentStatePagerAdapter, involves keeping a map of all your fragments, updating inside your getItem() and destroyItem() implementations. This method has worked for me.
Once you have a reference to the current fragment, you can just call a method in your fragment to refresh its view.