当使用ViewPager时,如何处理ActionBarActivity Fragments中的AsyncTask?

15

我正在使用ActionBarActivity来创建5个选项卡。我已经使用ViewPager和扩展了FragmentPagerAdapter的SectionsPagerAdapter来在这5个选项卡之间进行滑动。每个选项卡都有一个带有asynctask的fragment,在oncreateView方法中调用。当我在一个fragment中时,其他fragment中的asynctask也会被调用。

我尝试在每个fragment的oncreateView方法中使用toast消息代替asynctask,但是错误的toast消息会在错误的fragment中触发。

onCreate方法代码:

        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());


        mViewPager = (ViewPager) findViewById(R.id.pager_exp); 
        mViewPager.setAdapter(mSectionsPagerAdapter);


        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
                    @Override
                    public void onPageSelected(int position) 
                    {
                        actionBar.setSelectedNavigationItem(position);
                        actionBar.setTitle(getHomePageTitle(position)); 
                    }
                });


        for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) 
        {
            // Create a tab with text corresponding to the page title defined by
            // the adapter. Also specify this Activity object, which implements
            // the TabListener interface, as the callback (listener) for when
            // this tab is selected.
            actionBar.addTab(actionBar.newTab()
                    .setIcon(getPagedrawable(i))
                    .setTabListener(this));

        }

// 适配器类的代码:

public class SectionsPagerAdapter extends FragmentPagerAdapter {

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

    @Override
    public Fragment getItem(int position) {


    switch (position) 
    {
        case 0:
            Fragment1 f1 = new Fragment1(); 
            return f1;

        case 1:
            Fragment2 f2 = new Fragment2(); 
            return f2;

        case 2:
            Fragment3 f3 = new Fragment3();  
            return f3;

        case 3: 
            Fragment4 f4 = new Fragment4();
            return f4;


        case 4: 
            Fragment5 f5 = new Fragment5();
            return f5;

        }

        return null;

    }

    @Override
    public int getCount() {
        // Show 5 total pages.
        return 5;  
    } 


}

Fragment 类代码:

public class F1 extends Fragment 
{
@Override
public void onCreate(Bundle savedInstanceState)  
{
    super.onCreate(savedInstanceState);

}

 @SuppressLint("NewApi")
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) 
{ 
    View view       = inflater.inflate(R.layout.f1,container, false);

     Toast.makeText(getActivity(), "F1", Toast.LENGTH_SHORT).show();
    return view;  
}   
}
4个回答

13

FragmentPagerAdapter会保留除了当前展示的fragment之外的其他fragment,使其处于已恢复的状态。解决方案是实现自定义OnPageChangeListener接口,并创建一个新方法,用于当展示fragment时。

1)创建LifecycleManager接口 该接口将有两个方法,每个ViewPager的Fragment都将实现它们。 这些方法如下:

public interface FragmentLifecycle {

    public void onPauseFragment();
    public void onResumeFragment();

}

2)让每个Fragment实现该接口 为每个类声明添加implements语句:

public class FragmentBlue extends Fragment implements FragmentLifecycle
public class FragmentGreen extends Fragment implements FragmentLifecycle
public class FragmentPink extends Fragment implements FragmentLifecycle

3)在每个片段中实现接口方法 为了检查它是否按预期工作,我将只记录方法调用并显示Toast:

@Override
public void onPauseFragment() {
    Log.i(TAG, "onPauseFragment()");
    Toast.makeText(getActivity(), "onPauseFragment():" + TAG, Toast.LENGTH_SHORT).show(); 
}

@Override
public void onResumeFragment() {
    Log.i(TAG, "onResumeFragment()");
    Toast.makeText(getActivity(), "onResumeFragment():" + TAG, Toast.LENGTH_SHORT).show(); 
}

4) 当ViewPager页面更改时调用接口方法 您可以在ViewPager上设置OnPageChangeListener,并在每次ViewPager显示另一页时获取回调:

