2015-05-17 3 views
2

Пытается динамически добавлять фрагменты на разные страницы в Android ViewPager.Вставить новый фрагмент в уже запущенном Android ViewPager

Динамическое добавление фрагмента в конец списка фрагментов прекрасно работает, но попытка добавить к следующему слоту (невидимая страница после текущей просматриваемой страницы). Я попытался добавить в arraylist фрагменты и использовать notifyDataSetChanged и попытаться использовать set на каждом из фрагментов после того, как установить каждый из предыдущего элемента.

Edit: обновленный код

class MainPagerAdapter extends FragmentPagerAdapter { 

    public MainPagerAdapter(FragmentManager fm) { 
     super(fm); 
    } 

    @Override 
    public PageFragment getItem(int position) { 
     return PageFragment.newInstance(Datamart.getInstance().getURLs().get(position)); 
    } 

    @Override 
    public int getCount() { 
     return Datamart.getInstance().getURLs().size(); 
    } 

    @Override 
    public int getItemPosition(Object object) { 
     PageFragment pageFragment = (PageFragment) object; 
     for (int i = 0; i < getCount(); i++) { 
      if (pageFragment.getURL().equals(Datamart.getInstance().getURLs().get(i))) { 
       return i; 
      } 
     } 
     return POSITION_NONE; 
    } 
} 

В DataMart:

public class Datamart { 

    static Datamart instance; 

    private ArrayList<String> URLs; 
    private MainPagerAdapter mainPagerAdapter; 
    private ViewPager viewPager; 

    public Datamart() { 
     URLs = new ArrayList<>(); 
    } 

    public static Datamart getInstance() { 
     if (instance == null) { 
      instance = new Datamart(); 
     } 
     return instance; 
    } 

    public MainPagerAdapter getMainPagerAdapter() { 
     return mainPagerAdapter; 
    } 

    public void setMainPagerAdapter(MainPagerAdapter mainPagerAdapter) { 
     this.mainPagerAdapter = mainPagerAdapter; 
    } 

    public ViewPager getViewPager() { 
     return viewPager; 
    } 

    public void setViewPager(ViewPager viewPager) { 
     this.viewPager = viewPager; 
    } 

    public void addToEnd(String URL) { 
     URLs.add(URL); 
     mainPagerAdapter.notifyDataSetChanged(); 
    } 

    public void addNext(String URL) { 
     URLs.add(viewPager.getCurrentItem() + 1, URL); 
     mainPagerAdapter.notifyDataSetChanged(); 
    } 

    public ArrayList<String> getURLs() { 
     return URLs; 
    } 

    public void setURLs(ArrayList<String> URLs) { 
     this.URLs = URLs; 
    } 
} 
+0

Я подозреваю, что в вашем методе add() есть ошибка. Можете ли вы показать код для этого? – lemuel

+0

Мне кажется, проблема в mainPagerAdapter, предполагая подкласс PagerAdapter. Глядя на логарифм, я подозреваю, что проблема заключается в instantiateItem(). Возможно, опубликуйте некоторые/большинство кода в этом классе. –

+0

Добавлен код, который поможет лучше объяснить. – ogoldbart3

ответ

2

ViewPager делает его darndest, чтобы попытаться отслеживать, где все фрагменты идут, и вы получили его путают, потому что вы вставил фрагмент посередине, не сказав об этом.

Давайте сделаем пример. Скажите, что у вас есть две страницы, так что вы показываете первую страницу, а вторая страница - внеэкранная страница справа. У вас есть fragment0 для первой страницы и fragment1 для второй страницы.

Во-первых, имейте в виду, что поскольку ViewPager управляет закадровый страниц непосредственно слева и справа, он уже думает, что он знает, что второй фрагмент страницы, который fragment1.

Когда вы вставляете фрагмент между этими существующими фрагментами страниц, ViewPager не собирается спрашивать вас, какой фрагмент находится во второй позиции, он попросит вас , если фрагмент во второй позиции изменился.

Это где getItemPosition() приходит. Вы не выполнили getItemPosition() в вашем FragmentPagerAdapter, поэтому поведение по умолчанию getItemPosition() является возвращение POSITION_UNCHANGED. (Как вы увидите, поэтому добавление фрагмента в конце не вызывает ошибку.)

