简体   繁体   中英

How to delete a tab with actionbar, viewpager, and multiple fragments?

I'm using code I found here .

public class ActionBarTabs extends SherlockFragmentActivity {
CustomViewPager mViewPager;
TabsAdapter mTabsAdapter;
TextView tabCenter;
TextView tabText;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);

    mViewPager = new CustomViewPager(this);
    mViewPager.setId(R.id.pager);

    setContentView(mViewPager);
    ActionBar bar = getSupportActionBar();
    bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);

    mTabsAdapter = new TabsAdapter(this, mViewPager);

    mTabsAdapter.addTab(bar.newTab().setText("Home"),
            ToolKitFragment.class, null);
    mTabsAdapter.addTab(bar.newTab().setText("FujiSan"),
            FujiFragment.class, null);
}

public static class TabsAdapter extends FragmentPagerAdapter implements
        ActionBar.TabListener, ViewPager.OnPageChangeListener {
    private final Context mContext;
    private final ActionBar mActionBar;
    private final ViewPager mViewPager;
    private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();

    static final class TabInfo {
        private final Class<?> clss;
        private final Bundle args;

        TabInfo(Class<?> _class, Bundle _args) {
            clss = _class;
            args = _args;
        }
    }

    public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
        super(activity.getSupportFragmentManager());
        mContext = activity;
        mActionBar = activity.getSupportActionBar();
        mViewPager = pager;
        mViewPager.setAdapter(this);
        mViewPager.setOnPageChangeListener(this);
    }

    public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
        TabInfo info = new TabInfo(clss, args);
        tab.setTag(info);
        tab.setTabListener(this);
        mTabs.add(info);
        mActionBar.addTab(tab);
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return mTabs.size();
    }

    @Override
    public Fragment getItem(int position) {
        TabInfo info = mTabs.get(position);
        return Fragment.instantiate(mContext, info.clss.getName(),
                info.args);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset,
            int positionOffsetPixels) {
    }

    @Override
    public void onPageSelected(int position) {
        mActionBar.setSelectedNavigationItem(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }

    @Override
    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        Object tag = tab.getTag();
        for (int i = 0; i < mTabs.size(); i++) {
            if (mTabs.get(i) == tag) {
                mViewPager.setCurrentItem(i);
            }
        }
    }

    @Override
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    }

    @Override
    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }
}
}

I'm trying to modify it so that I can delete tabs dynamically. I tried by simply creating this function:

    public void removeTab(ActionBar.Tab tab) {
        mTabs.remove(tab);
        mActionBar.removeTab(tab);
        notifyDataSetChanged();
    }

But I always get an indexoutofboundsexception. Does anyone know a good way to do this?

EDIT

By changing my method to:

    public void removeTab(ActionBar.Tab tab) {
        mTabs.remove(tab.getTag());
        mActionBar.removeTab(tab);
        notifyDataSetChanged();
    }

I was able to successfully remove tabs. However, when I delete a tab that IS NOT the one on the far right, the view (or fragment?) that was associated with the tab doesn't go away. Instead, it seems to become associated with the next highest tab. For instance, if I deleted tab 0, tab 0's view just moves to tab 1. If I delete the tab with the highest ID then the tab and view go away correctly but then COME BACK when I add a new tab.

It's as if there's a list of fragments somewhere that is being associated with the tabs and deleting the tab doesn't delete the fragment. Does anyone have any idea what's causing this?

EDIT2

I've tried to delete the fragments with:

            Fragment fragmentToRemove = getItem(tab.getPosition());
            destroyItem(mViewPager, tab.getPosition(), fragmentToRemove);

and also

        Fragment fragmentToRemove = getItem(tab.getPosition());
        FragmentTransaction fragmentTransaction = mActivity
                .getSupportFragmentManager().beginTransaction();
        fragmentTransaction.remove(fragmentToRemove);
        fragmentTransaction.commit();

But neither has worked so far.

Got it!

public void removeTab(ActionBar.Tab tab) {
    mTabs.remove(tab.getTag());
    mActionBar.removeTab(tab);
    notifyDataSetChanged();
}

