Android:FragmentPagerAdapter:第一次调用getItem方法两次

25

在我的应用程序中,我使用了ViewPager。例如,

(say main.xml)

<android.support.v4.view.ViewPager
     android:id="@+id/viewPager"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:layout_above="@+id/register_header" />

在FragmentActivity中创建ViewPager对象和ViewPagerAdapter对象(它扩展了FragmentPagerAdapter)。请保留HTML标签。
_mViewPager = (ViewPager) findViewById(R.id.viewPager);

_adapter = new ViewPagerAdapter(getApplicationContext(),
         getSupportFragmentManager());

_mViewPager.setAdapter(_adapter);

而 Adapter 类就像是这样的:

public class ViewPagerAdapter extends FragmentPagerAdapter {
    private Context _context;
    private FragmentManager manager;
    private Fragment f;
    private String classname="ViewPagerAdapter";
    public ViewPagerAdapter(Context context, FragmentManager fm) {
        super(fm);  
        _context=context;
        manager=fm;
        }
    @Override
    public Fragment getItem(int position) {
        Log.i(classname,"getItem called"+position);
        f = new Fragment();
        f=MyFragment.newInstance();
        Bundle args = new Bundle();
        args.putInt("position", position);
        f.setArguments(args);
        return f;
    }

    @Override
    public int getCount() {
        return User.getPageCount();
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }
}

在MyFragment类中,

在onResume方法中,我必须将控件对象存储到共同静态变量中。

public void onResume() {
        super.onResume();
        pageIndex = getArguments().getInt("position");      
        User.useTextview1=textview1;
        User.useTextview2= textview2;
        User.current_pageIndex=pageIndex;



    }

我需要在FragmentActivity中更新从User类获得的TextViews。 我的问题是第一次调用getItem()方法两次 实际上,在模拟器中显示的视图是0索引,但在FragmentActivity中获得了1索引的值。 如果我停止第二次调用getItem()方法,我就能够在FragmentActivity中获得0索引的TextView引用。 请为我提供最佳方法来解决这个问题。 谢谢。

你解决了这个问题吗? - GvSharma
@GVSharma 不,我没有。 - Sridhar
我明白了...我会在这里写下我所做的。 - GvSharma
5个回答

19
FragmentPagerAdapter在启动时会实例化两个Fragments,分别是索引0和索引1。如果你想从当前屏幕上的Fragment获取数据,可以使用setOnPageChangeListenerPager设置监听器来获取当前位置。然后使用SparseArrayWeakReference存储你的fragments。在getItem调用中更新该数组。当onPageSelected被调用时,使用该位置获取正确的Fragment引用并更新User数据。

数组初始化:

private SparseArray<WeakReference<MyFragment>> mFragments = new SparseArray<WeakReference<MyFragment>>(3);

这是一个将对象映射到特定位置的数组。在getItem中,您可以这样使用它:myMemberSparseArrayObject.put(position, new WeakReference<MyFragment>(f)); - Niko
好的,那么FragmentActivity会维护myMemberSparseArrayObject对象,对吗? - Sridhar
如何初始化我的 myMemberSparseArrayObject 对象? - Sridhar
看我的答案如何初始化数组,3 是它可以保存引用的最大值,因为这是具有 Pager 的正常 Fragment 数量。 - Niko
@Niko http://stackoverflow.com/q/18097567/1503130 我遇到了一个问题,你能看一下吗? - Prateek
@Niko 你好,我不是很明白你如何使用SparseArray?你能否更新一下更详细的答案,关于编码等方面。 - Huy Tower

2
在getItem方法中,我编写了以下代码...
@Override
public Fragment getItem(int index) {
    EditFragment frag = new EditFragment();
    Bundle args = new Bundle();
    args.putSerializable("currentItem", itemsList.get(index));
    frag.setArguments(args);
    return (frag);
}

这里的itemsList.get(index)是模型类对象,我将在EditFragment类中使用它。

 @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
    View result = inflater.inflate(R.layout.pager_fragment_layout, container, false);
    image = (ImageView)result.findViewById(R.id.pager_image);
    text = (TextView)result.findViewById(R.id.pager_item_desc);
    ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getActivity()).build();
    ImageLoader.getInstance().init(config);
    imLoader = ImageLoader.getInstance();

            ****NOTE
    final SwapItems itemList = (SwapItems) getArguments().getSerializable("currentItem");

    imagePath = itemList.getPaths().get(0);
    imLoader.displayImage(imagePath,image);
    text.setText(itemList.getItemDescription());

    image.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            Fragment fragment = new ItemImagesFragment();
            FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
            Bundle bundle = new Bundle();
            bundle.putSerializable("val", itemList);
            fragment.setArguments(bundle);
            fragmentTransaction.replace(R.id.content_frame, fragment);
            fragmentTransaction.addToBackStack(null);
            fragmentTransaction.commit();
        }
    });
    return result;
}

