从Activity中访问ViewPager Fragment的方法

3
这是我的情景:
我在Activity中有一个ViewPager,其中包含6个Fragment。我禁用了通过手指滑动进行分页的功能,因此每当我想要滑动时,我会使用相关按钮并执行以下操作:
viewPager.setCurrentItem(viewPager.getCurrentItem() + 1, true);

在每个被滑动的碎片(在滑动完成后)中,我想向我的服务器发送一个GET请求,获取一些数据并显示在该碎片中。为了实现这个目的: 第一种方法:我在我的碎片中使用了以下代码,当碎片可见时立即运行:
@Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if(isVisibleToUser)
        {
          sendGetRequest();
        }
}

但是,这里有一个问题:当片段可见时,setUserVisibleHint正好执行,因此滑动动画会出现一些延迟(不够流畅)。

因此,我采用了第二种方法: 我在托管活动中向ViewPager添加了一个OnPageChangeListener(),如下所示:

viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            int CurrentPossition = 0;
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }
            @Override
            public void onPageSelected(int position) {
                CurrentPossition = position;
            }
            @Override
            public void onPageScrollStateChanged(int state) {
                if(state == ViewPager.SCROLL_STATE_IDLE && CurrentPossition != 0){
                    Toast.makeText(getBaseContext(),"finished" , Toast.LENGTH_SHORT).show();

                    try{
                        new fragment_two().sendGetRequest();;
                    }catch(Exception ex){
                        ex.printStackTrace();
                    }
                }
            }
        });

它的工作很好,当滑动完成时,toast立即显示出来,但与完全可见的片段不同,当sendGetRequest()运行时,我遇到了NullPointerException。

以下是StackTrace :

04-08 20:15:37.840 12848-12848/com.example.mohamad.travelagency W/System.err: java.lang.NullPointerException
04-08 20:15:37.850 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.content.ContextWrapper.getApplicationInfo(ContextWrapper.java:152)
04-08 20:15:37.850 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.view.ContextThemeWrapper.getTheme(ContextThemeWrapper.java:103)
04-08 20:15:37.850 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.app.AlertDialog.resolveDialogTheme(AlertDialog.java:143)
04-08 20:15:37.850 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.app.AlertDialog.<init>(AlertDialog.java:98)
04-08 20:15:37.850 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.app.ProgressDialog.<init>(ProgressDialog.java:77)
04-08 20:15:37.850 12848-12848/com.example.mohamad.travelagency W/System.err:     at com.example.mohamad.travelagency.fragment_two.GetServetData_L1(fragment_two.java:458)
04-08 20:15:37.850 12848-12848/com.example.mohamad.travelagency W/System.err:     at com.example.mohamad.travelagency.MainActivity$1.onPageScrollStateChanged(MainActivity.java:124)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.support.v4.view.ViewPager.dispatchOnScrollStateChanged(ViewPager.java:1811)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.support.v4.view.ViewPager.setScrollState(ViewPager.java:404)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.support.v4.view.ViewPager.access$000(ViewPager.java:91)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.support.v4.view.ViewPager$3.run(ViewPager.java:250)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.view.Choreographer.doCallbacks(Choreographer.java:574)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.view.Choreographer.doFrame(Choreographer.java:543)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.os.Handler.handleCallback(Handler.java:733)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:95)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.os.Looper.loop(Looper.java:136)
04-08 20:15:37.870 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5271)
04-08 20:15:37.870 12848-12848/com.example.mohamad.travelagency W/System.err:     at java.lang.reflect.Method.invokeNative(Native Method)
04-08 20:15:37.870 12848-12848/com.example.mohamad.travelagency W/System.err:     at java.lang.reflect.Method.invoke(Method.java:515)
04-08 20:15:37.870 12848-12848/com.example.mohamad.travelagency W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:851)
04-08 20:15:37.870 12848-12848/com.example.mohamad.travelagency W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:667)
04-08 20:15:37.870 12848-12848/com.example.mohamad.travelagency W/System.err:     at dalvik.system.NativeStart.main(Native Method)

任何想法都可以。最好的问候。

答案: 丹尼尔·纽金特的代码很好用,除了在发送GET请求时显示ProgressDialog之外,我使用了:

final ProgressDialog dialog = new ProgressDialog(new MainActivity());

这段代码也返回了NullPointerException,我把它移除后现在运行良好。