and override destroyItem

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        // TODO Auto-generated method stub
        super.destroyItem(container, position, object);
        FragmentManager manager = ((Fragment) object).getFragmentManager();
        FragmentTransaction trans = manager.beginTransaction();
        trans.remove((Fragment) object);
        trans.commit();
    }

Works perfectly!!!

I just discovered today that the real trick to supporting inserting or removing tabs is to override FragmentPagerAdapter.getItemPosition() :

@Override
public int getItemPosition(Object object) {
    Fragment fragment = (Fragment) object;
    for (int i = 0; i < tabInfos.size(); i++) {
        if (tabInfos.get(i).clss == fragment.getClass()) {
            return i;
        }
    }
    // The tab must have been removed
    return POSITION_NONE;
}

getItemPosition() will be called by the ViewPager after you have called notifyDataSetChanged() (after adding/removing tabs). The ViewPager is trying to resync its cache with this adapter. It calls getItemPosition() for each 'object' (Fragment) it holds, and we tell it what position that Fragment is from. If there is no match, it is because we deleted that tab, so return POSITION_NONE .

I have also found it's important to override FragmentPagerAdapter.getItemId() if you insert or remove tabs, because the Fragments will be shifting positions left and right. This method is called by FragmentPagerAdapter.instantiateItem() and is appended to a name (String) for the item. It uses this name to lookup a pre-existing Fragment with a matching name (if there is one) otherwise instantiate a new one. Here's some code:

@Override
public long getItemId(int position) {
    TabInfo info = tabInfos.get(position);
    // The default hashCode() implementation is based on memory address, which will be unique
    int hash = info.clss.hashCode();
    return hash;
}

Ok - this one ate my lunch for 2 weeks. I didn't find that the above worked for me at all exactly as shown. I am using Android Studio 3.5.1 in case that matters.

What follows is my FragmentPagerAdapter code in its entirety. It contains most everything you should need. The Fragment code is fairly straightforward for whatever each of your fragments contains. But let me know if more is required or if you have any questions.

Also, if you have any comments or find any errors, please post a comment. I hope this helps others.

import android.content.Context;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentTransaction;
import androidx.viewpager.widget.ViewPager;

import com.google.android.material.tabs.TabLayout;

import java.util.ArrayList;

public class ChecklistPagerAdapter extends FragmentPagerAdapter {

    private final Context mContext;
    private int currentTabPosition = -1;
    ArrayList<String> tabNames = new ArrayList<>();
    ArrayList<Fragment> fragments = new ArrayList<>();

    public ChecklistPagerAdapter(Context context, FragmentManager fm) {
        super(fm);
        mContext = context;
    }

    public void add(Fragment fragment, String title) {
        tabNames.add(title);
        fragments.add(fragment);
    }

    public int getCurrentTabPosition() {
        return currentTabPosition;
    }

    @Override
    public Fragment getItem(int position) {
        return fragments.get(position);
    }

    @Override
    public int getItemPosition(@NonNull Object object) {
        ChecklistFragment fragment = (ChecklistFragment) object;
        for (int i = 0; i < fragments.size(); i++) {
            if (fragments.get(i).equals(fragment)) {
                return i;
            }
        }
        return POSITION_NONE;
    }

    @Override
    public long getItemId(int position) {
        return fragments.get(position).hashCode();
    }

    @Override
    public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        super.setPrimaryItem(container, position, object);
        currentTabPosition = position;
    }

    @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        return tabNames.get(position);
    }

    @Override
    public int getCount() {
        return fragments.size();
    }

    public void removeTab(TabLayout tabLayout, ViewPager viewPager, DBHelper dbHelper) {
        // Get current tab position as it changes below
        int position = currentTabPosition;

        // Get current fragment representing curTab
        ChecklistFragment fragment = (ChecklistFragment) fragments.get(position);

        // Remove Tab
        tabLayout.removeTabAt(position);

        // Remove from ArrayLists
        fragments.remove(position);
        tabNames.remove(position);

        // Remove fragment
        FragmentManager fragmentManager = fragment.getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.remove(fragment);
        fragmentTransaction.commit();

        // Refresh
        tabLayout.invalidate();
        notifyDataSetChanged();

        // Remove all records from ChecklistTab table
        dbHelper.clearChecklistTab(fragment.getOutingId(), fragment.getChecklistId());
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM