英文:
ViewPager2 flashes/reloads on swipe
问题
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();
}
}
英文:
I'm trying to build an Android application using the new ViewPager2. I've added two ViewPagers, separated by a view, and when you swipe, both viewpagers should move. Both view pagers are moving correctly but upon completion of the gesture, the swiped view flashes and the non swiped view reloads, as demonstrated by the attached gif. Here is my code for the Activity, ViewPagerAdapter, and Fragment. Any help is appreciated
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
得分: 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)
}
英文:
You can implement by 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)
}
答案2
得分: 0
你可以尝试使用 adapter.setOffscreenPageLimit = n
。它设置应保留在当前可见页面两侧的页面数量。超出此限制的页面将在需要时从适配器中重新创建,如果您的页面数量有限的话。更多详细信息请参见此处。
英文:
You can try to use adapter.setOffscreenPageLimit = n
. It sets the number of pages that should be retained to either side of the currently visible page(s). Pages beyond this limit will be recreated from the adapter when needed If you have a limited number of pages.
More details are here.
答案3
得分: 0
在您的setCurrentItem()
用法中(位于onPageScrollStateChanged()
下方),将第二个参数设置为false
。
这将在您以编程方式设置页面时在ViewPager中禁用动画(不是永久性的),这就是您看到类似闪烁的动画的原因。
要了解原因:
考虑两个ViewPager,A和B
您在A中从一页滚动到另一页,这会在ViewPager
B中引发相同的滚动。一旦您在A中完全从一页滚动到另一页,currentItem
会在A内部设置,但不会用于B。
现在,通过调用setCurrentItem([index], true)
,您通知B页面已更改,而不管您的滚动如何,都会播放页面更改动画,从而引发闪烁(您引发的滚动被重置为正常状态,并开始一页到另一页的页面动画)。
英文:
In your setCurrentItem()
usages (under onPageScrollStateChanged()
), set the second parameter to false
This will disable animation (not permanently) in the ViewPager when you set a page programatically, which is the reason you see the flash-like animation.
<hr>
To know why:
Consider two ViewPagers A & B
You scroll from one page to another in A, which induces the same scroll in ViewPager
B. Once you completely scroll from one page to another in A, the currentItem
is internally set by A, but not for B.
Now, you inform B about the page change by calling setCurrentItem([index], true)
, which, irrespective of your scroll, will animate the page change introducing the flash (your induced scroll is reset to normal, and page animation for one page to another is started)
答案4
得分: 0
这看起来有点可疑,你正在每次传递的state
内部调用onPageScrollStateChanged
中的setCurrentItem
... 尝试添加一些条件判断,仅在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_SETTLING
是ViewPager2
自动滚动(自动对齐)到最近的页面 - 在这里,我不确定通过代码(使用scrollTo
)拖动ViewPager2
是否也会进入此state
,从而产生相同的对齐行为(因此也会调用onPageScrolled
?)... 值得测试,在其中放置一些日志。
另外:你正在拖动一个ViewPager2
,在onPageScrolled
中对第二个ViewPager2
调用scrollTo
。这可能会导致在这第二个监听器上调用onPageScrolled
,它会调用第一个ViewPager2
的scrollTo
方法,依此类推... 形成循环。考虑在用户开始拖动一个ViewPager2
时动态取消注册监听器,并在移动结束时重新注册监听器(SCROLL_STATE_IDLE
状态)。
英文:
this looks a bit suspicious that you are calling setCurrentItem
inside onPageScrollStateChanged
for every passed state
... try to add some if
and setCurrentItem
only when state==ViewPager2.SCROLL_STATE_IDLE
list of states:
ViewPager2.SCROLL_STATE_IDLE
ViewPager2.SCROLL_STATE_DRAGGING
ViewPager2.SCROLL_STATE_SETTLING
ViewPager2.SCROLL_STATE_DRAGGING
state is handled in onPageScrolled
method (sharing position offset aka. dragging by finger)
ViewPager2.SCROLL_STATE_SETTLING
is auto-scroll (snap) of ViewPager2
to closest page - in here I'm not shure that ViewPager2
dragged "by code" (sharing position with scrollTo
) will also enter in this state
causing same snap behavior (so also onPageScrolled
?) ... worth testing, put some logs in there
also: you are dragging one ViewPager2
and in onPageScrolled
you are calling scrollTo
on second ViewPager2
. this may lead to call onPageScrolled
call on this second listener, which is calling first ViewPager2
scrollTo
method and so on... looped. consider dynamic unregistering listener when user will start drag one ViewPager2
and reregistering when movement ends (SCROLL_STATE_IDLE
state
)
答案5
得分: 0
我认为问题在于两个ViewPagers的onPageScrolled方法中,您正在滚动另一个pager。因此,两个页面可能会同时相互滚动。例如,如果您滚动ViewPager_1,那么它的onPageScrolled方法将会滚动ViewPager_2,而由于ViewPager_2现在已经滚动,所以它的onPageScrolled方法将会被调用,从而滚动ViewPager_1,反之亦然。
因此,您可能需要考虑应用以下检查:如果触摸是由ViewPager_1保持的,则不应执行ViewPager_2的onPageScrolled方法中的代码。或者,如果您想避免这些检查,那么在一个ViewPager保持触摸时,可以使用registerOnPageChangeCallback/unregisterOnPageChangeCallback来注册/注销另一个ViewPager的监听器。
@Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
viewBinding.viewPager.scrollTo(positionOffsetPixels, 0);
}
英文:
I think the problem is that in 'onPageScrolled' method of both ViewPagers you are scrolling other pager. So, both pages might be scrolling each other at the same. For example, if you are scrolling ViewPager_1 then its onPageScrolled method will scroll ViewPager_2 and as ViewPager_2 is now scrolled then its onPageScrolled method will be called and that will scroll ViewPager_1 and vice versa.
So, You may need to consider applying the check that if touch is holded ny ViewPager_1 then code inside onPageScrolled method of ViewPager_2 should not be executed. Or if you want to avoid those checks. Then registerOnPageChangeCallback/unregisterOnPageChangeCallback the listener of other ViewPager when one holds the touch.
@Override
public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
viewBinding.viewPager.scrollTo(positionOffsetPixels, 0);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论