Итак, когда вы назвали notifyDataSetChanged(), то ViewPager называется getCount() и увидел, что вместо двух страниц, теперь у вас есть три , Затем он назывался getItemPosition() для страниц 0 и страницы 1 (страницы, о которых он знал), и ваша реализация по умолчанию сказала, что ничего не изменилось.

Но что-то сделал изменение, вы вставили страницу/фрагмент в середине двух существующих страниц. ViewPager спросил вас, изменилась ли вторая страница, и ваш getItemPosition() должен был вернуть 2 вместо POSITION_UNCHANGED.

Теперь, потому что ваш getCount() вернулся 3 вместо 2, ViewPager знает, что есть новая страница в конце, что он не знает о том, так она называет getItem(), чтобы получить новый фрагмент.

Здесь происходит ошибка. Ваш getItem() метод возвращен fragment1 для страницы 3 и ViewPager забитый, потому что вы передали ему фрагмент, который он уже знал о. В частности, он использует тег фрагмента для некоторой домашней работы.Он создает тег из идентификатора контейнера страницы и позиции позиции (номер страницы). Поэтому в любой момент времени он ожидает, что либо (а) тег равен нулю, либо (б) тег будет тем же самым значением, которое оно уже вычислено из идентификатора контейнера и позиции позиции. Ни одно из этих условий не было правдой, поэтому вот почему был выброшен IllegalStateException.

Итак, как вы это исправите?

Вы реализуете getItemPosition(), что делает правильную вещь. Когда вы вызываете getItemPosition(), вы просматриваете список фрагментов, и когда вы находите соответствующий фрагмент, вы возвращаете индекс фрагмента. Если вы удалили фрагмент, чтобы в списке не было соответствующего фрагмента, вы возвращаете POSITION_NONE, чтобы сообщить ViewPager, что страница должна быть удалена. ViewPager затем вызовет ваш метод getItem(), чтобы получить новую страницу. Таким образом, ViewPager всегда может оставаться в синхронизации с адаптером.


Когда я пишу FragmentPagerAdapter, я даже не использовать фрагменты в модели. Если бы я писал ваш адаптер, у меня просто был бы список URL-адресов, так как похоже, что каждая страница/фрагмент имеет собственный уникальный URL-адрес.

Я бы также реализовал метод getUrl() для класса фрагмента, чтобы получить URL-адрес, с которым был создан этот фрагмент.

Итак, для getItemPosition() я бы назвал getUrl() на фрагменте, который прошел, и выполнил поиск в списке моего адаптера для этого URL-адреса. Затем я вернул бы индекс URL-адреса в списке, или POSITION_NONE, если его больше нет.

Тогда в getItem(), я бы экземпляр нового фрагмента, используя URL-адрес в позиции списка, который был принят в. Мое правило в том, что я никогда не экземпляр фрагмента, пока ViewPager специально не просит его по телефону getItem().

Таким образом, доступ к фрагментам осуществляется только ViewPager, и я не сталкиваюсь с этими проблемами.

Также не вносите никаких изменений в модель адаптера между моментом, когда ViewPager звонит startUpdate(), когда он вызывает finishUpdate(). Вот как ViewPager сообщает вашему адаптеру, что он находится в «критическом разделе», чтобы выяснить, куда идут все фрагменты страницы.

PagerAdapter s может быть медведем для работы с. Поместите некоторое отладочное ведение журнала в свои методы getCount(), getItemPosition() и getItem(), чтобы вы могли понять, как ViewPager использует ваш адаптер для управления своими страницами.

+0

Здесь, после вызова 'getItemPosition' для ** фрагмента0 ** и ** фрагмента1 **, он вызовет' instantiateItem' с ** position = 1 ** (стр. 2), потому что именно там, где viewpager узнает, что ему нужно вставить , Если вы проверите внутри 'instantiateItem'' FragmentPagerAdapter', вы увидите, что в конечном итоге он вернет исходный старый фрагмент1 (containerId и ItemId остаются такими же). Поэтому в конце страницы page и страница2 будут показывать фрагмент 0, а фрагмент1 и страница1 будут пустыми. Я пытался сделать то же самое, и это то, что происходит для меня. Просьба пояснить, как работает ваш код без переопределения 'getItemId'. – ArunL

+0

Было бы лучше, если бы я посмотрел на ваш код, иначе мы поедем в круги. Отправьте вопрос с кодом и ответьте на этот комментарий ссылкой на вопрос, и я постараюсь вам помочь. –

Смежные вопросы