ViewPager PagerAdapter не обновляет представление

Как правило, и с настройками по умолчанию привязки ^ и $ являются хорошим способом обеспечения соответствия регулярного выражения целой строке.

Несколько предостережений:

Если у вас есть чередование в вашем регулярном выражении, обязательно добавьте ваше регулярное выражение в группу, не связанную с захватом, перед тем, как окружать ее с помощью ^ и $:

^foo|bar$

, конечно, отличается от

^(?:foo|bar)$

Кроме того, ^ и $ могут принимать другое значение (начало / конец строки вместо начала / конца строки ]), если установлены определенные параметры. В текстовых редакторах, поддерживающих регулярные выражения, обычно это поведение по умолчанию. На некоторых языках, особенно Ruby, это поведение нельзя даже отключить.

Поэтому существует еще один набор якорей, которые гарантированно будут совпадать только в начале / конце всей строки:

\A соответствует в начале строки.

\Z соответствует в конце строки или перед окончательным разрывом строки.

\z соответствует в самом конце строки.

Но не все языки поддерживают эти якоря, особенно JavaScript.

564
задан A-Sharabiani 11 October 2016 в 17:45
поделиться

11 ответов

Что бы это ни стоило, на KitKat + кажется, что adapter.notifyDataSetChanged() достаточно, чтобы вызвать появление новых представлений, при условии, что вы setOffscreenPageLimit достаточно высоко. Я могу получить желаемое поведение, выполнив viewPager.setOffscreenPageLimit(2).

0
ответ дан mszaro 11 October 2016 в 17:45
поделиться

В моем случае каждый раз, когда Fragment входил в @override onCreateView(...), я перезапускал значения, поэтому обрабатывал пустые или пустые значения, такие как:

if(var == null){
    var = new Something();
}

if(list.isEmpty()){
    list = new ArrayList<MyItem>();
}

if(myAdapter == null){
    myAdapter = new CustomAdapter();
    recyclerView.setAdapter(myAdapter);
}

// etc...

Класс Fragment мог входить в onCreateView каждый раз, когда мы меняемся, и возвращаться к вид из пользовательского интерфейса во время выполнения.

0
ответ дан Federico Grandi 11 October 2016 в 17:45
поделиться

Гораздо более простой способ: используйте FragmentPagerAdapter и перенесите ваши постраничные представления на фрагменты. Они обновляются

5
ответ дан Jorgesys 11 October 2016 в 17:45
поделиться

На всякий случай, если кто-либо использует адаптер на основе FragmentStatePagerAdapter (который позволит ViewPager создавать минимум страниц, необходимых для цели отображения, максимум 2 для моего случая), ответ @ rui.araujo о перезаписи getItemPosition в вашем адаптере не вызовет значительных потерь, но все же может быть улучшено.

В псевдокоде:

public int getItemPosition(Object object) {
    YourFragment f = (YourFragment) object;
    YourData d = f.data;
    logger.info("validate item position on page index: " + d.pageNo);

    int dataObjIdx = this.dataPages.indexOf(d);

    if (dataObjIdx < 0 || dataObjIdx != d.pageNo) {
        logger.info("data changed, discard this fragment.");
        return POSITION_NONE;
    }

    return POSITION_UNCHANGED;
}
4
ответ дан Jerry Tian 11 October 2016 в 17:45
поделиться

После долгих поисков этой проблемы я нашел действительно хорошее решение, которое я считаю правильным. По сути, instantiateItem вызывается только тогда, когда создается экземпляр представления, и никогда больше, если только представление не разрушено (это то, что происходит, когда вы переопределяете функцию getItemPosition для возврата POSITION_NONE). Вместо этого вам нужно сохранить созданные представления и либо обновить их в адаптере, сгенерировать функцию get, чтобы кто-то другой мог ее обновить, либо функцию set, которая обновляет адаптер (мой любимый).

Итак, в вашем MyViewPagerAdapter добавьте переменную, такую ​​как:

private View updatableView;

a в вашем instantiateItem:

 public Object instantiateItem(View collection, int position) {
        updatableView = new TextView(ctx); //My change is here
        view.setText(data.get(position));
        ((ViewPager)collection).addView(view);
        return view;
    }

, таким образом, вы можете создать функцию, которая будет обновлять ваш просмотр:

public updateText(String txt)
{
    ((TextView)updatableView).setText(txt);
}

