Как программно обновить-обновить фрагмент в ViewPager с помощью основного действия

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

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

Например, у меня есть этот Fragment, который показывает диаграмму (по моему намерению), когда я вызываю setFragment(int interval, Point[] snodePoint) из основного, диаграмма должна обновляться в ViewPager

public class LineFragment extends Fragment {
    static int interval;
    static Point snodePoints[];


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final View v = inflater.inflate(R.layout.fragment_linegraph, container,
                false);
        Bundle bundle = this.getArguments();
        ChartConfig cg = bundle.getParcelable("FragmentData");

        snodePoints = cg.getPoints();
        interval= cg.getInterval();
        Line l = new Line();

        for(int i=0; i<snodePoints.length; i++){
          LinePoint p = new LinePoint();
          Point sp=snodePoints[i];
          p.setX(sp.getX());
          p.setY(sp.getY());
          l.addPoint(p)
        }
        l.setColor(getResources().getColor(R.color.green));

        LineGraph li = (LineGraph) v.findViewById(R.id.linegraph);
        li.addLine(l);
        li.setRangeY(0, interval);
        li.setLineToFill(0);

        li.setOnPointClickedListener(new OnPointClickedListener() {

            @Override
            public void onClick(int lineIndex, int pointIndex) {
                // TODO Auto-generated method stub

            }

        });

        return v;
    }
}

В нескольких словах мне нужно

  • 1-создать фрагмент на основе данных, полученных основной активностью.
  • 2-изменить данные фрагмента каждый раз, когда в основном действии происходит что-то вроде setFragment(...) с новыми данными (всегда генерируемыми основным).

Как я мог это сделать?

Основное действие с FragmentAdapter

public class MainActivity extends FragmentActivity {

ViewPager mViewPager;
MyFragmentAdapter mMyFragmentAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mViewPager = (ViewPager) findViewById(R.id.view_pager);
    final ActionBar bar = this.getActionBar();
    bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    LineFragment lineFrag = new LineFragment();
    BarFragment barFrag = new BarFragment();
    PieFragment pieFrag = new PieFragment();

    mMyFragmentAdapter = new MyFragmentAdapter(this, mViewPager);
    mMyFragmentAdapter.addTab(bar.newTab().setText("Line"), LineFragment.class,
            null, lineFrag);
    mMyFragmentAdapter.addTab(bar.newTab().setText("Bar"), BarFragment.class,
            null, barFrag);
    mMyFragmentAdapter.addTab(bar.newTab().setText("Pie"), PieFragment.class,
            null, pieFrag);
    mViewPager.setOffscreenPageLimit(mMyFragmentAdapter.getCount() - 1);
}

public static class MyFragmentAdapter extends FragmentStatePagerAdapter implements
        ActionBar.TabListener, ViewPager.OnPageChangeListener {

    private final MainActivity mContext;
    private final ActionBar mActionBar;
    private final ViewPager mViewPager;
    private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
    private final ArrayList<Fragment> mFrag = new ArrayList<Fragment>();

    static final class TabInfo {
        private final Class<?> clss;
        private final Bundle args;

        TabInfo(Class<?> _class, Bundle _args) {
            clss = _class;
            args = _args;
        }
    }

    public MyFragmentAdapter(MainActivity activity, ViewPager pager) {
        super(activity.getSupportFragmentManager());
        mContext = activity;
        mActionBar = activity.getActionBar();
        mViewPager = pager;
        mViewPager.setAdapter(this);
        mViewPager.setOnPageChangeListener(this);
    }

    public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args,
            Fragment frag) {
        TabInfo info = new TabInfo(clss, args);
        tab.setTag(info);
        tab.setTabListener(this);
        mTabs.add(info);
        mFrag.add(frag);
        mActionBar.addTab(tab);
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return mTabs.size();
    }

    @Override
    public Fragment getItem(int position) {
        TabInfo info = mTabs.get(position);
        Bundle args = new Bundle();
        ChartConfig cg = new ChartConfig(interval, snodePoint);
        args.putParcelable("FragmentData", cg);
        Fragment f = mFrag.get(position);
        f.setArguments(args);

        return f;
    }

    @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) {
    }

    public void onTabSelected(Tab tab, android.app.FragmentTransaction ft) {
        Object tag = tab.getTag();
        for (int i = 0; i < mTabs.size(); i++) {
            if (mTabs.get(i) == tag) {
                mViewPager.setCurrentItem(i);
            }
        }
    }

    public void onTabUnselected(Tab tab, android.app.FragmentTransaction ft) {
    }

    @Override
    public void onTabReselected(Tab tab, android.app.FragmentTransaction ft) {
    }

}

}