在onPageSelected()中通过getItem()获取添加到您的ViewPager适配器中的片段。检查是否为空。如果不为空,则执行您的服务器调用。 - Sayem
当片段完全可见时,是否有可能使空片段变得可见并显示其静态视图? - M.Armoun
你的sendGetRequest()里面有什么?Logcat呢? - Yauraw Gadav
@Gauraw Yadav:在第一种方法中,它是一个AsyncTask,在第二种方法中为了提高速度,我使用了Square的Retrofit库。 - M.Armoun
1
@MohamadArmoon,你永远不应该调用 new Activity(),那行代码应该是final ProgressDialog dialog = new ProgressDialog(getActivity());。参见这里:https://dev59.com/-GAf5IYBdhLWcg3wSQ8z - Daniel Nugent
2个回答

3
使用ViewPager.OnPageChangeListener是正确的方式,但您需要稍微重构适配器以保留包含在FragmentPagerAdapter中的每个Fragment的引用。
您可以使用适配器中的instantiateItem()方法覆盖来实现这一点,以下是一个简化的示例:
 class PagerAdapter extends FragmentPagerAdapter {
        String tabTitles[] = new String[] { "One", "Two", "Three", "Four"};
        Context context;
        
        //This will contain your Fragment references:
        public Fragment[] fragments = new Fragment[tabTitles.length];
        
        public PagerAdapter(FragmentManager fm, Context context) {
            super(fm);
            this.context = context;
        }
        @Override
        public int getCount() {
            return tabTitles.length;
        }
        @Override
        public Fragment getItem(int position) {
            switch (position) {
            case 0:
                return new FragmentOne();
            case 1:
                return new FragmentTwo();
            case 2:
                return new FragmentThree();   
            case 3:
                return new FragmentFour();
            }
            return null;
        }

        //This populates your Fragment reference array:
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
            fragments[position]  = createdFragment;
            return createdFragment;
        }
        
        @Override
        public CharSequence getPageTitle(int position) {
            // Generate title based on item position
            return tabTitles[position];
        }         
 }

然后,不要创建一个新的Fragment,而是使用适配器中包含的一个:

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
  @Override
  public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }

  @Override
  public void onPageSelected(int position) {

        // do this instead, assuming your adapter reference
        // is named mAdapter:
        Fragment frag = mAdapter.fragments[position];
        if (frag != null && frag instanceof FragmentTwo) {
          ((FragmentTwo)frag).sendGetRequest();
        }
  }

  @Override
  public void onPageScrollStateChanged(int state) {  }
});

请注意,如果您在适配器中使用不同的Fragment类,则可以实现一个定义了sendGetRequest()的接口,并在每个Fragment类中实现sendGetRequest()方法。
如果不采用接口的方法,则需要像上面的示例中一样将Fragment强制转换为自己的Fragment类型。
if (frag instanceof FragmentTwo) {
    ((FragmentTwo)frag).sendGetRequest();
}

更新

如果要使用ViewPager2和Kotlin,以下是示例代码:

    viewPager.registerOnPageChangeCallback(
            object: ViewPager2.OnPageChangeCallback() {
                override fun onPageSelected(position: Int) {
                    super.onPageSelected(position)
                    val frag: Fragment = mAdapter.fragments[position]
                    if (frag != null && frag is FragmentTwo) {
                        (frag as FragmentTwo).sendGetRequest()
                    }
                }
            }
    )

感谢Daniel Nugent提供的完整答案。但是还有一个问题:<br/>mAdapter.fragments[CurrentPossition];返回的是Fragment,而我想要的是继承自Fragment的fragment_two,我该如何将其转换为fragment_two - M.Armoun
我又遇到了同样的错误,我之前使用的是FragmentStatePagerAdapter,现在我改成了FragmentPagerAdapter,但是还是出现了NullPointerException。 - M.Armoun

0

不要在视图页监听器中调用sendGetRequest()方法,而是在fragment_two片段类的onCreateView方法中调用GET请求。这样做的原因是通过片段的生命周期事件确保fragment_two中的所有视图都已经正确初始化。


正如我在第一种方法中提到的,当我在onCreateView或setUserVisibleHint中使用sendGetRequest()时,一切都运行良好,但问题在于过渡不够平滑。这种不流畅可能是因为在请求开始时显示ProgressDialog,但它应该被显示。 - M.Armoun
当用户看到片段时,为什么要立即调用GET请求?默认情况下,视图页预加载当前片段左侧和右侧的片段。为了提供更好的用户体验,我会在onCreateView中预加载数据,这样您就不必显示进度对话框。 - Anthony Ng

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