【问题标题】:NestedScrollView inside fragment not programatically scrolling片段内的 NestedScrollView 不以编程方式滚动
【发布时间】:2020-07-18 01:03:02
【问题描述】:

我有一个带有 viewpager2 和 FragmentStateAdapter 的 tablayout。我有 3 个选项卡,每个选项卡都有一个包含线性布局的 NestedScrollView:

<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:id="@+id/scrollView"
        android:focusableInTouchMode="true"
        android:layout_height="wrap_content">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:descendantFocusability="blocksDescendants">

            /* ... */

       </LinearLayout>
</androidx.core.widget.NestedScrollView>

当我在 tablayout 中的选项卡之间切换时,滚动视图不会从顶部开始。在 viewPager 的每个 Fragment 的 onViewCreated() 方法中,我添加了以下几行,但是滚动视图仍然没有滚动到顶部,它从它离开的地方开始。

public static class MyFragment extends Fragment {

        private NestedScrollView scrollView;

        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                                 @Nullable Bundle savedInstanceState) {
            return inflater.inflate(R.layout.fragment, container, false);
        }

        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            scrollView = view.findViewById(R.id.scrollView);
            scrollView.smoothScrollTo(0, 0);
        }
}

【问题讨论】:

    标签: android-studio android-nestedscrollview


    【解决方案1】:

    @star4z 的解决方案应该可行,但有一个更简单的选择。您可以做什么,为此使用 FragmentPagerAdapter 并将其行为设置为 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT

    每次您更改标签时,都会调用onResume,您可以在那里调用您的scrollView.smoothScrollTo(0, 0);

    public class MyAdapter extends FragmentPagerAdapter {
    ...
            public MyAdapter(FragmentManager fm) {
                super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
            }
    ...
        
    }
    

    然后在你的Fragment:

    public class MyFragment extends Fragment {
    
    ...
    
        public void onResume() {
            super.onResume();
            scrollView.smoothScrollTo(0, 0);
        }
    
    ...
    
    }
    

    【讨论】:

    • 唯一的问题是我将 viewPager2 与 FragmentStateAdapter 一起使用。你会怎么做呢?
    【解决方案2】:

    onViewCreated 仅在首次创建 Fragments 时调用。 ViewPager 不会更改 Fragments 的状态。你可以想象隐藏的Fragments 被存储在当前可见的Fragment 的两侧,可见。

    我通过在TabLayout 更改时设置回调来完成。 TabLayout 是监听器所在的位置。然后你可以让它在你的FragmentPagerAdapter 中调用一个回调方法。在每个Fragment 中,您应该实现一个返回回调的方法。在FragmentPagerAdapter 中,使用getItem(int position) 将来自Fragment 的回调存储在Map 中。适配器中的回调方法从TabLayout中获取位置

    首先,在您的 Fragments 中,为它们提供一个方法,该方法返回某种回调(我使用了 Runnable),该回调将从适配器中调用。

    public class PlaceHolderFragment extends Fragment {
    
    ...
    
        public Runnable getOnTabChangedListener() {
            return new Runnable() {
                @Override
                public void run() {
                    scrollView.smoothScrollTo(0, 0);
                }
            };
        }
    
    ...
    
    }
    

    然后,在您的FragmentPagerAdapter(我叫我的SectionsPagerAdapter)中,将回调存储在getItem() 方法中的映射中,然后添加自定义回调。

    public class SectionsPagerAdapter extends FragmentPagerAdapter {
    
        ...
    
        HashMap<Integer, Runnable> tabChangedCallbacks = new HashMap<>();
    
        ...
    
        @Override
        public Fragment getItem(int position) {
            // getItem is called to instantiate the fragment for the given page.
            // Return a PlaceholderFragment (defined as a static inner class below).
            PlaceholderFragment fragment = PlaceholderFragment.newInstance(position + 1);
            tabChangedCallbacks.put(position, fragment.getOnTabChangedListener());
            return fragment;
        }
    
        public void onTabChanged(int position) {
            tabChangedCallbacks.get(position).run();
        }
    
        ...
    
    }
    

    在包含 TabLayout 的 Activity 中,您需要在 onCreate() 方法中对 TabLayout 调用 addOnTabSelectedListener()

    protected void onCreate(Bundle savedInstanceState) {
    
        ...
    
        final SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
        final ViewPager viewPager = findViewById(R.id.view_pager);
        viewPager.setAdapter(sectionsPagerAdapter);
        TabLayout tabs = findViewById(R.id.tabs);
        tabs.setupWithViewPager(viewPager);
        tabs.addOnTabSelectedListener(
                new TabLayout.ViewPagerOnTabSelectedListener(viewPager) {
                    @Override
                    public void onTabSelected(TabLayout.Tab tab) {
                        super.onTabSelected(tab);
                        sectionsPagerAdapter.onTabChanged(tab.getPosition());
                    }
                });
    
        ...
    
    }
    

    编辑

    原理是一样的,但要使用ViewPager2FragmentStateAdapter,您需要进行以下更改:

    Fragment 保持不变。

    适配器在功能上是相同的,但有一些更新的方法:

    public class SectionsStateAdapter extends FragmentStateAdapter {
    
        HashMap<Integer, Runnable> tabChangedCallbacks = new HashMap<>();
    
        ...
    
        public void onTabChanged(int position) {
            tabChangedCallbacks.get(position).run();
        }
    
        @NonNull
        @Override
        public Fragment createFragment(int position) {
            PlaceholderFragment fragment = PlaceholderFragment.newInstance(position + 1);
            tabChangedCallbacks.put(position, fragment.getOnTabChangedListener());
            return fragment;
        }
    
        @Override
        public int getItemCount() {
            return 2;
        }
    
        ...
    }
    

    活动变化最大:

    public class MainActivity extends AppCompatActivity {
    
        ...
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
    
            ...
    
            final SectionsStateAdapter sectionsStateAdapter = new SectionsStateAdapter(this);
            final ViewPager2 viewPager = findViewById(R.id.view_pager);
            viewPager.setAdapter(sectionsStateAdapter);
            TabLayout tabs = findViewById(R.id.tabs);
            // connect the TabLayout to the ViewPager2
            new TabLayoutMediator(tabs, viewPager, new TabLayoutMediator.TabConfigurationStrategy() {
                @Override
                public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
                    // set tab text, etc
                }
            }).attach();
            // set the change listener on the ViewPager2
            viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
                @Override
                public void onPageSelected(int position) {
                    super.onPageSelected(position);
                    sectionsStateAdapter.onTabChanged(position);
                }
            });
    
            ...
    
        }
    }
    

    请注意,唯一真正的变化是 ViewPager2 不需要 TabLayout 来处理页面更改。如果您愿意,您仍然可以使用TabLayout.addOnTabSelectedListener(),但您必须实现自己的TabLayout.OnTabSelecterListener,因为TabLayout.ViewPagerOnTabSelectedListener 不适用于ViewPager2

    例如:

    tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
                @Override
                public void onTabSelected(TabLayout.Tab tab) {
                    sectionsStateAdapter.onTabChanged(tab.getPosition());
                }
    
                @Override
                public void onTabUnselected(TabLayout.Tab tab) {
    
                }
    
                @Override
                public void onTabReselected(TabLayout.Tab tab) {
    
                }
            });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-09
      • 2016-02-25
      • 2018-06-01
      • 1970-01-01
      • 2019-11-24
      • 2020-06-01
      • 1970-01-01
      相关资源
      最近更新 更多