ViewPager2在滑动时出现闪烁/重新加载

3
我正在尝试使用新的ViewPager2构建Android应用程序。 我已添加了两个ViewPager,由视图分隔,当您滑动时,两个ViewPager都应该移动。 两个ViewPager都在正确移动,但手势完成后,滑动的视图会闪烁,未滑动的视图重新加载,如所附的gif所示。 这是我的Activity,ViewPagerAdapter和Fragment代码。 感谢任何帮助

enter image description here

    public class MainActivity extends FragmentActivity {

    ActivityMainBinding viewBinding;

    MyPager adapter1, adapter2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        viewBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        List<String> data = populateData();

        adapter1 = new MyPager(this, data);
        adapter2 = new MyPager(this, data);

        viewBinding.viewPager.setAdapter(adapter1);
        viewBinding.viewPager2.setAdapter(adapter2);

        viewBinding.viewPager2.setOffscreenPageLimit(data.size());
        viewBinding.viewPager.setOffscreenPageLimit(data.size());

        viewBinding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {

            @Override
            public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
                viewBinding.viewPager2.scrollTo(positionOffsetPixels, 0);
            }

            @Override
            public void onPageScrollStateChanged(final int state) {
                viewBinding.viewPager2.setCurrentItem(viewBinding.viewPager.getCurrentItem(), true);
            }
        });
        viewBinding.viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {

            @Override
            public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
                viewBinding.viewPager.scrollTo(positionOffsetPixels, 0);
            }

            @Override
            public void onPageScrollStateChanged(final int state) {
                viewBinding.viewPager.setCurrentItem(viewBinding.viewPager2.getCurrentItem(), true);
            }
        });

    }
    private List<String> populateData() {
        List<String> data = new ArrayList<>();
        for (int x = 0; x < 10; x++) {
            String derril = "derril " + x;
            data.add(derril);
        }
        return data;
    }
}

public class MyPager extends FragmentStateAdapter {

    List<String> data;

    public MyPager(@NonNull FragmentActivity fragmentActivity, List<String> data) {
        super(fragmentActivity);
        this.data = data;
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return DerrilFragment.newInstance(data.get(position));
    }

    @Override
    public int getItemCount() {
        return data.size();
    }
}

public class DerrilFragment extends Fragment {

    PizzaBinding viewBinding;

    String data;

    private DerrilFragment(String data) {
        this.data = data;
    }

    public static DerrilFragment newInstance(String data) {
        return new DerrilFragment(data);
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        viewBinding = DataBindingUtil.inflate(inflater, R.layout.pizza, container, false);

        viewBinding.text.setText(data);

        return viewBinding.getRoot();

    }
}

1
为什么你有两个ViewPagers?你只需要一个就可以了。 - Viatcheslav Ehrmann
因为viewpager是由一个视图分隔开的。如果您能在一个viewpager中完成它,我很想听听如何实现。 - dlucci
看起来你的ViewPagers彼此叠放在一起。所以对我来说没有意义。 - Viatcheslav Ehrmann
它有什么不合理的地方?一个ViewPager在顶部,由一个视图分隔,然后是第二个ViewPager。 - dlucci
5个回答

1
您可以通过RecyclerView来实现。
class MainActivity : AppCompatActivity() {
    private var isFirstFocused = false

    private val helper: SnapHelper = LinearSnapHelper()
    private val helper2: SnapHelper = LinearSnapHelper()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        recycler1.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                if (isFirstFocused) recycler2.scrollBy(dx, dy)
            }
        })
        recycler2.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                if (!isFirstFocused) recycler1.scrollBy(dx, dy)
            }
        })
        recycler2.setOnTouchListener { v, event ->
            if (event.action  != null && event.action == MotionEvent.ACTION_DOWN ) {
                isFirstFocused = false
                return@setOnTouchListener  true
            }
            return@setOnTouchListener false
        }
        recycler1.setOnTouchListener { v, event ->
            if (event.action  != null && event.action == MotionEvent.ACTION_DOWN ) {
                isFirstFocused = true
                return@setOnTouchListener  true
            }
            return@setOnTouchListener false
        }
        helper.attachToRecyclerView(recycler1)
        helper2.attachToRecyclerView(recycler2)
        val manager = LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)
        val manager2 = LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)
        recycler1.layoutManager = manager
        recycler2.layoutManager = manager2
        recycler2.adapter = Adapter(items)
        recycler1.adapter = Adapter(items)
    }

    private val items = listOf("One", "Two", "Three")

}

