I have a TabLayout and inside that I have ViewPager. I need to dynamically add and remove tab in tablayout(material design). I can able to add the tabs dynamically but while removing the tab, tab is removing correctly. But viewpager last item is deleted. So tab is not showing specific fragment. FYI i have used FragmentPagerAdapter.
I have followed this tutorial to implement this
https://androidbelieve.com/navigation-drawer-with-swipe-tabs-using-design-support-library
public class TabFragment extends Fragment {
public static TabLayout tabLayout;
public static ViewPager viewPager;
public static int int_items = 2;
private MyNewsFragment mMyNewsFragment;
private ArrayList<TabFragmentModelClass> mFragmentArrayList;
private TabLayoutAdapter mTabLayoutAdapter;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mMyNewsFragment = new MyNewsFragment();
/**
*Inflate tab_layout and setup Views.i;;a
*/
View x = inflater.inflate(R.layout.tab_layout, null);
tabLayout = (TabLayout) x.findViewById(R.id.tabs);
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); // scorllable tab
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL); // tab name fill parent
viewPager = (ViewPager) x.findViewById(R.id.viewpager);
mFragmentArrayList = new ArrayList<>();
mFragmentArrayList.add(new TabFragmentModelClass(new MyNewsSelectionFragment(), "", ""));
mFragmentArrayList.add(new TabFragmentModelClass(mMyNewsFragment, "", getResources().getString(R.string.mynews)));
mFragmentArrayList.add(new TabFragmentModelClass(new BreakingNewsFragment(), "", getResources().getString(R.string.breakingnews)));
// Selected news from shared preference
ArrayList<MyNewsSharedPreferenceModelClass> tempSharedPreferenceModelClass = new MyNewsSharedPreferenceClass().loadFavorites(getActivity());
for (int i = 0; i < tempSharedPreferenceModelClass.size(); i++) {
mFragmentArrayList.add(new TabFragmentModelClass(new CategoreyNewsFragment(tempSharedPreferenceModelClass.get(i).getmCatID()), tempSharedPreferenceModelClass.get(i).getmCatID(),
tempSharedPreferenceModelClass.get(i).getmCatName()));
}
}
/**
*Set an Apater for the View Pager
*/
mTabLayoutAdapter = new TabLayoutAdapter(getChildFragmentManager(), mFragmentArrayList);
viewPager.setAdapter(mTabLayoutAdapter);
viewPager.setOffscreenPageLimit(mFragmentArrayList.size());
tabLayout.setupWithViewPager(viewPager);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
tabLayout.getTabAt(position);
}
#Override
public void onPageScrollStateChanged(int state) {
if (state == 1) {
updateMyNewsFragment();
}
}
});
/**
* Now , this is a workaround ,
* The setupWithViewPager dose't works without the runnable .
* Maybe a Support Library Bug .
*/
tabLayout.post(new Runnable() {
#Override
public void run() {
tabLayout.getTabAt(0).setIcon(R.mipmap.mynewselection);
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
switch (tab.getPosition()) {
case 1:
System.out.println("000000000000000000000 ");
updateMyNewsFragment();
break;
}
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
});
return x;
}
/**
* update the mynews selection
* this method trigger from the MainNewsActivity
*/
public void updateMyNewsFragment() {
if (ApplicationController.getInstance().getBoolPref(CommonVariableInterFace.isNewsSelected)) {
mMyNewsFragment.sendMyNewsRequest();
ApplicationController.getInstance().setBoolPref(CommonVariableInterFace.isNewsSelected, false);
}
}
/**
* update the tab values
* this method trigger from the MainNewsActivity
* if value is zero need to add in tab
* if value is one need to remove in tab
*/
public void updateTabvalue(String catId, String catName, int value) {
if (value == 0) { // add the value in tab
mFragmentArrayList.add(new TabFragmentModelClass(new CategoreyNewsFragment(catId), catId,
catName));
} else { // Removing the tab
for (int i = 0; i < mFragmentArrayList.size(); i++) {
if (mFragmentArrayList.get(i).getmCatID().equalsIgnoreCase(catId)) {
Log.i("-----same id ", catId);
mFragmentArrayList.remove(i);
mTabLayoutAdapter = new TabLayoutAdapter(getChildFragmentManager(), mFragmentArrayList);
viewPager.setAdapter(mTabLayoutAdapter);
}
}
}
mTabLayoutAdapter.notifyDataSetChanged();
viewPager.setOffscreenPageLimit(mFragmentArrayList.size());
tabLayout.getTabAt(0).setIcon(R.mipmap.mynewselection);
tabLayout.setupWithViewPager(viewPager);
tabLayout.post(new Runnable() {
#Override
public void run() {
tabLayout.getTabAt(0).setIcon(R.mipmap.mynewselection);
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
switch (tab.getPosition()) {
case 1:
updateMyNewsFragment();
break;
}
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
});
}
public void openSpecificTab() {
// tabLayout.settab
// tabLayout.se
}
}
Adapter code:
public class TabLayoutAdapter extends FragmentPagerAdapter {
private long baseId = 0;
ArrayList<TabFragmentModelClass> fragmentAdapterArrayList;
public TabLayoutAdapter(FragmentManager fm, ArrayList<TabFragmentModelClass> fragmentArrayList) {
super(fm);
fragmentAdapterArrayList = fragmentArrayList;
}
#Override
public Fragment getItem(int position) {
if (position == 0)
return fragmentAdapterArrayList.get(position).getmFragment();
if (position == 1)
return fragmentAdapterArrayList.get(position).getmFragment();
if (position == 2)
return fragmentAdapterArrayList.get(position).getmFragment();
return new CategoreyNewsFragment().newInstance(fragmentAdapterArrayList.get(position).getmCatID());
}
#Override
public int getCount() {
return fragmentAdapterArrayList.size();
}
//this is called when notifyDataSetChanged() is called
#Override
public int getItemPosition(Object object) {
// refresh all fragments when data set changed
return TabLayoutAdapter.POSITION_NONE;
}
#Override
public long getItemId(int position) {
// give an ID different from position when position has been changed
return baseId + position;
}
/**
* Notify that the position of a fragment has been changed.
* Create a new ID for each position to force recreation of the fragment
*
* #param n number of items which have been changed
*/
public void notifyChangeInPosition(int n) {
// shift the ID returned by getItemId outside the range of all previous fragments
baseId += getCount() + n;
}
/**
* // * This method returns the title of the tab according to the position.
* //
*/
#Override
public CharSequence getPageTitle(int position) {
return fragmentAdapterArrayList.get(position).getmCatName();
}
}
Remove tab from TabLayout
...
public void removeTab(int position) {
if (mTabLayout.getTabCount() >= 1 && position<mTabLayout.getTabCount()) {
mTabLayout.removeTabAt(position);
mPagerAdapter.removeTabPage(position);
}
}
...
Add a removeTabPage method to your PagerAdapter
...
public void removeTabPage(int position) {
if (!tabItems.isEmpty() && position<tabItems.size()) {
tabItems.remove(position);
notifyDataSetChanged();
}
}
...
Add a Tab
...
private void addTab(String title) {
mTabLayout.addTab(mTabLayout.newTab().setText(title));
mPagerAdapter.addTabPage(title);
}
...
Add a addTabPage method to your PagerAdapter
...
public void addTabPage(String title) {
tabItems.add(title);
notifyDataSetChanged();
}
...
Check out this sample code for a full example: ...samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutUsage.java
With the new support library (I'm using 23.2.1) you should only add to the Viewpager and not the TabLayout (otherwise you end up with double tabs and other funky behavior). So to update TouchBoarder's answer:
Add a removeTabPage method to your PagerAdapter
public void removeTabPage(int position) {
if (!tabItems.isEmpty() && position<tabItems.size()) {
tabItems.remove(position);
notifyDataSetChanged();
}
}
Add a addTabPage method to your PagerAdapter
public void addTabPage(String title) {
tabItems.add(title);
notifyDataSetChanged();
}
In addition to existing answers, to remove all tabs from tabLayout.
tabLayout.removeAllTabs();
You can add dynamic tabs like this :
public void addTabs(){
for(YourClassModel tabName: tabsName){
Log.i("ActivityClass","tabName"+tabName);
tabLayout.addTab(tabLayout.newTab().setText(tabName.getName()));
}
Removetab from tablayout
TabActivity and ViewPager code :
`public class ScrollableTabsActivity extends AppCompatActivity {
int NumberOfTab = 5;
private Toolbar toolbar;
private TabLayout tabLayout;
private ViewPager viewPager;
ViewPagerAdapter adapter ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scrollable_tabs);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
adapter = new ViewPagerAdapter(getSupportFragmentManager());
viewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager(viewPager);
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
int length = tabLayout.getTabCount();
for (int i = 0; i < length; i++) {
tabLayout.getTabAt(i).setCustomView(getTabView(i));
}
}
public View getTabView(final int position) {
View view = LayoutInflater.from(this).inflate(R.layout.close_tablayout, null);
TextView title = (TextView) view.findViewById(R.id.title);
ImageView icon = (ImageView) view.findViewById(R.id.close);
title.setText(adapter.getPageTitle(position));
icon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
NumberOfTab = NumberOfTab - 1;
setupViewPager(viewPager);
tabLayout.setupWithViewPager(viewPager);
if (tabLayout.getTabCount()==3)
tabLayout.setTabMode(TabLayout.MODE_FIXED);
int length = tabLayout.getTabCount();
for (int i = 0; i < length; i++) {
tabLayout.getTabAt(i).setCustomView(getTabView(i));
}
}
});
return view;
}
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
}
class ViewPagerAdapter extends FragmentPagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
final Fragment result;
switch (position) {
case 0:
// First Fragment of First Tab
result = new OneFragment();
break;
case 1:
// First Fragment of Second Tab
result = new TwoFragment();
break;
case 2:
// First Fragment of Second Tab
result = new ThreeFragment();
break;
case 3:
// First Fragment of Second Tab
result = new FourFragment();
break;
case 4:
// First Fragment of Second Tab
result = new FiveFragment();
break;
default:
result = null;
break;
}
// Log.d("RESULT",result.getTag());
return result;
}
#Override
public int getCount() {
return NumberOfTab;
}
#Override
public CharSequence getPageTitle(final int position) {
switch (position) {
case 0:
return "One";
case 1:
return "Two";
case 2:
return "Three";
case 3:
return "Four";
case 4:
return "Five";
default:
return null;
}
}
}
}`
Check out this Links :
https://stackoverflow.com/a/58480151/9223264
I am new in stackoverflow i try my best if you think my answer is useful then please appreciate
Related
I am trying to use custom tabs for my ViewPager2 via the TabLayout.Tab.setCustomView method. But it doesn't work.
Here is a code snippet from my Activity for preparing the ViewPager2 with a TabLayout:
#Override
protected void onCreate(Bundle savedInstanceState)
{
...
mMyViewPager2 = findViewById(R.id.view_pager);
mMyViewPager2.setAdapter(new MyPagerAdapter(this));
mMyTabLayout = findViewById(R.id.media_tab);
new TabLayoutMediator(mMyTabLayout, mMyViewPager2, myTabHandler).attach();
}
private class MyPagerAdapter extends FragmentStateAdapter
{
...
}
And here is the code snippet for my TabLayoutMediator.TabConfigurationStrategy (also inside my Activity):
private TabLayoutMediator.TabConfigurationStrategy myTabHandler = new TabLayoutMediator.TabConfigurationStrategy()
{
private TextView tabTitle;
private TextView tabSubtitle;
#Override
public void onConfigureTab(#NonNull TabLayout.Tab tab, int position)
{
View tabView = LayoutInflater.from(MyActivity.this).inflate(R.layout.tab_layout, null, false);
tabTitle = tabView.findViewById(R.id.tab_title);
tabSubtitle = tabView.findViewById(R.id.tab_subtitle);
tabTitle.setText("Title" + (position + 1));
tabSubtitle.setText("Subtitle" + (position + 1));
tab.setCustomView(tabView);
}
}
But with the above setup, the tabs remain blank.
I've also tried the following approach in my TabConfigurationStrategy (from this post); but it doesn't work, either:
FrameLayout frameLayout = new FrameLayout(MyActivity.this);
frameLayout.addView(tabView);
I'd like to know what is wrong with my code, and how can I correct it to show custom tab layouts with ViewPager2.
From setCustomView() documentation:
If the provided view contains a TextView with an ID of text1 then that
will be updated with the value given to setText(CharSequence)
So try changing the tabTitle's Id to "#android:id/text1".
Now for the subtitle, I am not certain of this. Using"#android:id/text2" might be worth a try. Note: "#android:id/text2" exists.
Nevertheless, you can apply your own layout declaratively to the TabItems using android:layout="", thus negating the need for you to do it programmatically. There is an example of the declarative version in the TabLayout documentation. So I believe something like this would work:
<TabLayout
...>
<TabItem
....
android:layout="#layout/linearLayout_tabitem_1"/>
<TabItem
....
android:layout="#layout/linearLayout_tabitem_2"/>
</TabLayout>
You can define the the title of tabs in your pagerAdapter itself.
#Override
protected void onCreate(Bundle savedInstanceState)
{
...
MyPagerAdapter adapter = new MyPagerAdapter(this, getSupportFragmentManager() , 2);
mMyTabLayout = findViewById(R.id.media_tab);
mMyViewPager2 = findViewById(R.id.view_pager);
pager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mMyTabLayout));
mMyViewPager2.setAdapter(adapter);
mMyTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
mMyViewPager2.setCurrentItem(tab.getPosition());
setTabView(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
private void setTabView(int index) {
TabLayout.Tab tab = mMyTabLayout.getTabAt(index);
tab.setCustomView(R.layout.tab_layout);
((TextView) tab.getCustomView().findViewById(R.id.tabSubtitle)).setText("Subtitle" + (position + 1));
}
private class MyPagerAdapter extends FragmentStatePagerAdapter
{
private Context mContext;
private int mCount;
public MyPagerAdapter(Context context, FragmentManager fm , int count) {
super(fm);
mContext = context;
mCount = count;
notifyDataSetChanged();
#Override
public Fragment getItem(int position) {
if (position == 0)
return someFragment();
else if (position == 1)
return someOtherFragment();
}
#Override
public int getCount() {
return mCount;
}
#Override
public CharSequence getPageTitle(int position) {
// Generate title based on item position
switch (position) {
case 0:
return "Title" + position+1;
case 1:
return "Title" + position+1;
default:
return null;
}
}
}
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've created Activity to handle ViewPager. At the beginning it was working fine, but then I had to add some more code, to make it refresh my pages while selecting them. I've handled it by calling onResume() method with every page change.
I've optimized my Fragments, cleared unnecessary code (inside onResume()) in each of them, but this give no result. That's how my Activity looks like:
public class MainTabsActivity extends AppCompatActivity implements FullList.OnListFragmentInteractionListener, AcceptedList.OnListFragmentInteractionListener, StartedList.OnFragmentInteractionListener {
private static final int NUM_TABS = 3;
private SectionsPagerAdapter mSectionsPagerAdapter;
private ViewPager mViewPager;
TabLayout tabLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_tabs);
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.setOffscreenPageLimit(2);
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int i, float v, int i1) {
}
#Override
public void onPageSelected(int i) {
Fragment fragment = ((SectionsPagerAdapter)mViewPager.getAdapter()).getFragment(i);
if (fragment != null) {
Log.e("PAGE SELECTED", " " + i);
tabLayout.getTabAt(i).select();
fragment.onResume();
}
}
#Override
public void onPageScrollStateChanged(int i) {
}
});
Intent i = getIntent();
int pageNum = i.getIntExtra("pagenumber", 0);
mViewPager.setCurrentItem(pageNum);
tabLayout.getTabAt(pageNum).select();
mViewPager.invalidate();
}
#Override
protected void onResume() {
super.onResume();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main_tabs, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onListFragmentInteraction(DummyContent.DummyItem item) {
}
#Override
public void onFragmentInteraction(Uri uri) {
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
mFragmentTags = new HashMap<>();
mFragmentManager = fm;
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
switch(position) {
case 0:
return getString(R.string.tab_text_1);
case 1:
return getString(R.string.tab_text_2);
case 2:
return getString(R.string.tab_text_3);
}
return null;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
FullList tab1 = new FullList();
return tab1;
case 1:
AcceptedList tab2 = new AcceptedList();
return tab2;
case 2:
StartedList tab3 = new StartedList();
return tab3;
default:
return null;
}
}
#Override
public int getCount() {
return NUM_TABS;
}
#NonNull
#Override
public Object instantiateItem(#NonNull ViewGroup container, int position) {
Object obj = super.instantiateItem(container, position);
if (obj instanceof Fragment) {
Fragment f = (Fragment) obj;
String tag = f.getTag();
mFragmentTags.put(position, tag);
}
return obj;
}
public Fragment getFragment(int position) {
String tag = mFragmentTags.get(position);
if (tag == null) return null;
return mFragmentManager.findFragmentByTag(tag);
}
}
}
I've added code, to call onResume() method, but funny thing is that the Fragments are loading so long only while I open this Activity from another, but not while scrolling between pages (everything works smoothly then), before changes everything was fine.
Is there any way to make this work faster?
You don't need to write onPageSelected code to change your tab selection.
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.setOffscreenPageLimit(2);
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager); // this will synchronize our tablayout with your viewpager
You should not call onResume directly, this is a system event and a call to this void
is not recommended.
Also, in your case, no need to call mViewPager.invalidate()
With the help of Vivek Pratap Singh and astuetz codes from following questions(Remove Fragment Page from ViewPager in Android and Android saving dynamically added viewpager's fragment while coming back from another activity and reopening app),
In my previous question in stack overflow I used inbuild method onDestroy, as it looks like it will save all the dynamically added fragments in viewpager.
But it's not happening, I am able to see only one intial static page(i.e. fragment) when I restart my app or navigate to another activity and come back to my activity containing the viewpager with fragments.Could any one please help me on this question.
Here is my source code for your reference:
public class MainActivity extends FragmentActivity implements TextProvider {
private Button mAdd;
private Button mRemove;
private ViewPager mPager;
int position;
private MyPagerAdapter mAdapter;
private ArrayList<String> mEntries = new ArrayList<String>();
#Override
public void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
bundle.putInt("currenItem", mPager.getCurrentItem());
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPager = (ViewPager) findViewById(R.id.pager);
if (savedInstanceState != null) {
mPager.setCurrentItem(savedInstanceState.getInt("currentItem", 0));
}
mEntries.add("page 1");
mAdd = (Button) findViewById(R.id.add);
mRemove = (Button) findViewById(R.id.remove);
mAdd.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
addNewItem();
}
});
mRemove.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
removeCurrentItem();
}
});
mAdapter = new MyPagerAdapter(this.getSupportFragmentManager(), this);
mPager.setAdapter(mAdapter);
mPager.setOffscreenPageLimit(3);
}
private void addNewItem() {
// int position = mPager.getCurrentItem();
mEntries.add("page ");
mAdapter.notifyDataSetChanged();
}
private void removeCurrentItem() {
int position = mPager.getCurrentItem();
if(position != 0){
mEntries.remove(position);
}else{
Toast.makeText(getApplicationContext(), "Sorry", Toast.LENGTH_LONG).show();
}
mAdapter.notifyDataSetChanged();
}
#Override
public String getTextForPosition(int position) {
return mEntries.get(position);
}
#Override
public int getCount() {
return mEntries.size();
}
private class MyPagerAdapter extends FragmentPagerAdapter {
private TextProvider mProvider;
private long baseId = 0;
public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
super(fm);
this.mProvider = provider;
}
#Override
public Fragment getItem(int position) {
switch(position) {
case 0:
return FragmentOne.newInstance(mProvider.getTextForPosition(position));
case 1:
return FragmentTwo.newInstance(mProvider.getTextForPosition(position));
case 2:
return FragmentThree.newInstance(mProvider.getTextForPosition(position));
default: return FragmentOne.newInstance(mProvider.getTextForPosition(position));
}
}
#Override
public int getCount() {
return mProvider.getCount();
}
//this is called when notifyDataSetChanged() is called
#Override
public int getItemPosition(Object object) {
// refresh all fragments when data set changed
return PagerAdapter.POSITION_NONE;
}
#Override
public long getItemId(int position) {
// give an ID different from position when position has been changed
return baseId + position;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
}
/**
* Notify that the position of a fragment has been changed.
* Create a new ID for each position to force recreation of the fragment
* #param n number of items which have been changed
*/
public void notifyChangeInPosition(int n) {
// shift the ID returned by getItemId outside the range of all previous fragments
baseId += getCount() + n;
}
}
}
I have PengaduanHomeActivity.class with 3 tabs using same Fragment PengaduanFragment.class like here :
public class PengaduanHomeActivity extends AppCompatActivity implements AppBarLayout.OnOffsetChangedListener {
....
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.detail_profile_user);
appBarLayout = (AppBarLayout) findViewById(R.id.appBarLayout);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
actionbar = getSupportActionBar();
actionbar.setDisplayHomeAsUpEnabled(true);
actionbar.setHomeButtonEnabled(true);
tabLayout = (TabLayout) findViewById(R.id.tabs);
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setOffscreenPageLimit(7);
mPagerAdapter = new PagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mPagerAdapter);
tabLayout.setupWithViewPager(mViewPager);
appBarLayout.addOnOffsetChangedListener(this);
}
int index = 0;
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int i) {
index = i;
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (index == 0) {
PengaduanFragment.enableRefreshLayout(true);
} else {
PengaduanFragment.enableRefreshLayout(false);
}
break;
}
return super.dispatchTouchEvent(ev);
}
public class PagerAdapter extends FragmentPagerAdapter {
private final String[] TITLES =
{"News Task", "Task On Progress", "Completed Task"};
private Bundle bundle;
public PagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public CharSequence getPageTitle(int position) {
return TITLES[position];
}
#Override
public int getCount() {
return TITLES.length;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return PengaduanFragment.newInstance("0");
case 1:
return PengaduanFragment.newInstance("1");
case 2:
return PengaduanFragment.newInstance("2");
default:
return null;
}
}
}
in code above, i call method in PengaduanFragment
public static void enableRefreshLayout(boolean b) {
mSwipeRefreshLayout.setEnabled(b);
}
but method just called in last PengaduanFragment with position 2. Other PengaduanFragment with position 0 or 1, not called. So how to Called method enableRefreshLayout in all Fragment ? thaks
You should create an interface that declares the method you want to use:
public interface IMyInterface {
public void myMethod();
}
Then, each fragment you have should implement this method:
public class MyFragment extends Fragment implements IMyInterface {
//Fragment code
public void myMethod() {
//Method code
}
}
Finally, make your adapter have a collection of this interface and loop over it when invoked by the activity
public class MyAdapter extends FragmentPagerAdapter {
//Collection of fragments
Fragment[] fragments;
//Adapter code
public void invokeMe(){
//Loop through each fragment
if (fragment instanceof IMyInterface) {
fragment.myMethod();
}
}
}
You should then be able to call invokeMe and have the adapter delegate to the right fragments the right behaviour. Just be careful of if the fragment instance is still active as per your settings on your ViewPager's current offscreen limit (adjustable by calling setOffscreenPageLimit())