Надеюсь, это поможет!

3
ответ дан German 11 October 2016 в 17:45
поделиться

то, что работало для меня, шло viewPager.getAdapter().notifyDataSetChanged();

, и в адаптере выкладывал свой код для обновления представления внутри getItemPosition, например,

@Override
public int getItemPosition(Object object) {

    if (object instanceof YourViewInViewPagerClass) { 
        YourViewInViewPagerClass view = (YourViewInViewPagerClass)object;
        view.setData(data);
    }

    return super.getItemPosition(object);
}

может быть не самым правильным как бы то ни было, но это сработало (трюк return POSITION_NONE вызвал у меня сбой, поэтому не было выбора)

1
ответ дан Ziem 11 October 2016 в 17:45
поделиться

Вы можете динамически обновлять все фрагменты, вы можете увидеть в три шага.

В вашем адаптере:

public class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;

public MyPagerAdapter(FragmentManager fragmentManager) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mFragmentTags = new HashMap<Integer, String>();
}

// Returns total number of pages
@Override
public int getCount() {
    return NUM_ITEMS;
}

// Returns the fragment to display for that page
@Override
public Fragment getItem(int position) {
    switch (position) {
        case 0:
            return FirstFragment.newInstance();
        case 1:
            return SecondFragment.newInstance();
        case 2:
            return ThirdFragment.newInstance();
        default:
            return null;
    }
}

// Returns the page title for the top indicator
@Override
public CharSequence getPageTitle(int position) {
    return "Page " + position;
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object object = super.instantiateItem(container, position);
    if (object instanceof Fragment) {
        Fragment fragment = (Fragment) object;
        String tag = fragment.getTag();
        mFragmentTags.put(position, tag);
    }
    return object;
}

public Fragment getFragment(int position) {
    Fragment fragment = null;
    String tag = mFragmentTags.get(position);
    if (tag != null) {
        fragment = mFragmentManager.findFragmentByTag(tag);
    }
    return fragment;
}}

Теперь в вашей деятельности:

public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener{

MyPagerAdapter mAdapterViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ViewPager viewPager = (ViewPager) findViewById(R.id.vpPager);
    mAdapterViewPager = new MyPagerAdapter(getSupportFragmentManager());
    viewPager.setAdapter(mAdapterViewPager);
    viewPager.addOnPageChangeListener(this);
}

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

}

@Override
public void onPageSelected(int position) {

    Fragment fragment = mAdapterViewPager.getFragment(position);
    if (fragment != null) {
        fragment.onResume();
    }
}

@Override
public void onPageScrollStateChanged(int state) {

}}

Наконец, в вашем фрагменте, что-то вроде этого:

public class YourFragment extends Fragment {

// newInstance constructor for creating fragment with arguments
public static YourFragment newInstance() {

    return new YourFragment();
}

// Store instance variables based on arguments passed
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

// Inflate the view for the fragment based on layout XML
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment, container, false);
}


@Override
public void onResume() {
    super.onResume();

    //to refresh your view
    refresh();

}}

Вы можете полный код см. здесь здесь .

Спасибо Альваро Луису Бустаманте.

1
ответ дан Cabezas 11 October 2016 в 17:45
поделиться

Измените FragmentPagerAdapter на FragmentStatePagerAdapter.

Переопределить метод getItemPosition() и вернуть POSITION_NONE.

В конце концов, он прослушает пейджер notifyDataSetChanged() при просмотре.

79
ответ дан Jorgesys 11 October 2016 в 17:45
поделиться

Я не думаю, что в PagerAdapter есть какая-либо ошибка. Проблема в том, что понять, как это работает, немного сложно. Глядя на решения, объясненные здесь, есть недопонимание и, следовательно, плохое использование воплощенных взглядов с моей точки зрения.

Последние несколько дней я работал с PagerAdapter и ViewPager и обнаружил следующее:

Метод notifyDataSetChanged() в PagerAdapter только уведомит ViewPager, что основные страницы изменились. Например, если вы создали / удалили страницы динамически (добавляя или удаляя элементы из вашего списка), об этом должно позаботиться ViewPager. В этом случае я думаю, что ViewPager определяет, должен ли новый вид быть удален или создан с использованием методов getItemPosition() и getCount().

