I have 3 fragments in a ViewPager Activity. All 3 fragments have input fields. Here I am trying to pass first two fragments data to third fragment. I read few posts here and most of them suggested to use interfaces(i.e. to pass data through parent activity)
I have also gone through this link
http://developer.android.com/training/basics/fragments/communicating.html
Interface: using interfaces is good approach when we are sending data through some user event. Here I am trying to send data without any user event. Hence I thought of onPause() since onPause() is always called. But ViewPager functions differently. When a fragment is loaded,the adjacent fragments are also loaded. I would be successful to pass data between 1st fragment to 3rd fragment. But 2nd fragment's onPause() wont be called unless I am navigating to some fragment that is not adjacent to it(which in my case is not there)
Setter/Getters:I have read in few posts people saying not to use setter/getters(I still havent understood the reason yet) Are getters and setters poor design? Contradictory advice seen
Bundle: I havent considered this yet. Since I am again confused here how would I pass data using bundle.(inside which method should I send data? and how?)
Sorry if my question sounds dumb.I am trying to understand fragments and i would like to know best way to pass data between fragments in viewpager.
Thank You in advance.
TabPAgerAdapter -- >
package com.jbandroid.model;
import com.jbandroid.fragment.LocationInfoFragment;
import com.jbandroid.fragment.PersonalInfoFragment;
import com.jbandroid.fragment.PostInfoFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
public class TabsPagerAdapter extends FragmentPagerAdapter {
public TabsPagerAdapter(FragmentManager fm){
super(fm);
}
#Override
public Fragment getItem(int index) {
switch(index) {
case 0 : //PostInfoFragment
return new PostInfoFragment();
case 1 : //LocationInfoFragment
return new LocationInfoFragment();
case 2 : //PersonalInfoFragment
return new PersonalInfoFragment();
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 3;
}
}
ViewPagerActivity -- >
package com.jbandroid;
public class SubmitPostActivity extends FragmentActivity implements ActionBar.TabListener,PostInfoFragment.setPostInfo,LocationInfoFragment.setLocationInfo{
private ViewPager viewpager;
private ActionBar actionBar;
private TabsPagerAdapter mAdapter;
FragmentManager manager;
PersonalInfoFragment frag;
List<String> location;
/*private MenuItem myActionMenuItem;
private Button myActionButton;*/
//Tab titles
private String[] tabs = {"Post Info" , "Location Info" , "Personal Info" };
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.action_submit_post);
viewpager = (ViewPager) findViewById(R.id.pager);
actionBar = getActionBar();
manager = getSupportFragmentManager();
mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
//viewpager.setOffscreenPageLimit(2);
viewpager.setAdapter(mAdapter);
//actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
for (String tab : tabs){
actionBar.addTab(actionBar.newTab().setText(tab).setTabListener(this));
}
if(savedInstanceState != null){
actionBar.setSelectedNavigationItem( savedInstanceState.getInt("tab",0));
}
/**
* on swiping the viewpager make respective tab selected
* */
viewpager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
actionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// on tab selected
// show respected fragment view
viewpager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void pass_location_details(List<String> location) {
frag = (PersonalInfoFragment) manager.findFragmentByTag("android:switcher:" + viewpager.getId() + ":" + 2);
frag.get_post_location_details(location);
Log.d("submitarea", location.get(0));
}
#Override
public void pass_post_details(List<String> post_details,ArrayList<CustomGallery> selected) {
frag = (PersonalInfoFragment) manager.findFragmentByTag("android:switcher:" + viewpager.getId() + ":" + 2);
frag.get_post_details(post_details,selected);
Log.d("submitpostinfo","hello"+ post_details.get(5));
}
}
1st Fragment(Here I am trying to pass data using interface in onPause()-->
package com.jbandroid.fragment;
public class PostInfoFragment extends Fragment {
private MenuItem myActionMenuItem;
private Button myActionButton;
private ActionBar actionBar;
private String post_title, post_desc,post_status;
private EditText submit_post_title, submit_post_desc;
private Resources res;
setPostInfo info;
List<String> post_details;
//RelativeLayout rel_submit_post_start_date,rel_submit_post_end_date;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_post_info,
container, false);
/*if(!imageLoader.isInited()){*/
initImageLoader();
/*}*/
//handler = new Handler();
submit_post_title = (EditText) rootView
.findViewById(R.id.submit_post_title);
submit_post_desc = (EditText) rootView
.findViewById(R.id.submit_post_description);
actionBar = getActivity().getActionBar();
setHasOptionsMenu(true);
post_details = new ArrayList<String>();
res = getResources();
setListeners();
Log.d("postinfo_oncreate view", "postinfo_oncreate view");
return rootView;
}
//interface to pass data to activity and then to PersonalInfoFragment
public interface setPostInfo {
//public void pass_post_details(List<String> post_details);
public void pass_post_details(List<String> post_details,ArrayList<CustomGallery> selected);
}
//making sure if the parent activity has implemented interface
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
info = (setPostInfo) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ "must implemet setPostInfo");
}
Log.d("postinfo_onattach", "postinfo_onattach");
}
//passing form inputs to personalinfofragments
#Override
public void onPause() {
super.onPause();
// setFormInputs();
passFormInputs(); ---> passing in onPause() This executes successfully
Log.d("postinfo_onPAuse", "postinfo_onPause");
}
//method to pass data to personalinfofragment
private void passFormInputs() {
try {
post_title = submit_post_title.getText().toString();
post_desc = submit_post_desc.getText().toString();
post_status = "1";
if(post_title != null && post_title.length() > 0
&& post_desc != null && post_desc.length() > 0
&& post_status != null && post_status.length() > 0
){
post_details.add(post_title);
post_details.add(post_desc);
post_details.add(post_status);
info.pass_post_details(post_details,dataT); -->here I am passing values via
}else{ activity to 3rd fragment
Log.d("post_info", "values are null");
}
} catch (Exception e) {
e.printStackTrace();
}
}
//setting next button on actionbar
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
// Inflate the menu items for use in the action bar
inflater.inflate(R.menu.mymenu, menu);
// Here we get the action view we defined
myActionMenuItem = menu.findItem(R.id.my_action);
View actionView = myActionMenuItem.getActionView();
// We then get the button view that is part of the action view
if (actionView != null) {
myActionButton = (Button) actionView.findViewById(R.id.action_btn);
myActionButton.setText(R.string.txt_next);
if (myActionButton != null) {
// We set a listener that will be called when the return/enter
// key is pressed
myActionButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
actionBar.setSelectedNavigationItem(1);
}
});
}
}
}
}
2nd Fragment-->
package com.jbandroid.fragment;
public class LocationInfoFragment extends Fragment implements OnClickListener {
private MenuItem myActionMenuItem;
private Button myActionButton;
private ActionBar actionBar;
Dialog dialog;
private EditText submit_post_exact_location;
private TextView selected_country, selected_city,
submit_post_exact_time;
String country, city, exact_location, exact_time;
setLocationInfo info;
List<String> location;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_location_info,
container, false);
actionBar = getActivity().getActionBar();
setHasOptionsMenu(true);
submit_post_exact_location = (EditText) rootView
.findViewById(R.id.submit_post_exact_location);
submit_post_exact_time = (TextView) rootView
.findViewById(R.id.submit_post_exact_time);
selected_country = (TextView) rootView
.findViewById(R.id.selected_country);
selected_city = (TextView) rootView.findViewById(R.id.selected_city);
location = new ArrayList<String>();
setListeners();
return rootView;
}
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
// Inflate the menu items for use in the action bar
inflater.inflate(R.menu.mymenu, menu);
// Here we get the action view we defined
myActionMenuItem = menu.findItem(R.id.my_action);
View actionView = myActionMenuItem.getActionView();
// We then get the button view that is part of the action view
if (actionView != null) {
myActionButton = (Button) actionView.findViewById(R.id.action_btn);
myActionButton.setText(R.string.txt_next);
if (myActionButton != null) {
// We set a listener that will be called when the return/enter
// key is pressed
myActionButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
actionBar.setSelectedNavigationItem(2);
}
});
}
}
}
public interface setLocationInfo {
public void pass_location_details(List<String> location);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
info = (setLocationInfo) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ "must implement setLocationInfo");
}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//setLocationDetails();
}
#Override
public void onPause() {
super.onPause();
setLocationDetails(); ----> doesnt executes since onPause isnt called when I navigate to 3rd fragment as it is an adjacent fragment of this fragment
// Log.d("location : onPause", area);
}
private void setLocationDetails() {
try {
exact_location = submit_post_exact_location.getText().toString();
exact_time = submit_post_exact_time.getText().toString();
country = selected_country.getText().toString();
city = selected_city.getText().toString();
if (country != null && country.length() > 0
&& !country.equalsIgnoreCase("select") && city != null
&& city.length() > 0 && !city.equalsIgnoreCase("select")
&& exact_location != null && exact_location.length() > 0
&& exact_time != null && exact_time.length() > 0) {
location.add(country);
location.add(city);
location.add(exact_location);
location.add(exact_time);
info.pass_location_details(location);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
In my 3rd Fragment I am trying to get this values
public class PersonalInfoFragment extends Fragment {
List<String> post_details;
List<String> location;
Button submit;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_personal_info,
container, false);
submit = (Button)rootView.findViewById(R.id.submitBtn);
submit.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//performing operations with the values obtained
setPostItems();
insertintodb();
}
});
return rootView;
}
public void get_post_details(List<String> post_details,
ArrayList<CustomGallery> selected) { -->receiving values from
this.post_details = post_details; 1st fragment
this.selected = selected;
Log.d("personalfrag(postinfo)", "hello" + post_details.get(5));
}
//receiving values from 2nd fragment
public void get_post_location_details(List<String> location) {
this.location = location;
Log.d("personalfrag(locationinfo)", "hello" + location.get(0));
}
}
Okay, I Had same issue to pass data(not just string) between two tabs in a ViewPager. So here is what i did.
I Use interfaces to communicate between the different components.
The data passes this way:
Tab 1 -> Activity -> VewPageAdapter -> Tab 2
In Tab 1
create an interface.
OnCartsDataListener mOncarOnCartsDataListener;
public interface OnCartsDataListener {
public void onCartsDataReceived(ArrayList<CartsViewModel> cartsViewModels);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mOncarOnCartsDataListener = (OnCartsDataListener)activity;
}catch (ClassCastException e){
}
}
// now call mOncarOnCartsDataListener.onCartsDataReceived(data) when you have the data
In Activity
Implement the interface and override the method
ViewPagerAdapter adapter;
adapter = new ViewPagerAdapter(getSupportFragmentManager(), Titles, Numboftabs);
#Override
public void onCartsDataReceived(ArrayList<CartsViewModel> cartsViewModels) {
Log.d(TAG, "data received to Activity... send to view pager");
adapter.onCartsDataReceived(cartsViewModels);
}
3.IN ViewPagerAdapter
Also implements the interface and override the method
#Override
public void onCartsDataReceived(ArrayList<CartsViewModel> cartsViewModels) {
Log.d(TAG, "data received to view pager... sending to tab 2");
if(tab2!=null){
tab2.onCartsDataReceived(cartsViewModels);
}else{
Log.d(TAG, "tab2 is null");
}
}
Finally tab 2
Also implements the interface and override the method
#Override
public void onCartsDataReceived(ArrayList<CartsViewModel> cartsViewModels) {
Log.d(TAG, "Finally ! received data to tab 2");
if(cartsViewModels!=null){
for(CartsViewModel cart : cartsViewModels){
Log.d(TAG,"got it :"+cart.getCartName());
}
}
}
Since AndroidX, you can create a ViewModel and share data between Activity and all fragments within ViewPager
Read here how to
Can you do something like this? First create any data structure like Arraylist in your main activity. Then send a reference of that data model to your fragments. Now update that data when, on change your text fields. By doing this all the fragment can see updated values. So fragments can update this data itself and we don't need to send that data since it is already shared. I'll explain this using your example. Try to improve this. You can maintain fragment specific data model then each fragment can access data with the knowledge of that data owner.
TabsPagerAdapter.java
public class TabsPagerAdapter extends FragmentPagerAdapter {
public TabsPagerAdapter(FragmentManager fm,SubmitPostActivity activity){
super(fm);
}
#Override
public Fragment getItem(int index) {
switch(index) {
case 0 : //PostInfoFragment
return new PostInfoFragment(0,activity);
case 1 : //LocationInfoFragment
return new LocationInfoFragment(1,activity);
case 2 : //PersonalInfoFragment
return new PersonalInfoFragment(2,activity);
}
return null;
}
#Override
public int getCount() {
return 3;
}
}
ViewPagerActivity -- >
package com.jbandroid;
public class SubmitPostActivity extends FragmentActivity implements ActionBar.TabListener,LocationInfoFragment.setLocationInfo{
private ViewPager viewpager;
private ActionBar actionBar;
private TabsPagerAdapter mAdapter;
FragmentManager manager;
PersonalInfoFragment frag;
List<String> location;
/*private MenuItem myActionMenuItem;
private Button myActionButton;*/
//Tab titles
private String[] tabs = {"Post Info" , "Location Info" , "Personal Info" };
public List<String> dataModel = new ArrayList<String>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.action_submit_post);
viewpager = (ViewPager) findViewById(R.id.pager);
actionBar = getActionBar();
manager = getSupportFragmentManager();
mAdapter = new TabsPagerAdapter(getSupportFragmentManager(),this);
//viewpager.setOffscreenPageLimit(2);
viewpager.setAdapter(mAdapter);
//actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
for (String tab : tabs){
actionBar.addTab(actionBar.newTab().setText(tab).setTabListener(this));
}
if(savedInstanceState != null){
actionBar.setSelectedNavigationItem( savedInstanceState.getInt("tab",0));
}
}
}
1st Fragment =>
public class PostInfoFragment extends Fragment {
private MenuItem myActionMenuItem;
private Button myActionButton;
private ActionBar actionBar;
private String post_title, post_desc,post_status;
private EditText submit_post_title, submit_post_desc;
private int position;
private Resources res;
SubmitPostActivity callingActivity;
List<String> post_details;
public PostInfoFragment(int position,SubmitPostActivity callingActivity )
{
this.callingActivity = callingActivity;
this.position = position;
}
//RelativeLayout rel_submit_post_start_date,rel_submit_post_end_date;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_post_info,
container, false);
/*if(!imageLoader.isInited()){*/
initImageLoader();
/*}*/
//handler = new Handler();
submit_post_title = (EditText) rootView
.findViewById(R.id.submit_post_title);
submit_post_desc = (EditText) rootView
.findViewById(R.id.submit_post_description);
actionBar = getActivity().getActionBar();
setHasOptionsMenu(true);
post_details = new ArrayList<String>();
res = getResources();
setListeners();
Log.d("postinfo_oncreate view", "postinfo_oncreate view");
//this is editText onchange listner do the same for submit_post_desc as well
submit_post_title.addTextChangedListener( new TextWatcher()
{
#Override
public void onTextChanged( CharSequence s, int start, int before, int count )
{
}
#Override
public void beforeTextChanged( CharSequence s, int start, int count, int after )
{
}
#Override
public void afterTextChanged( Editable s )
{
if( callingActivity != null )
{
//use this.position in order to update relevant data
List<String> post_details = callingActivity.dataModel;
if( post_details == null )
{
post_details = new ArrayList<String>();
}
post_details.add(s.toString());
}
}
} );
return rootView;
}
//making sure if the parent activity has implemented interface
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
callingActivity = (SubmitPostActivity) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ "must implemet setPostInfo");
}
Log.d("postinfo_onattach", "postinfo_onattach");
}
}
Please not that this may not compile as it is. Try to get the concept.
Related
I have an application in which I create TabItems dynamically and I add them to the TabLayout. I'll show you the code below. then I also have a mechanism that when a tabitem is created, the user can close it with a click of a button. NOW: the problem happens here. when the user deletes that tabitem and program automatically directs the user to another tab. I can no longer click on the other tabs that I created at the start of the application. I Can click on them, but the program closes with the error
java.lang.IllegalStateException: The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged! Expected adapter item count: 4, found: 3 Pager
and all of this happens when I delete a tabitem with a code written for a click. below is my MainActivity.java code :
public class MainActivity extends AppCompatActivity implements ContactsFragment.CallBacks, UserDetailFragment.DetailCallBacks {
android.support.v7.widget.Toolbar toolbar;
public static List<Fragment> fragments = new ArrayList<>();
public static List<String> fragmentsTitle = new ArrayList<>();
ViewPager viewPager;
TabLayout tabLayout;
int tabposition_number;
public List<Fragment> getFragments() {
return fragments;
}
public List<String> getFragmentsTitle() {
return fragmentsTitle;
}
public void addToFragments(Fragment fragment) {
fragments.add(fragment);
}
public void addToFragmentsTitle(String title) {
fragmentsTitle.add(title);
}
public Fragment getFragmentsWithPosition(int position) {
return fragments.get(position);
}
public String getFragmentsTitleWithPosition(int position) {
return fragmentsTitle.get(position);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = new MenuInflater(this);
menuInflater.inflate(R.menu.top_main_menu,menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == R.id.remove_tab) {
remove_tab_details(3);
}
return super.onOptionsItemSelected(item);
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_page_drawer);
this.tabLayout = findViewById(R.id.tab_layout);
this.viewPager = findViewById(R.id.view_pager);
tabLayout.setupWithViewPager(viewPager);
SetUpViewPager(viewPager);
this.toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
NavigationView navigationView = findViewById(R.id.navigation_view);
navigationView.setItemIconTintList(null);
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
#Override
public void onTabSelected(TabLayout.Tab tab) {
if(tab.getPosition() > 2) {
tabposition_number = tab.getPosition();
}
// viewPager.setCurrentItem(tab.getPosition());
if(tab.getPosition() == 1) {
toolbar.setBackgroundColor(ContextCompat.getColor(MainActivity.this,R.color.tab_contacts));
tabLayout.setBackgroundColor(ContextCompat.getColor(MainActivity.this,R.color.main_contacts));
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setStatusBarColor(ContextCompat.getColor(MainActivity.this,R.color.status_contacts));
}
} else if(tab.getPosition() == 2) {
toolbar.setBackgroundColor(ContextCompat.getColor(MainActivity.this,R.color.tab_register));
tabLayout.setBackgroundColor(ContextCompat.getColor(MainActivity.this,R.color.main_register));
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setStatusBarColor(ContextCompat.getColor(MainActivity.this,R.color.status_register));
}
} else {
toolbar.setBackgroundColor(ContextCompat.getColor(MainActivity.this,R.color.tab_signin));
tabLayout.setBackgroundColor(ContextCompat.getColor(MainActivity.this,R.color.main_signin));
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setStatusBarColor(ContextCompat.getColor(MainActivity.this,R.color.status_signin));
}
}
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
}
public void SetUpViewPager(ViewPager viewPager) {
MyViewPagerAdapter Adapter = new MyViewPagerAdapter((getSupportFragmentManager()));
Adapter.AddFragmentPage(new SignInFragment(),"ورود");
Adapter.AddFragmentPage(new ContactsFragment(),"ارتباطات");
Adapter.AddFragmentPage(new RegisterFragment(),"ثبت نام");
Adapter.notifyDataSetChanged();
viewPager.setAdapter(Adapter);
}
#Override
public void create_user_detail_tab(UserObject userObject) {
MyViewPagerAdapter Adapter = new MyViewPagerAdapter(getSupportFragmentManager());
UserDetailFragment userDetailFragment = new UserDetailFragment();
Bundle bundle = new Bundle();
bundle.putString("name",userObject.getName());
bundle.putString("family",userObject.getFamily());
bundle.putString("email",userObject.getEmail());
userDetailFragment.setArguments(bundle);
Adapter.AddFragmentPage(userDetailFragment,userObject.getName());
viewPager.setAdapter(Adapter);
TabLayout.Tab tab = tabLayout.getTabAt(1);
tab.select();
}
#Override
public void delete_previous_tab(int tabposition_number) {
remove_tab_details(tabposition_number);
MyViewPagerAdapter myViewPagerAdapter = new MyViewPagerAdapter(getSupportFragmentManager());
myViewPagerAdapter.notifyDataSetChanged();
}
#Override
public void changeTabItem(boolean mustdelete) {
ContactsFragment contactsFragment = new ContactsFragment();
if(tabposition_number > 2 && mustdelete) {
contactsFragment.setTextView(tabposition_number,mustdelete);
TabLayout.Tab tab = tabLayout.getTabAt(1);
tab.select();
}
}
public class MyViewPagerAdapter extends FragmentPagerAdapter {
public MyViewPagerAdapter(FragmentManager manager) {
super(manager);
}
public void removeTabPage(int position) {
fragments.remove(position);
fragmentsTitle.remove(position);
MyViewPagerAdapter myViewPagerAdapter = new MyViewPagerAdapter(getSupportFragmentManager());
myViewPagerAdapter.notifyDataSetChanged();
myViewPagerAdapter.notifyDataSetChanged();
}
public void AddFragmentPage(Fragment frag,String title) {
MainActivity.this.addToFragments(frag);
MainActivity.this.addToFragmentsTitle(title);
MyViewPagerAdapter myViewPagerAdapter = new MyViewPagerAdapter(getSupportFragmentManager());
myViewPagerAdapter.notifyDataSetChanged();
}
public Fragment getItem(int position) {
return MainActivity.this.getFragmentsWithPosition(position);
}
public CharSequence getPageTitle(int position) {
return MainActivity.this.getFragmentsTitleWithPosition(position);
}
public int getCount() {
return fragments.size();
}
}
public void remove_tab_details(int tab_to_delete) {
// TabLayout.Tab tab = tabLayout.getTabAt(2);
// tab.select();
tabLayout.removeTabAt(tab_to_delete);
MyViewPagerAdapter Adapter = new MyViewPagerAdapter(getSupportFragmentManager());
Adapter.removeTabPage(tab_to_delete);
Adapter.notifyDataSetChanged();
}
}
and the code for UserDetailFragment ( which creates when the user click on one of the items in a listview fragment .
public class UserDetailFragment extends Fragment {
View view;
DetailCallBacks detailCallBacks;
public UserDetailFragment() {}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.user_detail_fragment,null);
return view;
}
#Override
public void onResume() {
super.onResume();
final Bundle bundle = getArguments();
String name = (String) bundle.get("name");
String family = (String)bundle.get("family");
String email = (String)bundle.get("email");
TextView nameFamilytv = view.findViewById(R.id.user_detail_name_and_family);
String nameAndfamily = name + " " + family;
nameFamilytv.setText(nameAndfamily);
TextView emailtv = view.findViewById(R.id.user_detail_email);
emailtv.setText(email);
Button closebtn = view.findViewById(R.id.detail_close_button);
closebtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
detailCallBacks.changeTabItem(true);
}
});
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
detailCallBacks = (DetailCallBacks)context;
}
public interface DetailCallBacks {
public void changeTabItem(boolean mustdelete);
}
and last but not least : the code for that list item that creates dynamic tabs when user clicks on its items :
public class ContactsFragment extends ListFragment {
CallBacks callBacks;
View view;
public static int came_fromTabItem;
public static boolean do_delete;
public ContactsFragment() { }
ArrayList<UserObject> userObjects;
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
userObjects = intent.getParcelableArrayListExtra(Intent_Service.SERVICE_PAYLOAD);
ArrayAdapter<UserObject> userObjectArrayAdapter = new UserArrayAdapter(context,0,userObjects);
setListAdapter(userObjectArrayAdapter);
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(getActivity(), Intent_Service.class);
getActivity().startService(intent);
LocalBroadcastManager.getInstance(getActivity().getApplicationContext()).
registerReceiver(broadcastReceiver,new IntentFilter(Intent_Service.SERVICE_MESSAGE));
}
public void setTextView(int position,Boolean mustDelete) {
came_fromTabItem = position;
do_delete = mustDelete;
}
#Override
public void onResume() {
super.onResume();
if(came_fromTabItem > 2 && do_delete) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
callBacks.delete_previous_tab(came_fromTabItem);
do_delete = false;
Toast.makeText(getActivity().getApplicationContext(),String.valueOf(came_fromTabItem),Toast.LENGTH_LONG).show();
}
}, 2000);
}
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
this.view = inflater.inflate(R.layout.fragment_contacts,null);
return this.view;
}
public void onListItemClick(ListView l, View v, int position, long id) {
UserObject userObject = userObjects.get(position);
callBacks.create_user_detail_tab(userObject);
}
public interface CallBacks {
public void create_user_detail_tab(UserObject userObject);
public void delete_previous_tab(int positions);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
this.callBacks = (CallBacks)context;
}
}
So... can anyone help me please? the problem is simple, why the error The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged! Expected adapter item count: 4, found: 3 appears when a tabitem is removed, how can I solve it?
the problem occur because you delete a tab item and your pager still has 4 in item count , you have to make pager item count dynamic , and call notifyDataSetChanged() after you remove an item
here is an example
public class MyPagerAdapter extends FragmentPagerAdapter {
int nbrItem;
public ProfilPagerAdapter(FragmentManager fm,String token,Int nbrItem) {
super(fm);
this.nbrItem= nbrItem;
}
#Override
public Fragment getItem(int position) {
switch(position) {
case 0 :
new SignInFragment();
case 1 :
new ContactsFragment()
case 2 :
new RegisterFragment()
}
return null;
}
#Override
public int getCount() {
return nbrItem;
}
public void setNbrItem(int nbrItem) {
this.nbrItem= nbrItem;
}
now when you remove an item you will pass to new number of item to your adapater
getAdapter().setNbrItem(2);
getAdapter().notifyDataSetChanged();
I solved my Problem, But with a hack, you see the error was because: TabItems are counting and indexing from 0, but as my pages are dynamically creating, I set the
getCount() method of my FragmentPagerAdapter to return the ArrayList<Fragment> fragments size , with fragments.size() , on the other hand, the size of an ArrayList doesn't count 0. so for 3 elements, instead of 0 1 2, or number 2, it returns to number 3.
so back to business, I was compelled to add null to my ArrayList and one null to my ArrayList titles, so this way when I removed my last TabItem, program doesn't crash anymore, and to be more convenient, when a user closes all Tabs , everytime user opens ( adds ) a new tab, I call fragments.removeAll(Collections.singleton(null)); to clear every null element i have inserted, for the TabTitles Too .
anyway cheers you guys, I'm sure this would be a good tutorial for those who want to create such applications because I've included all of my codes. please give a thumbs up. thanks.
I want to create soemthing like this:
The ViewPager is attached to an adapter which has 2 fragments. One to list the upcoming orders and the other to list the past orders.
So the image shown above has a main fragment which hosts the ViewPager, and the adapter creates 2 fragments as children of the main fragment.
For sake of simplicity I'll call the main fragment as "Parent fragment" and the two fragments supplied by the adapter as "children fragments".
Once the parent fragment is created or resumed it has to fetch a list of data from the server and pass it to the two children fragment(The children fragments will process the list of data and display the necessary data). Both the children fragments have a listView, and each listView row item is clickable.
Now, the data fetched by the parent fragment has to be passed to the children fragments before the ViewPager is attached to the adapter. So I do not attach the adapter to the ViewPager in the onCreate method of the parent fragment, but rather once the list of data is fetched, I attach the adapter to the ViewPager in the onPostExecute method of the async task after the data is fetched.
This works fine the first time, but once I click on a listView row item of the child fragment and then press the back button, the getItem() method of the adapter is not called, but the onResume methods of the children fragments are called even before the data is fetched from the server.
So I guess the android framework remembers that the children fragment have already been created, and does not recreate them again.
How do I ensure that the children fragments are created/called only after the data is fetched from the server in the parent fragment?
I am adding some code for clarity.
BookingHistory.java(Parent Fragment)
public class BookingHistory extends android.support.v4.app.Fragment {
ViewPager mPager;
SlidingTabLayout mTabs;
Toolbar toolBar;
View view;
private ProgressDialog progress;
private OrderTask mOrderTask = null;
UserFunctions userFunctions = null;
OrderFunctions orderFunctions = null;
private BookingHistoryListener mListener;
private List<Order> mOrderList;
PlacedOrders upcomingOrders;
PlacedOrders pastOrders;
public BookingHistory() {
// Required empty public constructor
}
#Override
public void onResume() {
super.onResume();
mOrderList = null;
mPager = null;
mTabs = null;
upcomingOrders = null;
pastOrders = null;
progress = new ProgressDialog(getActivity());
fetchOrders();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_booking_history, container, false);
toolBar = (Toolbar) view.findViewById(R.id.toolbar_s);
if (toolBar != null) {
TextView tv = (TextView) view.findViewById(R.id.toolbar_title);
toolBar.setTitle("");
tv.setText("History");
}
return view;
}
class MyPagerAdapter extends FragmentPagerAdapter {
String tabs[] = {"Upcoming", "Past"};
public MyPagerAdapter(android.support.v4.app.FragmentManager fm) {
super(fm);
}
#Override
public android.support.v4.app.Fragment getItem(int position) {
if (position == 0) {
upcomingOrders = PlacedOrders.newInstance(Constants.DATE_TODAY_FUTURE);
upcomingOrders.getOrderList(mOrderList);
return upcomingOrders;
} else {
pastOrders = PlacedOrders.newInstance(Constants.DATE_PAST);
pastOrders.getOrderList(mOrderList);
return pastOrders;
}
}
#Override
public CharSequence getPageTitle(int position) {
return tabs[position];
}
#Override
public int getCount() {
return 2;
}
}
public void fetchOrders() {
if (mOrderTask != null) {
return;
}
progress.show();
mOrderTask = new OrderTask(getActivity());
mOrderTask.execute((Void) null);
}
public class OrderTask extends AsyncTask<Void, Void, Boolean> {
private final Activity mActivity;
OrderTask(Activity activity) {
mActivity = activity;
}
#Override
protected Boolean doInBackground(Void... params) {
userFunctions = new UserFunctions();
orderFunctions = new OrderFunctions();
return orderFunctions.getList(userFunctions.getToken(mActivity));
}
#Override
protected void onPostExecute(final Boolean success) {
mOrderTask = null;
progress.dismiss();
if (success) {
mOrderList = UserProfile.getOrders();
//attaching the view pager to adapter here!
mPager = (ViewPager) view.findViewById(R.id.pager);
mTabs = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);
mTabs.setDistributeEvenly(true);
mTabs.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
#Override
public int getIndicatorColor(int position) {
return getResources().getColor(R.color.white);
}
});
mPager.setAdapter(new MyPagerAdapter(getChildFragmentManager()));
mTabs.setViewPager(mPager);
} else {
//Error handling stuff
}
}
}
}
PlacedOrders.java(Children Fragments)
public class PlacedOrders extends android.support.v4.app.Fragment {
private static String flag;
private int dateFlag;
private PlacedOrdersListener mListener;
UserFunctions userFunctions = null;
OrderFunctions orderFunctions = null;
private PlacedOrdersAdapter ordersAdapter;
private ProgressDialog progress;
private List<Order> mOrderList;
private List<Order> mPendingOrderList;
private List<Order> mCompletedOrderList;
public static PlacedOrders newInstance(int date) {
PlacedOrders fragment = new PlacedOrders();
Bundle args = new Bundle();
args.putInt(flag, date);
fragment.setArguments(args);
return fragment;
}
public void getOrderList(List<Order> orderList) {
this.mOrderList = orderList;
}
public PlacedOrders() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
dateFlag = getArguments().getInt(flag);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mPendingOrderList = new ArrayList<Order>();
mCompletedOrderList = new ArrayList<Order>();
return inflater.inflate(R.layout.fragment_placed_orders, container, false);
}
#Override
public void onResume() {
super.onResume();
displayOrders();
}
private void displayOrders() {
if (isVisible() && (mOrderList != null)) {
mPendingOrderList.clear();
mCompletedOrderList.clear();
ListView listViewOrder = (ListView) getView().findViewById(R.id.orderList);
if(dateFlag == Constants.DATE_TODAY_FUTURE) {
for(int i = 0; i < mOrderList.size(); i++) {
String status = mOrderList.get(i).status;
if(status.equals("PENDING") || status.equals("PROCESSING")) {
mPendingOrderList.add(mOrderList.get(i));
ordersAdapter = new PlacedOrdersAdapter(mPendingOrderList, getActivity().getLayoutInflater());
listViewOrder.setAdapter(ordersAdapter);
}
}
}
else if(dateFlag == Constants.DATE_PAST) {
for(int i = 0; i < mOrderList.size(); i++) {
String status = mOrderList.get(i).status;
if(status.equals("COMPLETE")) {
mCompletedOrderList.add(mOrderList.get(i));
ordersAdapter = new PlacedOrdersAdapter(mCompletedOrderList, getActivity().getLayoutInflater());
listViewOrder.setAdapter(ordersAdapter);
}
}
}
listViewOrder.setOnItemClickListener(new AdapterView.OnItemClickListener() {
//Display a new fragment on clicking
});
}
}
}
I had same issue and this was my solution :
In container fragment (fragment that contains tabs) in onViewCreated() method I created startFetch() method:
#Override
public void onViewCreated(View v, Bundle savedInstanceState) {
super.onViewCreated(v, savedInstanceState);
mViewPager = (ViewPager) v.findViewById(R.id.home_tab_pager);
mTabsHost= (TabLayout) getActivity().findViewById(R.id.appTabs);
startFetch();
}
Then in startFetch method i use Volley request and in onResponse method i update data and then add tabs :
public void startFetch(){
//Create volley request
String url = BuildConfig.API_GET_CATEGORIES;
final RequestQueue queue = VolleyService.getInstance(this.getContext()).getRequestQueue();
StringRequest request = new StringRequest(url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
// we got the response, now our job is to handle it
try {
updateCategoryData(response);
} catch (RemoteException | OperationApplicationException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
//something happened, treat the error.
Log.e("ErrorFETCH", error.networkResponse.toString());
}
});
queue.add(request);
}
My udpateCategory() method :
public void updateCategoryData(final String stream) throws RemoteException, OperationApplicationException {
//Update the data to SQLITE
setupTabs();
}
My setupTabs() method :
public void setUpTabs(){
ArrayList<Category> categories = new ArrayList<>();
Cursor data = getActivity().getContentResolver().query(
Category.Entry.CONTENT_URI, // URI
Category.PROJECTION, // Projection
Category.Entry.COLUMN_NAME_PARENT_ID + " = ?", // Selection
new String[]{"0"}, // Selection args
null);
if (data != null) {
while(data.moveToNext()){
categories.add(new Category(data));
}
}
TabsPagerAdapter mAdapter = new TabsPagerAdapter(getActivity().getSupportFragmentManager(), this.getActivity(), categories);
mViewPager.setAdapter(mAdapter);
mTabsHost.setupWithViewPager(mViewPager);
}
I'm having an Activity that hosts two Fragments. One which contains an EditText and one that shows the input in a GridView. Fragment1 implements an interface to notify when the user wants to save the input. In the Activity I want to pass the data to the ArrayAdapter of Fragment2 but here I get the NullPointerException.
Activity:
public class SwipeTest extends FragmentActivity implements TestFragment1
.OnFragmentInteractionListener {
private final String[] tabs = {"Test 1", "Test 2"};
private ViewPager pager;
private ActionBar actionBar;
private TabsPagerAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.pager = new ViewPager(this);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup
.LayoutParams.MATCH_PARENT,
ViewGroup
.LayoutParams.MATCH_PARENT);
this.pager.setLayoutParams(params);
setContentView(this.pager);
this.actionBar = getActionBar();
this.adapter = new TabsPagerAdapter(
getSupportFragmentManager());
this.pager.setAdapter(this.adapter);
this.actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
setUpViewPager();
setUpActionBar();
}
/**
* Sets up the ViewPager and adds an OnPageChangeListener
*/
private void setUpViewPager() {
this.pager.setAdapter(this.adapter);
// pager needs an id; crashes if it has none
this.pager.setId(123456789);
// Set up the listener
ViewPager.OnPageChangeListener onPageChangeListener = new ViewPager
.OnPageChangeListener() {
#Override
public void onPageScrolled(int i, float v, int i2) {
}
#Override
public void onPageSelected(int i) {
SwipeTest.this.actionBar.setSelectedNavigationItem(i);
}
#Override
public void onPageScrollStateChanged(int i) {
}
};
this.pager.setOnPageChangeListener(onPageChangeListener);
}
/**
* Sets up the ActionBar with it's tabs and adds an ActionBar.TabListener to
* them
*/
private void setUpActionBar() {
this.actionBar = getActionBar();
this.actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Set up listener
ActionBar.TabListener tabListener = new ActionBar.TabListener() {
#Override
public void onTabSelected(ActionBar.Tab tab,
FragmentTransaction
fragmentTransaction) {
SwipeTest.this.pager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(ActionBar.Tab tab,
FragmentTransaction
fragmentTransaction) {
}
#Override
public void onTabReselected(ActionBar.Tab tab,
FragmentTransaction
fragmentTransaction) {
}
};
for (String tab_name : this.tabs) {
this.actionBar.addTab(
this.actionBar.newTab()
.setText(tab_name)
.setTabListener(tabListener));
}
}
/**
* Interface method of Fragment1
*/
#Override
public void onFragmentInteraction(String s) {
// This gets the fragment correct
TestFragment2 fragment = (TestFragment2) ((TabsPagerAdapter) pager
.getAdapter()).getItem(1);
// This assigns null to adapter
ArrayAdapter<String> adapter = (ArrayAdapter<String>) fragment
.getAdapter();
adapter.add(s);
adapter.notifyDataSetChanged();
}
public class TabsPagerAdapter extends FragmentPagerAdapter {
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
return new TestFragment1();
case 1:
return new TestFragment2();
}
return null;
}
#Override
public int getCount() {
return 2;
}
}
}
Fragment1:
public class TestFragment1 extends Fragment {
private OnFragmentInteractionListener mListener;
private EditText edit;
public TestFragment1() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
LinearLayout layout = new LinearLayout(getActivity());
layout.setLayoutParams(params);
this.edit = new EditText(getActivity());
Button btn = new Button(getActivity());
btn.setText("Save");
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onButtonPressed();
}
});
layout.addView(this.edit);
layout.addView(btn);
return layout;
}
public void onButtonPressed() {
if (this.mListener != null) {
String input = this.edit.getText().toString();
this.mListener.onFragmentInteraction(input);
}
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
this.mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement " +
"OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
this.mListener = null;
}
public interface OnFragmentInteractionListener {
public void onFragmentInteraction(String s);
}
}
Fragment2:
public class TestFragment2 extends Fragment {
private final List<String> data = new ArrayList<String>();
private ArrayAdapter<String> adapter;
public TestFragment2() {
// Required empty public constructor
}
public ArrayAdapter getAdapter() {
return this.adapter;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
GridView view = new GridView(getActivity());
GridView.LayoutParams params = new AbsListView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
view.setLayoutParams(params);
this.data.add("Hello");
this.data.add("World");
this.adapter = new ArrayAdapter(getActivity(),
android.R.layout
.simple_list_item_1,
this.data);
view.setAdapter(this.adapter);
return view;
}
}
I don't know why ArrayAdapter<String> adapter = (ArrayAdapter<String>) fragment.getAdapter(); is null. So, how do I get access to the fields of Fragment2?
It seems problem about lifecycle. As you may know, onCreateView() is async method, so fragment instance could be accessed before setAdapter().
My suggestion is to create TestFragment2 instance in SwipeTest#onCreate(). Also, your code creates new instance every time button pressed. It seems not good idea.
The problem was within the PagerAdapter. I used getItem() to recieve the Fragment which is bound to the tab's position. The mistake was that I returned a new instance of that Fragment instead of returning the existing Fragment. Since that new Fragment has never been shown the Fragment's onCreateView() and hence setUpAdapter() has never been called. That's why I recieved NPE in
ArrayAdapter<String> adapter = (ArrayAdapter<String>) fragment
.getAdapter();
adapter.add(s);
Approach[*]:
public class TabsPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragments = new ArrayList<Fragment>();
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
mFragments.add(0, new TestFragment1());
mFragments.add(1, new TestFragment2());
}
#Override
public Fragment getItem(int index) {
if (index > (mFragments.size() - 1) )
return null;
return mFragments.get(index);
}
#Override
public int getCount() {
return mFragments.size();
}
}
[*] Untested since I changed my code and don't use the PagerAdapter anymore but I think the solution is comprehensible.
If the pager has never shown the second fragment, when your fragment1 interface callback is called the pager returns null:
// This gets the fragment correct
TestFragment2 fragment = (TestFragment2) ((TabsPagerAdapter) pager
.getAdapter()).getItem(1);
So your subsequent call to getAdapter() will result in the NPE.
I am trying to create a very simple Android application that uses a FragmentPagerAdapter to swipe between three fragments. Each of the fragments contains a single EditText and has the exact same code.
The desired behavior is that when the user updates the EditText, the new value is saved to the application instance. Then, once a new fragment is selected, that new fragment should show the saved value. For some reason this is not working.
I also want the focused fragment to show the saved data when the application resumes (comes back from background). This too does not work.
I am really confused as to why something as simple as this is so difficult!
Here is my code so far:
StackOverflowDemoApplication.java:
public class StackOverflowDemoApplication extends Application {
private ApplicationData applicationData;
// the index of the last fragment that was displayed
private int lastItem = 0;
#Override
public void onCreate() {
applicationData = new ApplicationData();
}
public ApplicationData getApplicationData() {
return applicationData;
}
public int getLastItem() {
return lastItem;
}
public void setLastItem(int lastItem) {
this.lastItem = lastItem;
}
}
MainActivity.java
public class MainActivity extends ActionBarActivity implements ActionBar.TabListener {
private static final String TAG = "MainActivity";
// the application instance
private StackOverflowDemoApplication application;
// the pager adapter
private SectionsPagerAdapter pagerAdapter;
// the view pager
private ViewPager viewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Save the application instance.
application = (StackOverflowDemoApplication) getApplication();
// Set up the action bar.
final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
pagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(pagerAdapter);
// For each of the sections in the app, add a tab to the action bar.
for (int i = 0; i < pagerAdapter.getCount(); i++) {
actionBar.addTab(
actionBar.newTab()
.setText(pagerAdapter.getPageTitle(i))
.setTabListener(this));
}
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
viewPager.setCurrentItem(tab.getPosition());
pagerAdapter.notifyDataSetChanged();
}
#Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
#Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
private int NUM_ITEMS = 3;
public SectionsPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
#Override
public Fragment getItem(int position) {
// get the application data instance
ApplicationData data = application.getApplicationData();
switch (position) {
case 0:
return SecondFragment.newInstance(data);
case 1:
return FirstFragment.newInstance(data);
case 2:
return ThirdFragment.newInstance(data);
default:
return null;
}
}
#Override
public int getCount() {
return NUM_ITEMS;
}
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "FIRST FRAGMENT";
case 1:
return "SECOND FRAGMENT";
case 2:
return "THIRD FRAGMENT";
default:
return null;
}
}
}
#Override
public void onResume() {
// load the previous fragment
viewPager.setCurrentItem(application.getLastItem());
super.onResume();
}
#Override
public void onPause() {
// save the last fragment we used
application.setLastItem(viewPager.getCurrentItem());
super.onPause();
}
}
FirstFragment.java
public class FirstFragment extends Fragment {
private static final String TAG = "FirstFragment";
// the activity reference
private Activity activity;
// the application data
private ApplicationData data;
// the edit text
private EditText editText;
// are we currently loading data for this fragment?
private boolean loadingData = false;
public FirstFragment(ApplicationData data) {
super();
this.data = data;
}
public static FirstFragment newInstance(ApplicationData data) {
Log.e(TAG, "New instance called");
FirstFragment fragment = new FirstFragment(data);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.e(TAG, "Creating FirstFragment view");
// inflate the view
View view = inflater.inflate(R.layout.first_fragment_layout, container, false);
// get the activity instance
activity = getActivity();
// the textview
editText = (EditText) view.findViewById(R.id.textView);
editText.addTextChangedListener(textWatcher);
// update the ui from the data
updateUIFromData();
return view;
}
public void updateUIFromData() {
// we have started loading the data
loadingData = true;
// if there is data
if (null != data) {
// set the value
if (null != data.getStringValue()) {
editText.setText(data.getStringValue());
}
}
// done loading the data
loadingData = false;
}
private void updateDataFromUi() {
data.setStringValue(editText.getText().toString());
}
private TextWatcher textWatcher = new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// if we are not loading data
if (!loadingData) {
// update the data from the ui
updateDataFromUi();
}
}
#Override
public void afterTextChanged(Editable s) {
}
};
}
Try to update view when fragment get visible to user as follows:
public class MyFragment extends Fragment
#Override
public void setMenuVisibility(final boolean visible) {
super.setMenuVisibility(visible);
if (visible) {
**//Get new data and update views here**
}
}
Check this tutorial, it might help you:
http://www.androidhive.info/2013/10/android-tab-layout-with-swipeable-views-1/
You don't have to call notifyDataSetChanged() inside tabSelected callback.
You should update your fragment datas in onResumer() like TextView or EditText ... etc. See example:
Oncreate....
TextView mtextview = rootview.findViewById....
#Override
public void onResume() {
if(do some thing){
mtextview.setText("It did something");
}
super.onResume();
}
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.