Viewpager и FragmentPagerAdapter: удаление вида страницы
Я использую Viewpager
с FragmentPagerAdapter
, чтобы добавлять и удалять страницы. На каждой странице отображаются данные, полученные из Интернета.
По мере добавления новой страницы с этой страницей связан новый фрагмент. Данные получены через AsyncTask и отображаются во Фрагменте. Когда пользователь выбирает удаление страницы, идея состоит в том, чтобы уничтожить страницу и связанный с ней фрагмент.
В общем, все это хорошо работает. Проблема, которую я вижу, следующая:
-
У вас есть три страницы с данными:
[Страница 1] [Страница 2] [Страница 3]
-
Вы удаляете любую страницу, отличную от последней, например, стр. 2; Исчезает по желанию:
[Страница 1] [Страница 3]
-
Вы добавляете новую страницу; но вместо пустой новой страницы на новой странице отображаются данные (вид) со страницы 3.
[Страница 1] [Страница 3] [Страница 4, но показ представления/данных Page 3, должен быть пустым]
Код удаления страницы в моей деятельности выглядит следующим образом:
// Destroy fragment for this page
DataListFragment curFrag = getFragmentByName(currentPage);
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction().remove(curFrag).commit();
fm.executePendingTransactions();
curFrag = null;
// Remove page and update adapter
mPageTitles.remove(position);
mAdapter.notifyDataSetChanged();
Используя отладчик, он показывает, что фрагмент удаляется из FragmentManager
после вызова executePendingTransactions()
. Но в вызове FrampePagerAdapters
mAdapter.notifyDataSetChanged()
фрагмент добавляется обратно, а затем отображается при создании новой страницы.
Я попытался использовать FrameStatePagerAdapter, так как это должно позволить уничтожить фрагменты, но это не сработало. В моем методе FragmentPagerAdapter getItemPosition()
я использую return FragmentAdapter.POSITION_NONE;
, как указано в другой статье SO, с которой я столкнулся.
Кажется, что представление для этой страницы не уничтожено, а затем добавлено обратно на новую страницу. Я попытался использовать метод removeViewAt()
в представлении новой страницы, но это не сработало.
Будучи новичком в этом, я уверен, что мне не хватает чего-то очевидного...
Ответы
Ответ 1
Вам лучше расширить FragmentStatePagerAdapter
и удалить соответствующий элемент из списка элементов этого адаптера вместо того, чтобы пытаться удалить фрагмент из действия. Не забудьте вызвать adapter.notifyDataSetChanged()
после удаления элемента в адаптере. После этого ViewPager
и FragmentStatePagerAdapter
позаботятся об остальном.
Ответ 2
См. метод getCount() возвращает точное количество элементов в viewPager. И да, FragmentStatePagerAdapter тоже подсчитывается.
Ответ 3
Я закончил с решением, которое смешивает следующие знания, основанные на опыте:
- Вы можете добавить новый
Fragment
в хвост без проблем.
- Вы не можете прочитать
Fragment
, который ранее был удален, поскольку иногда это приводит к java.lang.IllegalStateException: Can't change tag of fragment
, поэтому вам нужно клонировать его.
- Для удаления
Fragment
вам нужно вернуть PagerAdapter.POSITION_NONE
в методе getItemPosition(Object object)
и удалить Fragment
из FragmentManager
.
- Если вы добавляете/удаляете/заменяете в другом месте, отличном от хвоста, вам нужно удалить все с позиции, которую вы меняете до конца, делать вещи, а затем читать (клонированные)
Fragment
, которые вы удалены.
Вот полный код FragmentActivity
с FragmentPagerAdapter
, который имеет 3 метода для добавления, удаления и замены вкладок:
public class TabTestActivity extends FragmentActivity implements
ActionBar.TabListener {
private SectionsPagerAdapter mSectionsPagerAdapter;
private ViewPager mViewPager;
private static int tabCount = 0;
private static String labelString = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
labelString = getString(R.string.title_section);
setContentView(R.layout.activity_tab_test);
final ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
mSectionsPagerAdapter = new SectionsPagerAdapter(
getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(final int position) {
(new Handler()).postDelayed(new Runnable() {
@Override
public void run() {
actionBar.setSelectedNavigationItem(position);
}
}, 1);
}
});
for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
actionBar.addTab(actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(i))
.setTabListener(this));
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.tab_test, menu);
return true;
}
public void addNewTab() {
int position = (mSectionsPagerAdapter.getCount() > 0 ? mViewPager.getCurrentItem() : 0);
mSectionsPagerAdapter.insertFragment(position);
mViewPager.setCurrentItem(position, true);
}
public void removeTab() {
if (mSectionsPagerAdapter.getCount() > 0) {
int position = mViewPager.getCurrentItem();
mSectionsPagerAdapter.removeFragment(position);
}
}
public void replaceTab() {
if (mSectionsPagerAdapter.getCount() > 0) {
int position = mViewPager.getCurrentItem();
mSectionsPagerAdapter.replaceFragment(position);
mViewPager.setCurrentItem(position, false);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_add_tab:
addNewTab();
return true;
case R.id.action_remove_tab:
removeTab();
return true;
case R.id.action_replace_tab:
replaceTab();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onTabSelected(ActionBar.Tab tab,
FragmentTransaction fragmentTransaction) {
mViewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(ActionBar.Tab tab,
FragmentTransaction fragmentTransaction) {
}
@Override
public void onTabReselected(ActionBar.Tab tab,
FragmentTransaction fragmentTransaction) {
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> currentFragments;
private FragmentManager fragmentManager;
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
fragmentManager = fm;
currentFragments = new ArrayList<Fragment>();
}
public void insertFragment(int position) {
// Remove fragments from position
List<Fragment> fragmentsToRemove = new ArrayList<Fragment>(currentFragments.subList(position, currentFragments.size()));
int i = currentFragments.size() - 1;
int j = -1;
int k = i;
while (i >= position) {
currentFragments.remove(i);
i--;
j++;
}
notifyDataSetChanged();
final ActionBar actionBar = getActionBar();
while (k >= position) {
actionBar.removeTabAt(k);
k--;
}
android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction();
while (j >= 0) {
Fragment fragmentToRemove = fragmentsToRemove.get(j);
transaction.detach(fragmentToRemove);
transaction.remove(fragmentToRemove);
j--;
}
transaction.commit();
fragmentManager.executePendingTransactions();
notifyDataSetChanged();
// Add new fragment
Fragment fragment = new DummySectionFragment();
currentFragments.add(position, fragment);
notifyDataSetChanged();
actionBar.addTab(actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(position))
.setTabListener(TabTestActivity.this), position);
// Readd fragments
if (fragmentsToRemove.size() > 0) {
i = 1;
for (Fragment fragmentToRemove : fragmentsToRemove) {
currentFragments.add(DummySectionFragment.cloneExistingFragment((DummySectionFragment)fragmentToRemove));
notifyDataSetChanged();
actionBar.addTab(actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(position + i))
.setTabListener(TabTestActivity.this), position + i);
i++;
}
}
}
public void removeFragment(int position) {
// Remove fragments from position
List<Fragment> fragmentsToRemove = new ArrayList<Fragment>(currentFragments.subList(position, currentFragments.size()));
int i = currentFragments.size() - 1;
int j = -1;
int k = i;
while (i >= position) {
currentFragments.remove(i);
i--;
j++;
}
notifyDataSetChanged();
final ActionBar actionBar = getActionBar();
while (k >= position) {
actionBar.removeTabAt(k);
k--;
}
android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction();
while (j >= 0) {
Fragment fragmentToRemove = fragmentsToRemove.get(j);
transaction.detach(fragmentToRemove);
transaction.remove(fragmentToRemove);
j--;
}
transaction.commitAllowingStateLoss();
fragmentManager.executePendingTransactions();
notifyDataSetChanged();
// Readd fragments (except one)
if (fragmentsToRemove.size() > 1) {
i = 0;
for (Fragment fragment : fragmentsToRemove.subList(1, fragmentsToRemove.size())) {
currentFragments.add(DummySectionFragment.cloneExistingFragment((DummySectionFragment)fragment));
notifyDataSetChanged();
actionBar.addTab(actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(position + i))
.setTabListener(TabTestActivity.this), position + i);
i++;
}
}
}
public void replaceFragment(int position) {
// Remove fragments from position
List<Fragment> fragmentsToRemove = new ArrayList<Fragment>(currentFragments.subList(position, currentFragments.size()));
int i = currentFragments.size() - 1;
int j = -1;
int k = i;
while (i >= position) {
currentFragments.remove(i);
i--;
j++;
}
notifyDataSetChanged();
final ActionBar actionBar = getActionBar();
while (k >= position) {
actionBar.removeTabAt(k);
k--;
}
android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction();
while (j >= 0) {
Fragment fragmentToRemove = fragmentsToRemove.get(j);
transaction.detach(fragmentToRemove);
transaction.remove(fragmentToRemove);
j--;
}
transaction.commit();
fragmentManager.executePendingTransactions();
notifyDataSetChanged();
// Add new fragment
Fragment fragment = new DummySectionFragment();
currentFragments.add(position, fragment);
notifyDataSetChanged();
actionBar.addTab(actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(position))
.setTabListener(TabTestActivity.this), position);
// Readd fragments (except one)
if (fragmentsToRemove.size() > 0) {
i = 1;
for (Fragment fragmentToRemove : fragmentsToRemove.subList(1, fragmentsToRemove.size())) {
currentFragments.add(DummySectionFragment.cloneExistingFragment((DummySectionFragment)fragmentToRemove));
notifyDataSetChanged();
actionBar.addTab(actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(position + i))
.setTabListener(TabTestActivity.this), position + i);
i++;
}
}
}
@Override
public Fragment getItem(int position) {
if (currentFragments == null) {
currentFragments = new ArrayList<Fragment>();
}
while (currentFragments.size() <= position) {
currentFragments.add(null);
}
if (currentFragments.get(position) != null) {
return currentFragments.get(position);
}
Fragment fragment = new DummySectionFragment();
currentFragments.set(position, fragment);
return fragment;
}
@Override
public int getCount() {
return currentFragments.size();
}
@Override
public int getItemPosition(Object object) {
int position = currentFragments.indexOf(object);
if (position == -1) {
return PagerAdapter.POSITION_NONE;
}
return position;
}
@Override
public CharSequence getPageTitle(int position) {
return ((DummySectionFragment)getItem(position)).getTitle();
}
}
public static class DummySectionFragment extends Fragment {
private int sectionNumber;
public DummySectionFragment() {
super();
sectionNumber = ++tabCount;
}
public static DummySectionFragment cloneExistingFragment(DummySectionFragment fragment) {
DummySectionFragment cloned = new DummySectionFragment();
// Hack for avoiding autoincrement
--tabCount;
cloned.sectionNumber = fragment.getSectionNumber();
return cloned;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_tab_test_dummy,
container, false);
TextView dummyTextView = (TextView) rootView
.findViewById(R.id.section_label);
dummyTextView.setText(String.format(labelString, sectionNumber));
return rootView;
}
public int getSectionNumber() {
return sectionNumber;
}
public String getTitle() {
return String.format(labelString, sectionNumber);
}
}
}