当前位置:网站首页>Viewpager2 usage record
Viewpager2 usage record
2022-06-27 12:47:00 【Mo Mo 0830】
List of articles
Make a note of ,ViewPager2 Several small problems encountered in the process of use :
1. ViewPager2 and RecyclerView Slide conflict .
background
ViewPager2 Horizontal... Is used in RecyclerView.
The phenomenon
Watch... On the Internet , Most people come across RecyclerView Can't slide or ViewPager2 Can't slide . But what I encountered was : If you click RecyclerView Immediately after that, slide left and right , It is ViewPager2 Sliding , Instead of RecyclerView Sliding ; If you click RecyclerView Then pause , Slide again , It can slide normally RecyclerView.
terms of settlement
Use Google Provided NestedScrollableHost.kt, The packing RecyclerView that will do .
/* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.widget.FrameLayout
import androidx.viewpager2.widget.ViewPager2
import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
import kotlin.math.absoluteValue
import kotlin.math.sign
/** * Layout to wrap a scrollable component inside a ViewPager2. Provided as a solution to the problem * where pages of ViewPager2 have nested scrollable elements that scroll in the same direction as * ViewPager2. The scrollable element needs to be the immediate and only child of this host layout. * * This solution has limitations when using multiple levels of nested scrollable elements * (e.g. a horizontal RecyclerView in a vertical RecyclerView in a horizontal ViewPager2). */
class NestedScrollableHost : FrameLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
private var touchSlop = 0
private var initialX = 0f
private var initialY = 0f
private val parentViewPager: ViewPager2?
get() {
var v: View? = parent as? View
while (v != null && v !is ViewPager2) {
v = v.parent as? View
}
return v as? ViewPager2
}
private val child: View? get() = if (childCount > 0) getChildAt(0) else null
init {
touchSlop = ViewConfiguration.get(context).scaledTouchSlop
}
private fun canChildScroll(orientation: Int, delta: Float): Boolean {
val direction = -delta.sign.toInt()
return when (orientation) {
0 -> child?.canScrollHorizontally(direction) ?: false
1 -> child?.canScrollVertically(direction) ?: false
else -> throw IllegalArgumentException()
}
}
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
handleInterceptTouchEvent(e)
return super.onInterceptTouchEvent(e)
}
private fun handleInterceptTouchEvent(e: MotionEvent) {
val orientation = parentViewPager?.orientation ?: return
// Early return if child can't scroll in same direction as parent
if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) {
return
}
if (e.action == MotionEvent.ACTION_DOWN) {
initialX = e.x
initialY = e.y
parent.requestDisallowInterceptTouchEvent(true)
} else if (e.action == MotionEvent.ACTION_MOVE) {
val dx = e.x - initialX
val dy = e.y - initialY
val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL
// assuming ViewPager2 touch-slop is 2x touch-slop of child
val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f else 1f
val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f
if (scaledDx > touchSlop || scaledDy > touchSlop) {
if (isVpHorizontal == (scaledDy > scaledDx)) {
// Gesture is perpendicular, allow all parents to intercept
parent.requestDisallowInterceptTouchEvent(false)
} else {
// Gesture is parallel, query child if movement in that direction is possible
if (canChildScroll(orientation, if (isVpHorizontal) dx else dy)) {
// Child can scroll, disallow all parents to intercept
parent.requestDisallowInterceptTouchEvent(true)
} else {
// Child cannot scroll, allow all parents to intercept
parent.requestDisallowInterceptTouchEvent(false)
}
}
}
}
}
}
Usage method :
<com.example.NestedScrollableHost android:id="@+id/nsh" android:layout_width="match_parent" android:layout_height="72dp" android:orientation="horizontal">
<androidx.recyclerview.widget.RecyclerView android:id="@+id/rv" android:layout_width="match_parent" android:layout_height="72dp"/>
</com.example.NestedScrollableHost>
2.ViewPager2 Slide to edge shadow cancel .
The phenomenon
Not only is ViewPager2, similar ScrollView、RecyclerView The control of a class , There will be a time to slide to the first or last item , And keep sliding , A lilac ripple shadow will appear .
terms of settlement
If it is RecyclerView, Then directly set :
binding.rvRoom.overScrollMode = View.OVER_SCROLL_NEVER
Or in the xml Set in :
android:overScrollMode="never"
that will do .
but ViewPager2 This setting is not allowed , Use the following methods :
//ViewPager Cancel the slide to edge shadow effect
val child = binding.viewPager.getChildAt(0)
if (child is RecyclerView) child.overScrollMode = View.OVER_SCROLL_NEVER
3. TabLayout and ViewPager2 Sliding effect
background
When in TabLayout There are several in the middle Tab Clicking time ,ViewPager2 It will slide too many pages in succession .
Need to click Tab No sliding animation effect , slide ViewPager2 Still have animation effect .( similar Android End wechat effect )
terms of settlement
rewrite TabLayoutMediator
public class TabLayoutMediators {
private TabLayout tabLayout;
private ViewPager2 viewPager;
private boolean autoRefresh;
private static boolean smoothScroll;
private TabConfigurationStrategy tabConfigurationStrategy;
@Nullable
private RecyclerView.Adapter<?> adapter;
private boolean attached;
@Nullable
private TabLayoutOnPageChangeCallback onPageChangeCallback;
@Nullable
private TabLayout.OnTabSelectedListener onTabSelectedListener;
@Nullable
private RecyclerView.AdapterDataObserver pagerAdapterObserver;
/** * A callback interface that must be implemented to set the text and styling of newly created * tabs. */
public interface TabConfigurationStrategy {
/** * Called to configure the tab for the page at the specified position. Typically calls {@link * TabLayout.Tab#setText(CharSequence)}, but any form of styling can be applied. * * @param tab The Tab which should be configured to represent the title of the item at the given * position in the data set. * @param position The position of the item within the adapter's data set. */
void onConfigureTab(@NonNull TabLayout.Tab tab, int position);
}
public TabLayoutMediators(
@NonNull TabLayout tabLayout,
@NonNull ViewPager2 viewPager,
@NonNull TabConfigurationStrategy tabConfigurationStrategy) {
this(tabLayout, viewPager, /* autoRefresh= */ true, tabConfigurationStrategy);
}
public TabLayoutMediators(
@NonNull TabLayout tabLayout,
@NonNull ViewPager2 viewPager,
boolean autoRefresh,
@NonNull TabConfigurationStrategy tabConfigurationStrategy) {
this(tabLayout, viewPager, autoRefresh, /* smoothScroll= */ true, tabConfigurationStrategy);
}
public TabLayoutMediators(
@NonNull TabLayout tabLayout,
@NonNull ViewPager2 viewPager,
boolean autoRefresh,
boolean smoothScroll,
@NonNull TabConfigurationStrategy tabConfigurationStrategy) {
this.tabLayout = tabLayout;
this.viewPager = viewPager;
this.autoRefresh = autoRefresh;
this.smoothScroll = smoothScroll;
this.tabConfigurationStrategy = tabConfigurationStrategy;
}
/** * Link the TabLayout and the ViewPager2 together. Must be called after ViewPager2 has an adapter * set. To be called on a new instance of TabLayoutMediator or if the ViewPager2's adapter * changes. * * @throws IllegalStateException If the mediator is already attached, or the ViewPager2 has no * adapter. */
public void attach() {
if (attached) {
throw new IllegalStateException("TabLayoutMediator is already attached");
}
adapter = viewPager.getAdapter();
if (adapter == null) {
throw new IllegalStateException(
"TabLayoutMediator attached before ViewPager2 has an " + "adapter");
}
attached = true;
// Add our custom OnPageChangeCallback to the ViewPager
onPageChangeCallback = new TabLayoutOnPageChangeCallback(tabLayout);
viewPager.registerOnPageChangeCallback(onPageChangeCallback);
// Now we'll add a tab selected listener to set ViewPager's current item
onTabSelectedListener = new ViewPagerOnTabSelectedListener(viewPager, smoothScroll);
tabLayout.addOnTabSelectedListener(onTabSelectedListener);
// Now we'll populate ourselves from the pager adapter, adding an observer if
// autoRefresh is enabled
if (autoRefresh) {
// Register our observer on the new adapter
pagerAdapterObserver = new PagerAdapterObserver();
adapter.registerAdapterDataObserver(pagerAdapterObserver);
}
populateTabsFromPagerAdapter();
// Now update the scroll position to match the ViewPager's current item
tabLayout.setScrollPosition(viewPager.getCurrentItem(), 0f, true);
}
/** * Unlink the TabLayout and the ViewPager. To be called on a stale TabLayoutMediator if a new one * is instantiated, to prevent holding on to a view that should be garbage collected. Also to be * called before {@link #attach()} when a ViewPager2's adapter is changed. */
public void detach() {
if (autoRefresh && adapter != null) {
adapter.unregisterAdapterDataObserver(pagerAdapterObserver);
pagerAdapterObserver = null;
}
tabLayout.removeOnTabSelectedListener(onTabSelectedListener);
viewPager.unregisterOnPageChangeCallback(onPageChangeCallback);
onTabSelectedListener = null;
onPageChangeCallback = null;
adapter = null;
attached = false;
}
/** * Returns whether the {@link TabLayout} and the {@link ViewPager2} are linked together. */
public boolean isAttached() {
return attached;
}
@SuppressWarnings("WeakerAccess")
void populateTabsFromPagerAdapter() {
tabLayout.removeAllTabs();
if (adapter != null) {
int adapterCount = adapter.getItemCount();
for (int i = 0; i < adapterCount; i++) {
TabLayout.Tab tab = tabLayout.newTab();
tabConfigurationStrategy.onConfigureTab(tab, i);
tabLayout.addTab(tab, false);
}
// Make sure we reflect the currently set ViewPager item
if (adapterCount > 0) {
int lastItem = tabLayout.getTabCount() - 1;
int currItem = Math.min(viewPager.getCurrentItem(), lastItem);
if (currItem != tabLayout.getSelectedTabPosition()) {
tabLayout.selectTab(tabLayout.getTabAt(currItem));
}
}
}
}
/** * A {@link ViewPager2.OnPageChangeCallback} class which contains the necessary calls back to the * provided {@link TabLayout} so that the tab position is kept in sync. * * <p>This class stores the provided TabLayout weakly, meaning that you can use {@link * ViewPager2#registerOnPageChangeCallback(ViewPager2.OnPageChangeCallback)} without removing the * callback and not cause a leak. */
private static class TabLayoutOnPageChangeCallback extends ViewPager2.OnPageChangeCallback {
@NonNull
private final WeakReference<TabLayout> tabLayoutRef;
private int previousScrollState;
private int scrollState;
TabLayoutOnPageChangeCallback(TabLayout tabLayout) {
tabLayoutRef = new WeakReference<>(tabLayout);
reset();
}
@Override
public void onPageScrollStateChanged(final int state) {
if (state == SCROLL_STATE_DRAGGING) {
smoothScroll = true;
} else if (state == SCROLL_STATE_IDLE) {
smoothScroll = false;
}
previousScrollState = scrollState;
scrollState = state;
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
TabLayout tabLayout = tabLayoutRef.get();
if (tabLayout != null) {
// Only update the text selection if we're not settling, or we are settling after
// being dragged
boolean updateText =
scrollState != SCROLL_STATE_SETTLING || previousScrollState == SCROLL_STATE_DRAGGING;
// Update the indicator if we're not settling after being idle. This is caused
// from a setCurrentItem() call and will be handled by an animation from
// onPageSelected() instead.
boolean updateIndicator =
!(scrollState == SCROLL_STATE_SETTLING && previousScrollState == SCROLL_STATE_IDLE);
tabLayout.setScrollPosition(position, positionOffset, updateText, updateIndicator);
}
}
@Override
public void onPageSelected(final int position) {
TabLayout tabLayout = tabLayoutRef.get();
if (tabLayout != null
&& tabLayout.getSelectedTabPosition() != position
&& position < tabLayout.getTabCount()) {
// Select the tab, only updating the indicator if we're not being dragged/settled
// (since onPageScrolled will handle that).
boolean updateIndicator =
scrollState == SCROLL_STATE_IDLE
|| (scrollState == SCROLL_STATE_SETTLING
&& previousScrollState == SCROLL_STATE_IDLE);
tabLayout.selectTab(tabLayout.getTabAt(position), updateIndicator);
}
}
void reset() {
previousScrollState = scrollState = SCROLL_STATE_IDLE;
}
}
/** * A {@link TabLayout.OnTabSelectedListener} class which contains the necessary calls back to the * provided {@link ViewPager2} so that the tab position is kept in sync. */
private static class ViewPagerOnTabSelectedListener implements TabLayout.OnTabSelectedListener {
private final ViewPager2 viewPager;
// private final boolean smoothScroll;
ViewPagerOnTabSelectedListener(ViewPager2 viewPager, boolean smoothScroll) {
this.viewPager = viewPager;
// this.smoothScroll = smoothScroll;
}
@Override
public void onTabSelected(@NonNull TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition(), smoothScroll);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
// No-op
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
// No-op
}
}
private class PagerAdapterObserver extends RecyclerView.AdapterDataObserver {
PagerAdapterObserver() {
}
@Override
public void onChanged() {
populateTabsFromPagerAdapter();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
populateTabsFromPagerAdapter();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
populateTabsFromPagerAdapter();
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
populateTabsFromPagerAdapter();
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
populateTabsFromPagerAdapter();
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
populateTabsFromPagerAdapter();
}
}
}
Use
Be careful : Using a custom TabLayoutMediators , Instead of TabLayoutMediator
// Definition TabLayout
val tabLayoutMediator = TabLayoutMediators(
binding.tabLayout,
binding.viewPager
) {
tab, position ->
when (position) {
0 -> tab.text = " intelligence "
1 -> tab.text = " scene "
2 -> tab.text = " I "
}
}
边栏推荐
- 浅谈软件研发的复杂性与效能提升之道
- What's the matter with Amazon's evaluation dropping and failing to stay? How to deal with it?
- 【TcaplusDB知识库】TcaplusDB-tcapsvrmgr工具介绍(三)
- Stack calculation (whether the order of entering and leaving the stack is legal) - Code
- Pyqt, pyside slot functions are executed twice
- Threejs' ambient light + point light + parallel light + spherical light and Hepler understanding + shadow ()
- 消息队列的使用
- Neo4j:入门基础(一)之安装与使用
- MySQL high level statements (I)
- printf不定长参数原理
猜你喜欢

【粉丝福利】今天给大家介绍一个白捡钱的方法-可转债,本人亲自验证,每年每人能获利1500元

亚马逊测评掉评、留不上评是怎么回事呢?要如何应对?

Dm8: Dameng database - lock timeout

【TcaplusDB知识库】TcaplusDB-tcapsvrmgr工具介绍(三)

MySQL learning 1: installing MySQL

Mathematical knowledge -- ideas and examples of game theory (bash game, Nim game, wizov game)

Airbnb复盘微服务

阿里一个面试题:使用两个线程,交替输出字母和数字

nmcli team bridge 基本配置
Interview shock 60: what will cause MySQL index invalidation?
随机推荐
浏览器cookie转selenium cookie登录
MySQL high level statements (I)
今晚战码先锋润和赛道第2期直播丨如何参与OpenHarmony代码贡献
DM8:达梦数据库-锁超时
对象序列化
Thymeleaf的相关知识
SSH workflow and principle
mybaitis生成器详解
ACL 2022 | TAMT proposed by Chinese Academy of Sciences: TAMT: search for a portable Bert subnet through downstream task independent mask training
解压 .log.gz 文件
Configuration management center of microservices
Hands on API development
How to participate in openharmony code contribution
build. Gradle configuration
esp32s3 IPERF例程测试 esp32s3吞吐量测试
threejs的环境光+点光源+平行光源+球面光 以及hepler理解+阴影()
Take stock of some easy-to-use and niche markdown editors
Mathematical knowledge -- ideas and examples of game theory (bash game, Nim game, wizov game)
Industry insight - how should brand e-commerce reshape growth under the new retail format?
ViewPager2使用记录