Обновить фрагмент из ViewPager
У меня есть Activity
с ViewPager
, у меня тоже есть Dialog
это восстановит список с некоторыми элементами, которые выберет пользователь. Теперь, как я могу обновить Fragment
где элементы должны отображаться как новые виды, прикрепленные к этому фрагменту?
Вот мой ViewPager
Адаптер:
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return 1;
}
@Override
public android.support.v4.app.Fragment getItem(int position) {
switch (position) {
case 0:
return new Fragment1();
default:
return null;
}
}
}
Теперь вот Fragment
:
public class Fragment1 extends Fragment{
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
}
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
}
@Override
public ViewGroup onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
ViewGroup view = (ViewGroup)inflater.inflate(R.layout.screen1, container, false);
}
return view;
}
}
Это метод, который я хочу использовать, чтобы добавить элементы в мой Fragment
private void addItem() {
// Instantiate a new "row" view.
final ViewGroup[] newViews = new ViewGroup[selectedItems.length];
for(int i = 0; i < selectedItems.length; i++){
newViews[i] = (ViewGroup) LayoutInflater.from(this).inflate(
R.layout.animated_layout_item, mContainerView, false);
// Set the text in each new row.
((TextView) newViews[i].findViewById(android.R.id.text1)).setText(selectedItems[i]);
// Because mContainerView has android:animateLayoutChanges set to true,
// adding this view is automatically animated.
mContainerView.addView(newViews[i], 0);
// Set a click listener for the "X" button in the row that will remove the row.
OnClickListener listener = new MyAddItemListener(newViews[i], mContainerView, this);
newViews[i].findViewById(R.id.delete_button).setOnClickListener(listener);
}
}
15 ответов
Обновить фрагмент из ViewPager
Вам необходимо реализовать getItemPosition(Object obj)
метод.
Этот метод вызывается при вызове
notifyDataSetChanged()
на ваше ViewPagerAdaper
, Неявно этот метод возвращает POSITION_UNCHANGED
значение, которое означает что-то вроде этого:"Фрагмент - это то, где он должен быть, поэтому ничего не меняйте".
Так что если вам нужно обновить Fragment, вы можете сделать это:
- Всегда возвращайся
POSITION_NONE
отgetItemPosition()
метод. Это означает: "Фрагмент должен быть всегда воссоздан" - Вы можете создать метод update(), который обновит ваш фрагмент (фрагмент будет обрабатывать обновления самостоятельно)
Пример второго подхода:
public interface Updateable {
public void update();
}
public class MyFragment extends Fragment implements Updateable {
...
public void update() {
// do your stuff
}
}
И в FragmentPagerAdapter вы будете делать что-то вроде этого:
@Override
public int getItemPosition(Object object) {
MyFragment f = (MyFragment ) object;
if (f != null) {
f.update();
}
return super.getItemPosition(object);
}
И если вы выберете первый подход, он может выглядеть так:
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
Примечание: стоит подумать о том, какой подход вы выберете.
Я сталкивался с проблемой несколько раз, так что в этом случае всегда избегайте FragmentPagerAdapter и используйте FragmentStatePagerAdapter.
Это работает для меня. Я надеюсь, что это будет работать и для вас.
Я реализую демо-версию приложения после ответа Саймона. Просто для тех, кто ищет этот вопрос.
Вы можете получить доступ к исходному коду на GitHub
В вашем PagerAdapter переопределите getItemPosition
@Override
public int getItemPosition(Object object) {
// POSITION_NONE makes it possible to reload the PagerAdapter
return POSITION_NONE;
}
После этого добавьте viewPager.getAdapter(). NotifyDataSetChanged(); этот код как показано ниже
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
viewPager.getAdapter().notifyDataSetChanged();
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
должно сработать.
Вы можете обновить фрагмент двумя разными способами,
Первый путь
как @Sajmon
Вам необходимо реализовать getItemPosition(Object obj)
метод.
Этот метод вызывается при вызове
notifyDataSetChanged()
Вы можете найти пример в Github и больше информации в этом посте.
Второй способ
Мой подход к обновлению фрагментов в ViewPager заключается в использовании setTag()
метод для любого экземпляра представления в instantiateItem()
метод. Поэтому, если вы хотите изменить данные или сделать недействительным представление, которое вам нужно, вы можете вызвать findViewWithTag()
метод ViewPager для извлечения ранее созданного экземпляра представления и изменения / использования его по своему усмотрению без необходимости удалять / создавать новое представление каждый раз, когда вы хотите обновить какое-либо значение.
@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;
}
Вы можете найти пример в Github или дополнительную информацию в этом посте:
Очень просто переопределить метод во фрагменте:
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser){
actionView();
}
else{
//no
}
}
Принятый ответ не понимаю, поэтому добавлено простое решение. после долгой борьбы найти рабочее решение.
Просто сообщите об этом вашему адаптеру, он работает потрясающе,
Код ссылки
void setAdapter(int position) {
PagerAdapter pagerAdapter = new PagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pagerAdapter);
// when notify then set manually current position.
viewPager.setCurrentItem(position);
pagerAdapter.notifyDataSetChanged();
}
вызывайте этот код, когда вы устанавливаете адаптер и хотите обновить интерфейс.
В свой фрагмент добавьте этот код для обновления
((YourActivityName) getContext()).setAdapter(selectedTabPosition);
использование instanceof
для приведения контекста.
Этот код разделяют только помогающие цели, которые находят решение.
Множество ответов, но быстрое и грязное решение, которое выполнило свою работу за меня, - это метод для инициализации вашего pagerAdapter в вашем MainActivity:
public void initPagerAdapter(){
pagerAdapter = new MainPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pagerAdapter);
tabLayout.setupWithViewPager(viewPager);
}
В моем фрагменте, который я хочу обновить, я просто получаю MainActivity и вызываю этот метод:
MainActivity activity = (MainActivity) getContext();
activity.initPagerAdapter();
У меня было 4 фрагмента в ViewPager, но я хотел обновить только один фрагмент "FavoriteFragment" в позиции 0 viewPager, каждый раз, когда пользователь нажимает на этот фрагмент. но ни один из приведенных выше кодов не помог мне перезагрузить только тот фрагмент, который я хочу. так что я попробовал это, и это работает для меня:
в моем любимом фрагменте я переопределил onResume()
@Override
public void onResume() {
super.onResume();
updateUI(); // code to update the UI in the fragment
}
в Activity, где размещается переопределение viewPager addOnPageChangeListener()
и добавьте этот код
mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
if(position == 0) { // 0 = the first fragment in the ViewPager, in this case, the fragment i want to refresh its UI
FavoritesFragment fragment = (FavoritesFragment) mViewPager.getAdapter().instantiateItem(mViewPager, position);
fragment.onResume(); // here i call the onResume of the fragment, where i have the method updateUI() to update its UI
mViewPager.getAdapter().notifyDataSetChanged();
}
}
});
и это прекрасно работает для меня, чтобы обновить / перезагрузить только один фрагмент, который я хочу.
Очень простое, но эффективное решение для обновления ваших фрагментов. использование setUserVisibleHint()
метод. Он будет вызван, когда фрагмент станет видимым для пользователя. Там вы можете обновить наш взгляд (setText()
или же setVisibility()
так далее.)
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser){
//Write your code to be executed when it is visible to user
}
}
Это сработало в моем случае. Надеюсь, поможет!
Если вы используете Kotlin, вы можете сделать следующее:
1. На первом, вы должны быть созданы Interface
и внедрил его в свой Fragment
interface RefreshData {
fun refresh()
}
class YourFragment : Fragment(), RefreshData {
...
override fun refresh() {
//do what you want
}
}
2. Следующий шаг - добавить OnPageChangeListener
на ваш ViewPager
viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) { }
override fun onPageSelected(position: Int) {
viewPagerAdapter.notifyDataSetChanged()
viewPager.currentItem = position
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { }
})
3. переопределить getItemPosition
в вашем Adapter
override fun getItemPosition(obj: Any): Int {
if (obj is RefreshData) {
obj.refresh()
}
return super.getItemPosition(obj)
}
Возможно, ваш метод добавления элементов во фрагмент должен быть публичным (помещенным в нужный фрагмент) и должен иметь параметр того же типа, что и selectedItems.
Это сделает его видимым из действия, которое будет иметь массив selectedItems и вуаля..
лучше назови это addItemsFromArray(typeOfSelectedItems[] pSelectedItems)
имя причины addItem()
довольно неописуемо
Редактировать: stackru только что предложил похожую тему:) Проверьте здесь для подробной реализации идеи..:)
1) Создайте обработчик во фрагменте, который вы хотите обновить.
public static Handler sUpdateHandler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
sUpdateHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// call you update method here.
}
};
}
2) В Activity/Fragment/Dialog, где бы вы ни хотели, чтобы был запущен вызов обновления, получите ссылку на этот обработчик и отправьте сообщение (сообщая вашему фрагменту об обновлении)
// Check if the fragment is visible by checking if the handler is null or not.
Handler handler = TaskTabCompletedFragment.sUpdateHandler;
if (handler != null) {
handler.obtainMessage().sendToTarget();
}
Это сработало для меня:
Деятельность:
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
if (fragment.isVisible() && fragment instanceof MyFragment) {
((MyFragment) fragment).refreshContent();
break;
}
}
Фрагмент:
public void refreshContent() {
//refresh content
}
Поскольку ни один из приведенных выше ответов не помог мне, вот мое решение:
Я объединил POSITION_NONE
с нагрузкой на setUserVisibleHint(boolean isVisibleToUser)
вместо onStart()
Как видно здесь: /questions/42337290/viewpagersetoffscreenpagelimit0-ne-rabotaet-dolzhnyim-obrazom/42337292#42337292
в Fragment
:
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// load data here
}else{
// fragment is no longer visible
}
}
и в FragmentStatePagerAdapter
как видно в верхнем ответе от Саймона Дорочака, /questions/40703237/obnovit-fragment-iz-viewpager/40703241#40703241:
@Override
public int getItemPosition(@NonNull Object object) {
return POSITION_NONE;
}
Теперь фрагменты загружают данные в свои представления каждый раз, когда они показываются пользователю.