Я думаю, что ViewPager после вызова notifyDataSetChanged() принимает его дочерние представления и проверяет их положение с помощью getItemPosition(). Если для дочернего представления этот метод возвращает POSITION_NONE, ViewPager понимает, что представление было удалено, вызывает destroyItem() и удаляет это представление.

Таким образом, переопределение getItemPosition() всегда возвращать POSITION_NONE совершенно неправильно, если вы хотите обновить только содержимое страниц, потому что ранее созданные представления будут уничтожены, а новые будут создаваться при каждом вызове. notifyDatasetChanged(). Может показаться, что это не так неправильно только на несколько TextView с, но когда у вас есть сложные представления, такие как ListViews, заполненные из базы данных, это может быть реальной проблемой и пустой тратой ресурсов.

Таким образом, существует несколько подходов для эффективного изменения содержимого представления без необходимости повторного удаления и создания экземпляра представления. Это зависит от проблемы, которую вы хотите решить. Мой подход заключается в использовании метода setTag() для любого экземпляра представления в методе instantiateItem(). Поэтому, когда вы хотите изменить данные или сделать недействительным нужное представление, вы можете вызвать метод findViewWithTag() в ViewPager, чтобы получить ранее созданное представление и изменить / использовать его так, как вам нужно, без необходимости удалять / создавать новый вид каждый раз, когда вы хотите обновить некоторое значение.

Представьте, например, что у вас есть 100 страниц со 100 TextView с, и вы хотите периодически обновлять только одно значение. С подходами, объясненными ранее, это означает, что вы удаляете и создаете 100 экземпляров при каждом обновлении. Это не имеет смысла ...

467
ответ дан JDJ 11 October 2016 в 17:45
поделиться

я знаю, что я - дамба поздно, но тем не менее она может помочь кому-то. Я просто расширяю ответ accetping, и я также добавил комментарий к нему.

хорошо,

в самом ответе говорится, что это неэффективно

поэтому, чтобы заставить его обновить aonly при необходимости, можно сделать это

private boolean refresh;

public void refreshAdapter(){
    refresh = true;
    notifyDataSetChanged();
}

@Override
public int getItemPosition(@NonNull Object object) {
    if(refresh){
        refresh = false;
        return POSITION_NONE;
    }else{
        return super.getItemPosition(object);
    }
}
0
ответ дан 22 November 2019 в 22:07
поделиться

ViewPager не был разработан для поддержки изменения динамического представления.

у меня было подтверждение этого при поиске другой ошибки, связанной с этим https://issuetracker.google.com/issues/36956111 и в особенности https://issuetracker.google.com/issues/36956111#comment56

, Этот вопрос немного стар, но Google недавно решил эту проблему с ViewPager2. Это позволит заменять ручной работы (несохраняемый и потенциально ошибочный) решения стандартным. Это также предотвращает представления воссоздания напрасно, как некоторые ответы делают.

Для примеров ViewPager2, можно проверить https://github.com/googlesamples/android-viewpager2

, Если Вы захотите использовать ViewPager2, то необходимо будет добавить следующую зависимость в build.gradle файле:

  dependencies {
     implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'
  }

Затем можно заменить ViewPager в XML-файле с:

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

После этого, необходимо будет заменить ViewPager ViewPager2 в действии

, ViewPager2 нужен любой RecyclerView. Адаптер или FragmentStateAdapter, в Вашем случае это может быть RecyclerView. Адаптер

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {

    private Context context;
    private ArrayList<String> arrayList = new ArrayList<>();

    public MyAdapter(Context context, ArrayList<String> arrayList) {
        this.context = context;
        this.arrayList = arrayList;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.tvName.setText(arrayList.get(position));
    }

    @Override
    public int getItemCount() {
        return arrayList.size();
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        TextView tvName;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            tvName = itemView.findViewById(R.id.tvName);
        }
    }
} 

В случае, Вы использовали TabLayout, можно использовать TabLayoutMediator:

        TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager, true, new TabLayoutMediator.OnConfigureTabCallback() {
            @Override
            public void onConfigureTab(@NotNull TabLayout.Tab tab, int position) {
                // configure your tab here
                tab.setText(tabs.get(position).getTitle());
            }
        });

        tabLayoutMediator.attach();

Затем Вы сможете обновить свои представления путем изменения данных адаптера и вызова notifyDataSetChanged метода

0
ответ дан 22 November 2019 в 22:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: