ViewPager翻页特效(全文),2024年教你增加拿到BAT等大厂offer几率

parent.requestDisallowInterceptTouchEvent(true);
}
}
}
// Not else! Note that mIsBeingDragged can be set above.
if (mIsBeingDragged) {
// Scroll to follow the motion event
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(activePointerIndex);
needsInvalidate |= performDrag(x);
}
break;

我们需要关注的只是 X方向上的拖拽有什么规律. 所以,顺着final float x = ev.getX(pointerIndex); 这个变量去找关键方法, 最终锁定:performDrag(x); 它是处理X方向上位移的关键入口。

private boolean performDrag(float x) {
boolean needsInvalidate = false;

final float deltaX = mLastMotionX - x;
mLastMotionX = x;


// Don’t lose the rounded component
mLastMotionX += scrollX - (int) scrollX;
scrollTo((int) scrollX, getScrollY()); // 关键代码1, 控件在画布上的横像滚动
pageScrolled((int) scrollX);// 关键代码2,将 scrollX进一步往下传递

return needsInvalidate;
}

发现两句关键代码,一个是处理滑动的 scrolllTo,一个是把scrollX往下传递的 pageScrolled(scrollX). 前面一句都明白,但是这个第二句就有点不懂了,继续深入。

private boolean pageScrolled(int xpos) {

final float pageOffset = (((float) xpos / width) - ii.offset)
/ (ii.widthFactor + marginOffset);
final int offsetPixels = (int) (pageOffset * widthWithMargin);

mCalledSuper = false;
onPageScrolled(currentPage, pageOffset, offsetPixels);
if (!mCalledSuper) {
throw new IllegalStateException(
“onPageScrolled did not call superclass implementation”);
}
return true;
}

追踪 参数xpos得知,x方向上的偏移量信息,最后进入了 onPageScrolled(...) 方法.

protected void onPageScrolled(int position, float offset, int offsetPixels) {
// Offset any decor views if needed - keep them on-screen at all times.
if (mDecorChildCount > 0) {
… // 这里还是在处理 装饰,所以不用看,而且参数也没进入到这里
}

dispatchOnPageScrolled(position, offset, offsetPixels);

if (mPageTransformer != null) {
final int scrollX = getScrollX();
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();

if (lp.isDecor) continue;
final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();
mPageTransformer.transformPage(child, transformPos);
}
}

mCalledSuper = true;
}

又是两句关键代码:

dispatchOnPageScrolled(position, offset, offsetPixels);

点进去看了之后,发现只是 调用了 OnPageChangeListener 监听回调.

如果我们设置了滑动监听,就可以在滑动的时候,收到回调。相信大家都用过这个。

mPageTransformer.transformPage(child, transformPos);

这里就比较奇怪了。这句代码把子view,以及子view当前的位置信息返回到了外界。

那么外界拿到这两个参数值之后可以做什么事呢?理论上,可以做任何事

二,探索源码结论

  1. ViewPager的初始子view摆放,都是横向的。在纵向上是上下平齐。

  2. ViewPager将 子view以及子view的当前位置参数,通过PageTransformer.transformPage(view,position)反馈到外界,能做很多。比如说,让横着排放的子view变成竖着放,又或者 让即将滑出屏幕的子view以倾斜的角度以某个加速度飞出去,为所欲为。这个就是我们可以完成这个动画的基础。

三,PageTransformer参数规律探索

ViewPager 提供了一个DIY滑动特效的可能性。不过在动手做动画之前,还需要了解 这两个参数的变化规律。

新建一个android工程,写好ViewPager+TabLayout 的代码和布局。运行起来大概是这个效果:

同时,我们给viewpager加上setPageTransformer(…)方法,并且打印日志。

viewPager.adapter = MyFragmentPagerAdapter(supportFragmentManager);
viewPager.offscreenPageLimit = 3 // 最少缓存3个,让左右两边都显示出来
viewPager.setPageTransformer(true, ViewPager.PageTransformer { view, position ->
Log.d(“setPageTransformer”, “view: v i e w . h a s h C o d e ( ) ∣ p o s i t i o n : {view.hashCode()} | position: view.hashCode()position:{position}”)
})

然后启动app,看看日志:

03-12 14:14:46.222 1583-1583/? D/setPageTransformer: view:136851691 | position:0.0
03-12 14:14:46.222 1583-1583/? D/setPageTransformer: view:147234376 | position:1.0
03-12 14:14:46.222 1583-1583/? D/setPageTransformer: view:75203809 | position:2.0
03-12 14:14:46.222 1583-1583/? D/setPageTransformer: view:35279366 | position:3.0

可以看到,在一开始,有4个子view被初始化,位置信息分别是 0.0 / 1.0 / 2.0 / 3.0 . 这是由于我设置了offscreenPageLimit 为3 ,所以除了当前view之外,还会初始化3个屏幕之外的view 。这就意味着:当前view的position是0,而往右边,position会递增,每递增1个view,就会加1.0, 反过来,我们也可以推导,往左边,每过一个view,position会递减. 为了验证我们的推导,我们滑动一下,观察position的变化.

