如何通过主活动以编程方式更新刷新ViewPager中的Fragment

24
我正在开发一个应用程序,使用Fragment和单个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-基于主Activity接收到的数据创建一个碎片。
  • 2-每当主Activity发生类似于setFragment(...)的操作时,都要更改碎片中的数据(始终由主Activity生成)。

我该如何做到这一点?

使用FragmentAdapter的主Activity

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)成功向片段传递参数,但是如果我尝试更改数据,即使我在适配器mMyFragmentAdapter上调用notifyDataSetChanged(),什么也不会发生,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片段的id(我不确定这种操作),但未调用fragment.updateDisplay();。

你没有搜索太多,是吗?https://dev59.com/pms05IYBdhLWcg3wNPO7 - user
1
@Luksprog 嗯,这与我所要求的有些不同。我需要了解Fragment如何获取由主函数传递的数据,也许我漏掉了什么,但我真的不明白我该如何做到这一点。如果我尝试为我的“LineFragment”创建自定义构造函数,我会收到错误提示:“此片段应提供默认构造函数(一个没有参数的公共构造函数)”。 - AndreaF
@AndreaF 请检查对话框片段 http://developer.android.com/reference/android/app/DialogFragment.html 使用静态的 static MyDialogFragment newInstance(int num),并传递参数,然后使用setget参数。如果您的意思是从主要界面传递数据到片段,那么您可以像这样传递值newInstance(value) - Raghunandan
@Raghunandan 谢谢,但是DialogFragment是一个显示在其活动窗口上方的对话框窗口的片段,这不是我的情况。 - AndreaF
@Raghunandan但是如果Fragment不能有任何不同于默认构造函数的构造函数,我该如何解决我的问题?DialogFragment使用int来标识不同的实例。据我所知,片段无法这样做。 - AndreaF
显示剩余3条评论
5个回答

60

在您的Adapter中,重写getItemPosition方法。

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

之后

viewPager.getAdapter().notifyDataSetChanged() 

应该可以工作。


1
@Nino Handler.. notifyDataSetChanged 在适配器中刷新所有片段还是仅刷新对用户可见的片段? - Raja Jawahar
如果您使用FragmentStatePagerAdapter,那么当前附加的所有片段可能并非所有片段都是如此。 - luckyhandler
@RajaJawahar 这将更新当前片段。 - Surya Reddy

3

在片段中覆盖方法非常简单

@Override

public void setUserVisibleHint(boolean isVisibleToUser) {

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

        actionView();
    }
    else{
        //no
    }

}

这个方法已经过时了,有什么替代方案吗? - Hitesh Tarbundiya

3
我需要了解如何通过主要代码传递数据给片段,也许我遗漏了什么,但我真的不明白我该怎么做。从这里看来,您想在创建片段时传递数据。 如果这是您想要的,则不应该创建自定义构造函数(并且一般情况下对于片段,您应该只有默认的无参数构造函数(因为平台需要在某些时候重新创建片段))。 相反,您应该在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中时才有效(请参见Bundle类的put...方法,所有基本类型和实现Parcelable接口的对象)。在Fragment中,您可以使用getArguments()检索参数。您还可以使Fragment直接从Activity中检索数据。
// in the lifecycle method where you want the data
((MainActivity)getActivity()).getDataForFragment(); // use the data

例如,我有一个Fragment显示图表,当我从主要界面调用setFragment(int interval, Point[] snodePoint)时,ViewPager中的图表应该更新(在我的意图中)。
你似乎自相矛盾。为此,请使用我链接到的问题中的代码,当然,只调用您当前的setFragment()方法不会做任何事情,因为您只更新了两个变量的值,您需要让UI知道更改(例如调用另一个方法来重新创建视图层次结构,在图表视图上设置新值并调用invalidate()等)。
编辑:
我链接到的问题中使用的代码应该可以工作,但是您没有使用正确的代码。您有一个FragmentStatePagerAdapter,但是您正在使用FragmentPagerAdapter的代码,这是行不通的:
FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) mViewPager.getAdapter();
LineFragment f = (LineFragment) a.instantiateItem(pager, thePositionYouWant);

请记住,上面的代码仅适用于可见片段及其两侧的1个片段,其他片段将不可用(由于用户看不到它们或无法立即移动到它们,因此不应更新)。

谢谢你的回答。简单来说,我需要进行以下两个步骤:1-根据MainActivity接收到的数据创建一个Fragment。2-每当在MainActivity中发生“setFragment(...)”之类的操作时更改Fragment的数据。如果我可以使用Bundle实现这一点,你能否给我更多关于如何与Fragment一起使用Bundle的细节? - AndreaF
@AndreaF 我稍微编辑了一下我的回答。你可以查看任何基础教程,了解如何将Bundle用作片段的参数。 - user
@Raghunandan getItem() 方法来自于 FragmentPagerAdapter/FragmentStatePagerAdapter,在该方法中创建了 Fragment,因此无法将其设置为 static - user
@Luksprog 嗯,现在我明白了,我想我误解了问题。我以为这是一个片段。getItem在这里是FragmentPagerAdapter的重写方法。 - Raghunandan
@Luksprog,现在我已经明白了如何从主活动传递参数到片段,但不幸的是,如果我尝试更改数据并调用notifyDataSetChanged(),什么也不会发生。 - AndreaF
显示剩余6条评论

3
如何在主活动中以编程方式更新-刷新View Pager中的Fragment 经过长时间的努力,我找到了适当的解决方案
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 
    }

0

这段代码适用于Kotlin

在Viewpager Adapter中使用以下代码:

  val viewPagerAdapter = ViewpagerAdapter(childFragmentManager)

    view_pager.offscreenPageLimit = 3

在片段内使用这个:-
override fun setMenuVisibility(menuVisible: Boolean) {
    super.setMenuVisibility(menuVisible)
   // hit API
}

override fun onResume() {
    super.onResume()
     // hit API
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接