class Adapter(val list: List<String>) : RecyclerView.Adapter<Adapter.Holder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val view = LayoutInflater.from(parent.context).inflate(
            R.layout.activity_listview,
            parent,
            false
        )
        return Holder(view)
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        holder.itemView.text_view.text = list[position]
    }

    override fun getItemCount() = list.size

    class Holder(view: View) : RecyclerView.ViewHolder(view)
}

0
你可以尝试使用 adapter.setOffscreenPageLimit = n。它设置了当前可见页面两侧应保留的页面数量。超出此限制的页面将在需要时从适配器中重新创建,如果你只有有限数量的页面。更多细节请参考这里

0
在你的setCurrentItem()用法(在onPageScrollStateChanged()下),将第二个参数设置为false 这将在ViewPager中禁用动画(不是永久性的),当你以编程方式设置页面时,这就是你看到类似闪光的动画的原因。

为什么要这样做:

考虑两个ViewPagers A和B

您在A中从一页滚动到另一页,这会在ViewPager B中引起相同的滚动。一旦您在A中完全从一页滚动到另一页,A会在内部设置currentItem,但不会为B设置。

现在,您通过调用setCurrentItem([index], true)告知B页面更改,无论您的滚动如何,都将引入闪光动画(您引发的滚动被重置为正常状态,并开始从一页到另一页的页面动画)。


0

这看起来有点可疑,你正在调用setCurrentItem在每个传递的state中的onPageScrollStateChanged中...尝试添加一些if语句,并且只有当state == ViewPager2.SCROLL_STATE_IDLE时才调用setCurrentItem

状态列表:

  • ViewPager2.SCROLL_STATE_IDLE
  • ViewPager2.SCROLL_STATE_DRAGGING
  • ViewPager2.SCROLL_STATE_SETTLING

ViewPager2.SCROLL_STATE_DRAGGING状态在onPageScrolled方法中处理(即通过手指拖动共享位置偏移量)

ViewPager2.SCROLL_STATE_SETTLINGViewPager2自动滚动(捕捉)到最接近的页面 - 在这里我不确定通过代码拖动ViewPager2(与scrollTo共享位置)是否也会进入此state,从而导致相同的捕捉行为(因此也会调用onPageScrolled?)...值得测试,在那里放一些日志

还有:您正在拖动一个ViewPager2,并在onPageScrolled中调用第二个ViewPager2上的scrollTo。这可能会导致在第二个侦听器上调用onPageScrolled,该侦听器调用第一个ViewPager2scrollTo方法,依此类推...循环。考虑在用户开始拖动一个ViewPager2时动态注销侦听器,并在移动结束(SCROLL_STATE_IDLE state)时重新注册


0

我认为问题在于在ViewPagers的'onPageScrolled'方法中,你正在滚动其他的页面。因此,两个页面可能同时相互滚动。例如,如果你正在滚动ViewPager_1,那么它的'onPageScrolled'方法将滚动ViewPager_2,而由于ViewPager_2现在已经滚动,它的'onPageScrolled'方法将被调用,这将继续滚动ViewPager_1,反之亦然。 因此,你可能需要考虑应用一个检查,即如果ViewPager_1持有触摸,则不执行ViewPager_2的'onPageScrolled'方法内的代码。或者如果你想避免这些检查,可以在触摸时使用'registerOnPageChangeCallback'/ 'unregisterOnPageChangeCallback'来监听其他ViewPager。

            @Override
            public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
                viewBinding.viewPager.scrollTo(positionOffsetPixels, 0);
            }

问题在于这个功能的要求是每个viewpager之间的滑动需要同步。 - dlucci

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