向左滑动一格。

日志节略如下:

hashCode为 136851691 的子view,它的position从 原本的0.0,,最终变成了 -1.0

03-12 14:22:11.836 1583-1583/? D/setPageTransformer: view:136851691 | position:-1.0

而,原本hashCode为147234376,position为1的子view,position则变成了 0.0

03-12 14:22:11.836 1583-1583/? D/setPageTransformer: view:147234376 | position:0.0

再试试向又滑动一格,hashCode为 136851691 的子view, 从 -0.99326146 变成了0.0 , 这里的小数大概是由于计算精度丢失造成的。可以认为是 从-1.0 变为了0.0 .

画图描述刚才的结论(粉色是当前视野):

image-20200312143524467

OK,了解到这里,position的变化规律基本也掌握了,那么接下来可以进行动画拆分 编程实现.

关键代码

有了思路,那么IT民工现在开始搬砖。

一,动画拆分各个击破

  1. 子view重叠排布

原本的子view都是横向,从左到又排布,默认的排布方式并没有相互覆盖. 所以我们可以考虑使用视图动画

? 为什么是视图动画,而不是属性动画?因为没必要,当前的需求我只需要视觉效果上的位置变化,不需要子view的交互事件,用属性动画理论上应该也可以,但是直觉会存在交互问题,有时间再试试).

使用视图动画,将所有子view层叠在一起。原本都是横向排布,所以只需要将所有的view进行x轴位移,即可。

上代码:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

公式的推导很简单,就是让右边的子view向左平移 -position个自身宽度.

效果为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

滑动之后,不再出现其他子view。

  1. 让多个子view之间呈现x轴上的位置差

虽然重叠在了一起,但是我还需要让右边的子view呈现位置偏差. 并且,越往右,偏差越大。

上代码:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

效果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 让多个子view之间呈现缩放差

x轴上的位置差虽然有了,但是,原图上,越往右,越小,所以还需要做出x,y方向上的缩放

上代码:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

效果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 监听滑动position,做出透明度逐渐变化

视觉效果都有了,那么可以开始做动画效果.

经过对position的观察,我们知道position会以小数的形式渐变。原图中,向左滑出的view,会以一个透明度慢慢减小的方式消失,那么先来完成这一步。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

效果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 监听滑动position,做出左滑时 当前view的平移动画

最后一步,滑出消失的view虽然透明度的动画完成了,但是原图中,还有一个渐渐向左移动的动画。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

下图是我进阶学习所积累的历年腾讯、头条、阿里、美团、字节跳动等公司2019-2021年的高频面试题,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节

以上【历年大厂高级工程师面试题集】、【Android高级进阶教学视频】、【Android高级知识点学习PDF】皆无偿分享给大家。如有需要,点击**【Android架构视频+BATJ面试专题PDF+学习笔记】**即可免费获取。

些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节

[外链图片转存中…(img-L1Yuu7VD-1710850157605)]

以上【历年大厂高级工程师面试题集】、【Android高级进阶教学视频】、【Android高级知识点学习PDF】皆无偿分享给大家。如有需要,点击**【Android架构视频+BATJ面试专题PDF+学习笔记】**即可免费获取。

整理不易,望各位看官老爷点个关注转发,谢谢!祝大家都能得到自己心仪工作。

  • 9
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要修改 ViewPager2 的翻页时间,可以通过设置 ViewPager2 的滑动速度来实现。具体步骤如下: 1. 创建一个自定义的 ViewPager2 类,并继承自 ViewPager2。 2. 实现 ViewPager2.OnPageChangeCallback 接口,并重写 onPageScrolled() 方法。 3. 在 onPageScrolled() 方法中获取 ViewPager2 的滑动速度,并将其设置为自定义的时间。 以下是示例代码: ```java public class CustomViewPager2 extends ViewPager2 implements ViewPager2.OnPageChangeCallback { private final int SCROLL_DURATION = 500; // 自定义的滑动时间,单位为毫秒 public CustomViewPager2(@NonNull Context context) { super(context); init(); } public CustomViewPager2(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } private void init() { registerOnPageChangeCallback(this); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { try { // 获取 ViewPager2 的滑动速度 Field field = ViewPager2.class.getDeclaredField("mRecyclerView"); field.setAccessible(true); RecyclerView recyclerView = (RecyclerView) field.get(this); LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(getContext()) { @Override protected int calculateTimeForScrolling(int dx) { // 将滑动速度设置为自定义的时间 return SCROLL_DURATION; } }; linearSmoothScroller.setTargetPosition(position); recyclerView.getLayoutManager().startSmoothScroll(linearSmoothScroller); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } } } ``` 使用时,只需要将布局文件中的 ViewPager2 替换为 CustomViewPager2 即可: ```xml <com.example.myapp.CustomViewPager2 android:id="@+id/viewPager2" android:layout_width="match_parent" android:layout_height="match_parent" /> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值