В первый раз, когда я создаю фрагмент, основное действие успешно передает параметры фрагменту через переопределенный getItem(int position), но если я попытаюсь изменить данные, а также если я попытаюсь вызвать notifyDataSetChanged() на адаптере mMyFragmentAdapter, ничего не произойдет, и фрагменты в ViewPager продолжают использовать те же данные, которые были переданы при создании.

ИЗМЕНИТЬ

Следуя некоторому частичному коду, увиденному в другом вопросе об обновлении фрагмента, я также пробовал это

public void update() {
        try{
            LineFragment fragment = 
                      (LineFragment) getSupportFragmentManager().findFragmentByTag(
                                   "android:switcher:"+R.id.view_pager+":0");
                  if(fragment != null)
                  {
                     if(fragment.getView() != null) 
                     {

                        fragment.updateDisplay();
                     }
                  }
        }
        catch(RuntimeException e){
            e.printStackTrace();
        }

    }

Предполагая, что 0 - это идентификатор первого фрагмента Fragment-Tab (я не уверен в этом виде операции), но fragment.updateDisplay(); не вызывается.


person AndreaF    schedule 02.11.2013    source источник
comment
Вы не слишком много искали, не так ли? stackoverflow.com/ вопросы/7379165/   -  person user    schedule 02.11.2013
comment
@Luksprog ммм Это немного отличается от того, что я просил. Мне нужно понять, как Fragment может получить данные, передаваемые main, может быть, я что-то упустил, но на самом деле я не понял, как я мог это сделать. Если я попытаюсь создать пользовательский конструктор для моего «LineFragment», я получаю сообщение об ошибке «Этот фрагмент должен предоставить конструктор по умолчанию (общедоступный конструктор без аргументов)».   -  person AndreaF    schedule 02.11.2013
comment
Фрагмент диалогового окна проверки @AndreaF developer.android.com/reference/android/app/DialogFragment .html использует статический static MyDialogFragment newInstance(int num), которому передается параметр, а затем использует аргументы set и get. и вы передаете значение типа newInstance(value), если это то, что вы имеете в виду, передавая данные из основного во фрагмент   -  person Raghunandan    schedule 02.11.2013
comment
@Raghunandan Спасибо, но DialogFragment - это фрагмент, который отображает диалоговое окно, плавающее поверх окна своей активности, это не мой случай.   -  person AndreaF    schedule 02.11.2013
comment
Концепция @AndreaF такая же, как и для фрагмента   -  person Raghunandan    schedule 02.11.2013
comment
@Raghunandan Но если у Фрагмента не может быть конструктора, отличного от конструктора по умолчанию, как я могу решить свою проблему? DialogFragment использует int для определения другого места. Из того, что я знаю, фрагмент не может.   -  person AndreaF    schedule 02.11.2013
comment
@AndreaF DialogFragment не является фрагментом? Но я думаю, это не то, что вам нужно, кажется, ответ ниже - это то, что вам нужно. Однако ваше понимание фрагмента неверно   -  person Raghunandan    schedule 02.11.2013
comment
@Raghunandan хорошо, ты прав, теперь я понял, что ты имеешь в виду   -  person AndreaF    schedule 02.11.2013


Ответы (4)


В вашем адаптере переопределите getItemPosition