pager.setOnPageChangeListener(pageChangeListener);

5)实现OnPageChangeListener调用您自定义的生命周期方法

监听器知道新位置,并可以在PagerAdapter的帮助下调用新Fragment上的接口方法。我可以在此处为新片段调用onResumeFragment(),并在当前片段上调用onPauseFragment()。

我还需要存储当前片段的位置(最初当前位置等于0),因为我不知道用户是从左向右还是从右向左滚动。在代码中看到我的意思:

private OnPageChangeListener pageChangeListener = new OnPageChangeListener() {

    int currentPosition = 0;

    @Override
    public void onPageSelected(int newPosition) {

        FragmentLifecycle fragmentToShow = (FragmentLifecycle)pageAdapter.getItem(newPosition);
        fragmentToShow.onResumeFragment();

        FragmentLifecycle fragmentToHide = (FragmentLifecycle)pageAdapter.getItem(currentPosition);
        fragmentToHide.onPauseFragment();

        currentPosition = newPosition;
    }

    @Override
    public void onPageScrolled(int arg0, float arg1, int arg2) { }

    public void onPageScrollStateChanged(int arg0) { }
};

我没有编写这段代码。完整的教程在这里:http://looksok.wordpress.com/2013/11/02/viewpager-with-detailed-fragment-lifecycle-onresumefragment-including-source-code/


@SomeoneSomewhere 邻近的片段必须被恢复,这样你才能在它们之间滚动。 - LordRaydenMK
1
我弄清楚了 - 适配器真的很重要:它需要实例化所有的片段并保持对它们的引用。getItem() 然后只是简单地返回对象。不幸的是,我之前的实现每次都会创建一个新的 Fragment1、Fragment2 等等。但是,使用 ViewPager 内部的片段实在太复杂了 - 简直是荒谬。当前的实现只适用于少数片段,否则堆使用量会迅速增加。 - Someone Somewhere
@SomeoneSomewhere 请查看FragmentStatePagerAdapter。当页面数量较多时,这个版本的分页器更加有用,更像一个列表视图。当页面对用户不可见时,它们的整个片段可能会被销毁,只保留该片段的保存状态。与FragmentPagerAdapter相比,这使得分页器在保持每个访问页面关联的内存更少的情况下,可能需要更多的开销来切换页面。 - LordRaydenMK

1
使用这个。
viewPager.setOffscreenPageLimit(1); // the number of pages you want to load in background

同时也有一个 ProgressDialogue

这是一个不可行的答案。 - Someone Somewhere

1

你可以尝试使用每个片段的标签,并在页面更改时调用每个片段的方法。

mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
                    @Override
                    public void onPageSelected(int position) {
                        actionBar.setSelectedNavigationItem(position);
                        actionBar.setTitle(getHomePageTitle(position));
                        Fragment f = getFragrmentManager().findFragmentByTag(
                                "" + position);
                        if (f != null)
                            f.refresh();
                    }
                });

和在

         @Override
        public Fragment getItem(int position) {
          Fragment f=null;

        switch (position) 
        {
            case 0:
                f = new Fragment1(); 
                break;

            case 1:
                 f = new Fragment2(); 
                 break;

            case 2:
                 f = new Fragment3();  
                 break;

            case 3: 
                f = new Fragment4();
                break;


            case 4: 
                 f = new Fragment5();
                break;

            }
             f.setTag(""+position);
            return f;

        }

1

ViewPager 会为当前页面相邻的片段创建视图。这也为您提供了加载任何相邻片段所需数据的机会。如果您正在使用 AsyncTasks 加载数据,则使用此功能将导致更好的用户体验。但是,如果您需要在 viewpager 打开特定页面时触发事件,则 LordRaydenMK 的解决方案可行。

请参阅以下主题中我的帖子,以了解在 ViewPager 中使用 AsyncTasks:

AsyncTask runs on each page of the ViewPager


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