注意:在此,我从之前的getItem方法中获取了swapitems模型对象,使用'final'关键字解决了我的问题。之前我使用static修饰符初始化了同一对象。

希望您也能理解清楚。


1
我的问题是在第一次调用getItem()方法时会被调用两次。
这不是问题,这是FragmentPagerAdapter的默认功能。它可以让你从这一页滑动到下一页和上一页。
在模拟器中显示的视图是0号索引,但在FragmentActivity中却获得了1号索引的值。
我遇到了同样的问题,我知道原因所在。这是因为你使用了相同的Fragment在ViewPager中。
如何解决呢?通过使用不同的Fragment来分离它们。

我实现了这个功能...在里面,我需要点击每一页,然后跳转到其他的片段...但是它默认给出了错误的位置...我理解你的回答到一定程度上...你能否更清楚地解释一下? - GvSharma
看起来你又遇到了一个问题,“它默认给出了错误的位置”。你可以使用“viewpager.getCurrentItem(position)”来指示点击某些按钮后需要转到哪个页面。 - Huy Tower
@GVSharma,你得到了什么解决方案?你应用了哪个解决方案? - sam_k
@GVSharma,你解决了我遇到的问题吗?我也遇到了同样的问题。 public Fragment getItem(int i) 调用了两次,导致位置错误。 我有5个选项卡和5个片段。如果(i>0){i = i-1;}可以解决问题,但是问题在于最后一个选项卡或片段没有被调用,因为position=position-1。 - Mushtaq Rahim

1

我遇到了相同的问题 - getItem() 方法被调用两次。我不知道你使用该方法的目的是什么,但我的目的是在新幻灯片出现时触发托管活动中的更改。

首先,getItem() 不是获取新幻灯片出现事件的正确方法。

为此,我在 ViewPager 上使用了 ViewPager.OnPageChangeListener 监听器。

以下是我所做的:

在持有幻灯片碎片的 Activity 中:

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

    // Instantiate a ViewPager and a PagerAdapter.
    mPager = (ViewPager) findViewById(R.id.pager);
    mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
    mPager.setAdapter(mPagerAdapter);
    mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            onSlideChanged(position); // change color of the dots
        }
        @Override
        public void onPageSelected(int position) {}
        @Override
        public void onPageScrollStateChanged(int state) {}
    });
}

onPageScrolled() 每次您的幻灯片出现时都会被调用,作为参数它获取当前显示的幻灯片的位置。好处在于它包括第一次出现时也会被调用,而不仅仅是当幻灯片更改时才调用。

何时可以使用? 例如,如果您有一个带有ViewPager的向导活动,其中您滑动带有一些提示的片段,您很可能希望在下方有一个灰色点的栏,表示幻灯片总数,并且一个点将表示当前幻灯片的颜色不同。 在这种情况下,ViewPager.OnPageChangeListener 和 onPageScrolled() 方法将是最佳选择。

或者如果您有 PREV 和 NEXT 按钮可以更改幻灯片,您想要在第一张幻灯片上禁用 PREV. 按钮并在最后一张幻灯片上禁用 NEXT 按钮。以下是您如何在 onPageScrolled() 方法中执行此操作:

 @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            if(position == 0){
                prevBtn.setTextColor(ContextCompat.getColor(SignatureWizardActivity.this, R.color.main_grey_500));
                prevBtn.setEnabled(false);
            }else{
                prevBtn.setTextColor(ContextCompat.getColor(SignatureWizardActivity.this, R.color.main_blue));
                prevBtn.setEnabled(true);
            }

            if(position == NUM_PAGES -1){
                nextBtn.setTextColor(ContextCompat.getColor(SignatureWizardActivity.this, R.color.main_grey_500));
                nextBtn.setEnabled(false);
            }else{
                nextBtn.setTextColor(ContextCompat.getColor(SignatureWizardActivity.this, R.color.main_blue));
                nextBtn.setEnabled(true);
            }
        }

其中NUM_PAGES是一个常量,表示幻灯片的总数


0

你可以使用这个方法来更新你的视图

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);

    if (isVisibleToUser) {
        // Fetch data or something...
    }
}

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