@Override
public int getItemPosition(Object object) {
    // POSITION_NONE makes it possible to reload the PagerAdapter
    return POSITION_NONE;
}

После этого

viewPager.getAdapter().notifyDataSetChanged() 

должно сработать.

person luckyhandler    schedule 22.10.2014
comment
@Nino Handler .. Обновляет ли этот notifyDataSetChanged в адаптере все фрагменты или единственный фрагмент, который будет виден пользователю .. - person Raja Jawahar; 09.08.2017
comment
все, что в настоящее время подключено - если вы используете FragmentStatePagerAdapter, это может быть не для всех фрагментов - person luckyhandler; 10.08.2017
comment
@RajaJawahar, это обновит текущий фрагмент - person Surya Reddy; 23.06.2021

Мне нужно понять, как Fragment может получить данные, передаваемые main, может быть, я что-то упустил, но на самом деле я не понял, как я мог это сделать.

Отсюда кажется, что вы хотите передать данные в момент создания файла Fragment. Если это то, что вы хотите, вам не следует создавать пользовательский конструктор (и, как правило, для Fragment у вас должен быть только конструктор без аргументов по умолчанию (потому что платформа нуждается в нем для воссоздания Fragment в определенное время)).

Вместо этого вы должны передать данные в Bundle:

@Override
public Fragment getItem(int position) {
     switch (position) {
        //...
        Bundle args = new Bundle();
        args.put("a_string_representing_theKey", data)
        LineFragment f = new LineFragment();
        f.setArguments(args);
        return f;
        //...
     } 
}

Это будет работать только в том случае, если данные можно поместить в Bundle (см. метод put... класса Bundle, все примитивы + объекты, которые являются Parcelable). Затем во фрагменте вы можете получить аргументы, используя getArguments().

Вы также можете заставить Fragment извлекать данные непосредственно из Activity:

// in the lifecycle method where you want the data
((MainActivity)getActivity()).getDataForFragment(); // use the data

Например, у меня есть этот фрагмент, который показывает диаграмму (по моему намерению), когда я вызываю setFragment(int interval, Point[] snodePoint) из основного, диаграмма должна обновляться в ViewPager

Вы как будто сами себе противоречите. Для этого используйте код из вопроса, на который я ссылался, конечно, просто вызов вашего текущего метода setFragment() ничего не сделает, поскольку вы просто обновите значение двух переменных, вам нужно будет сообщить пользовательскому интерфейсу об изменении (например, вызов другого метод для воссоздания иерархии представлений, установки новых значений в представлении диаграммы и вызова invalidate() и т. д.).

Редактировать:

Код, использованный в вопросе, на который я ссылался, должен работать, но вы используете неправильный код. У вас есть FragmentStatePagerAdapter, но вы используете код для FragmentPagerAdapter, который не будет работать:

FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) mViewPager.getAdapter();
LineFragment f = (LineFragment) a.instantiateItem(pager, thePositionYouWant);

Имейте в виду, что приведенный выше код будет работать только для видимого фрагмента плюс 1 фрагмент с каждой стороны, остальные фрагменты будут недоступны (и поскольку пользователь их не видит или не может сразу перейти к ним, они не должны не обновляться).

