Como deletar uma aba com actionbar, viewpager e vários fragmentos?
-
11-12-2019 - |
Pergunta
Estou usando o código que encontrei aqui.
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) {
}
}
}
Estou tentando modificá-lo para poder excluir guias dinamicamente.Eu tentei simplesmente criando esta função:
public void removeTab(ActionBar.Tab tab) {
mTabs.remove(tab);
mActionBar.removeTab(tab);
notifyDataSetChanged();
}
Mas sempre recebo uma exceção indexoutofbound.Alguém sabe uma boa maneira de fazer isso?
EDITAR
Mudando meu método para:
public void removeTab(ActionBar.Tab tab) {
mTabs.remove(tab.getTag());
mActionBar.removeTab(tab);
notifyDataSetChanged();
}
Consegui remover as guias com sucesso.No entanto, quando excluo uma guia que NÃO É a da extrema direita, a visualização (ou fragmento?) que estava associada à guia não desaparece.Em vez disso, parece estar associado à próxima guia mais alta.Por exemplo, se eu excluí a guia 0, a visualização da guia 0 apenas passa para a guia 1.Se eu excluir a guia com o ID mais alto, a guia e a visualização desaparecerão corretamente, mas VOLTARÃO quando eu adicionar uma nova guia.
É como se houvesse uma lista de fragmentos em algum lugar que está sendo associada às guias e excluir a guia não exclui o fragmento.Alguém tem alguma ideia do que está causando isso?
EDITAR2
Tentei excluir os fragmentos com:
Fragment fragmentToRemove = getItem(tab.getPosition());
destroyItem(mViewPager, tab.getPosition(), fragmentToRemove);
e também
Fragment fragmentToRemove = getItem(tab.getPosition());
FragmentTransaction fragmentTransaction = mActivity
.getSupportFragmentManager().beginTransaction();
fragmentTransaction.remove(fragmentToRemove);
fragmentTransaction.commit();
Mas nenhum dos dois funcionou até agora.
Solução
Entendi!
public void removeTab(ActionBar.Tab tab) {
mTabs.remove(tab.getTag());
mActionBar.removeTab(tab);
notifyDataSetChanged();
}
e substituir 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();
}
Funciona perfeitamente!!!
Outras dicas
Acabei de descobrir hoje que o verdadeiro truque para suportar a inserção ou remoção de guias é substituir 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()
será chamado pelo ViewPager
depois que você ligou notifyDataSetChanged()
(depois de adicionar/remover guias).O ViewPager
está tentando ressincronizar seu cache com este adaptador.Ele chama getItemPosition()
para cada 'objeto' (Fragmento) que ele contém, e nós informamos de qual posição esse Fragmento vem.Se não houver correspondência é porque excluímos aquela aba, então retorne POSITION_NONE
.
Eu também descobri que é importante substituir FragmentPagerAdapter.getItemId()
se você inserir ou remover abas, porque os fragmentos mudarão de posição para a esquerda e para a direita.Este método é chamado por FragmentPagerAdapter.instantiateItem()
e é anexado a um nome (String) para o item.Ele usa esse nome para procurar um Fragment pré-existente com um nome correspondente (se houver), caso contrário, instancia um novo.Aqui está um código:
@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 - este comeu meu almoço por 2 semanas.Não achei que o procedimento acima funcionasse para mim exatamente como mostrado.Estou usando o Android Studio 3.5.1, caso isso seja importante.
O que se segue é meu código FragmentPagerAdapter na íntegra.Ele contém quase tudo que você precisa.O código do Fragment é bastante simples para tudo o que cada um dos seus fragmentos contém.Mas deixe-me saber se for necessário mais ou se você tiver alguma dúvida.
Além disso, se você tiver algum comentário ou encontrar algum erro, poste um comentário.Espero que isso ajude outros.
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());
}
}