person user    schedule 02.11.2013
comment
Спасибо за ответ. В двух словах мне нужно 1-создать фрагмент на основе данных, полученных основной активностью. 2- менять данные фрагмента каждый раз, если в основном происходит что-то вроде 'setFragment(...)'. Если я могу сделать это с помощью Bundle, не могли бы вы дать мне более подробную информацию о том, как использовать это с Fragment? - person AndreaF; 02.11.2013
comment
@AndreaF Я немного отредактировал свой ответ. Вы можете просмотреть любые базовые руководства, чтобы узнать, как использовать Bundle в качестве аргументов фрагмента. - person user; 02.11.2013
comment
@Raghunandan Метод getItem() взят из FragmentPagerAdapter/FragmentStatePagerAdapter, где он создает Fragment, и вы не можете сделать его static. - person user; 02.11.2013
comment
@Luksprog да, теперь понял, наверное, я неправильно понял вопрос. Я думал это фрагмент. getItem здесь - переопределенный метод FragmentPagerAdapter - person Raghunandan; 02.11.2013
comment
@Luksprog теперь я понял, как передать параметры из основного действия во фрагмент, но, к сожалению, если я попытаюсь изменить данные и вызвать notifyDataSetChanged(), ничего не произойдет - person AndreaF; 03.11.2013
comment
@AndreaF Вы звоните notifyDataSetChanged() на MyFragmentAdapter, чтобы обновить LineFragment? Если да, не делайте этого, используйте код из вопроса, на который я ссылался, чтобы сослаться на LineFragment, вызовите метод setFragment(), чтобы установить переменные и обновить данные (например, сбросив представление и перестроив его). - person user; 03.11.2013
comment
@Luksprog Пожалуйста, не могли бы вы дать мне образец? Я попытался выполнить шаг по ссылке, но безуспешно. Я также пытался использовать myadapter.destroyItem(mViewPager, 0, LineFragment.class);, но, возможно, я передал неправильные параметры в методе destroyItem, потому что происходит сбой. - person AndreaF; 03.11.2013
comment
Я обновил вопрос, указав более подробную информацию о моей основной деятельности. - person AndreaF; 04.11.2013
comment
@AndreaF Вы хотите обновить данные фрагментов адаптера, а не заменить сами фрагменты. В вопросе, на который я ссылался, есть некоторый код о том, как получить доступ к фрагменту из ViewPager, чтобы вызвать для него методы обновления. - person user; 04.11.2013
comment
@Luksprog правильно, я хочу обновить фрагмент, но, поскольку у меня не получилось с этим подходом, я также попытался заменить фрагменты. Однако по-прежнему не могут получить доступ и обновить фрагмент. Метод принятого ответа в ссылке не работает (непонятно, как и где реализован HomeListFragment) 2-й верхний метод ответа возвращает NullPointerException, если я пытаюсь получить какие-либо данные из объявленного FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) pager.getAdapter();, а если я пытаюсь что-то вроде a.startUpdate(mViewPager); не ничего не случится - person AndreaF; 05.11.2013
comment
@Luksprog См. мой связанный с этим вопрос: stackoverflow.com/questions /24833912/ - person Dr.jacky; 19.07.2014

очень просто переопределить метод во фрагменте

@Override

public void setUserVisibleHint(boolean isVisibleToUser) {

    super.setUserVisibleHint(isVisibleToUser);
    if(isVisibleToUser){

        actionView();
    }
    else{
        //no
    }

}
person Issac Balaji    schedule 24.11.2016
comment
этот метод устарел, поэтому есть альтернатива? - person Hitesh Tarbundiya; 27.04.2020

Как программно обновить-обновить фрагмент в представлении пейджера с помощью основного действия после долгой борьбы. Я нашел правильное решение

tablayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                
                refreshTab(tab.getPosition());// create a method
                viewpager.setCurrentItem(tab.getPosition());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });
 private void refreshTab(int position) {
    switch (position) {
        case 0:
    
  
            break;
        case 1:
            
            CompaniesInterest comP1 = (CompaniesInterest) viewpager.getAdapter().instantiateItem(viewpager, viewpager.getCurrentItem());
            comP1.refreshpage();
            break;
        case 2:
            
            TotalSelectedCompanies toTal = (TotalSelectedCompanies) viewpager.getAdapter().instantiateItem(viewpager, viewpager.getCurrentItem());
            toTal.refreshpage();
           
    }
}
// Create a interface for refresh data for fragmen
public interface Refreshpage {
    void refreshpage();
}
// implements Refreshpage interface in both fragment TotalSelectedCompanies and CompaniesInterest
//
@Override`enter code here`
    public void refreshpage() {
        // do what you want to refresh 
    }
person Shiv Kumar    schedule 27.01.2017