实现效果
UC浏览器首页效果
我们先来看下UC浏览器首页的滑动动画和我最终实现的动画效果
使用方式
1 | <cn.ittiger.ucpage.view.UCIndexView |
使用方式只需要如此布局即可,没有其他的任何代码操作,非常简单。几个重要属性如下:
isPageHeadViewFixed:
设置PageHeadView
是否固定显示(效果如上图2)isContentHeadViewEnable:
设置ContentHeadView
是否启用(效果如上图3)isPullRestoreEnable:
设置是否可以通过下拉手势恢复到初始状态,UC首页下拉不能恢复初始状态contentHeadViewHeight:
设置contentHeadView
视图的高度pageNavigationViewHeight:
设置PageNavigationView
视图的高度pageHeadViewLayoutId:
设置PagetHeadView
视图的内容布局,如图中的文字UC头条布局pageNavigationViewLayoutId:
设置PageNavigationView
视图的内容布局,如图中的文字网址导航布局contentHeadViewLayoutId:
设置ContentHeadViev
视图的内容布局,如图中的文字新闻头部导航布局contentViewLayoutId:
设置ContentView
视图的内容布局,如图中的文字新闻内容区布局
github地址
实现及Demo地址:https://github.com/huyongli/UCIndexAnimation
实现过程
下面来讲讲我的实现过程:
首先来分析下UC首页这个动画中涉及到的元素和要点
- 向上滑动过程顶部的UC头条会慢慢的显示出来,我把这部分视图称为:
PageHeadView(页面头部视图)
,很明显其初始化时是在屏幕外部的 - 向上滑动过程中UC漫站上面会有个新闻Tab菜单头部慢慢的显示出来,我把这部分视图称为:
ContentHeadView(新闻头部视图)
,初始时是被ContentView
遮盖,而后慢慢滑出,显然它的滑动速度是比ContentView
快的 - 向上滑动过程中天气、搜索、网站导航会稍微往上滑动一段距离最终隐藏,我把这部分视图称为:
PageNavigationView(页面导航视图)
,随着不断的滑动,会慢慢的被其余三个视图共同遮盖掉 - UC漫站整个新闻内容视图慢慢的向上滑动直至跟UC头条相接,我把这部分称为:
ContentView(新闻内容视图)
- 根据动画效果来看,上面所说的四个不同的视图部分都是同时停止滑动,但是他们滑动的距离明显是不相同的
PageHeadView
滑动的距离为其自身高度,ContentHeadView
的滑动距离为其自身高度与ContentView
滑动的距离之和,而其相对ContentView
视图的滑动距离为其自身高度ContentView
滑动距离为其自身初始距离顶部的边距减去PageHeadView
和ContentHeadView
两者的高度PageNavigationView
的滑动距离很小- 上面的分析把UC首页整体划分成四个不同的View部分,另外其首页中只有
ContentView、PageNavigationView
两个视图会处理滑动事件实现技术点和细节
根据上面的分析我们知道有三个视图是从被遮盖到显示或者是从显示到遮盖,剩下的ContentView
一直是遮盖其他的视图。根据视图遮盖很容易联想到FrameLayout
布局,多个FrameLayout
布局叠加在一起就类似PS中的图层叠加,滑动可以看成是不同层级的FrameLayout
的marginTop
不断变化的过程。
实现类结构图
我的实现中先要说明两点:
- 我把上滑称为展示状态或者叫Show状态
- 下滑称为恢复初始化状态或者叫Hide状态
下图是我的实现类结构图:
主要类实现解析
UCIndexView:
模仿UC首页的最终实现,使用时在布局文件中使用此类即可PageHeadView、PageNavigationView、ContentView、ContentHeadView:
这四个类的作用见我上面的分析MoveView:
页面四个视图的基类,主要包含滑动过程中的一些基本属性和方法mNeedMoveHeight:
视图需要滑动的距离(ContentHeadView
的此属性设置为相对ContentView
的滑动距离),此属性主要用来计算各自视图在滑动过程中的步长mShowStopMarginTop:
视图进行Show操作时,当视图的marginTop
值等于该值时,结束Show操作。此值用来确定上滑过程中,视图滑动结束时的位置。mHideStopMarginTop:
视图进行Hide操作时,当视图的marginTop
值等于该值时,结束Hide操作。此值用来确定下滑过程中,视图滑动结束时的位置。通常此值为视图初始化成功后的marginTop
值。getMarginTop():
获取视图当前的marginTop
值updateMarginTop(flaot step):
根据当前的滑动步长更新视图的marginTop
值isHideFinish():
判断当前视图的Hide操作是否完成isShowFinish():
判断当前视图的Show操作是否完成getShowMoveStep(float step):
获取视图Show
过程中的实际可滑动步长,有可能计算得到的滑动步长超过了视图的可滑动距离getHideMoveStep(float step):
获取视图Hide过程中的实际可滑动步长,有可能计算得到的滑动步长超过了视图的可滑动距离TouchMoveView:
用来处理手指触摸事件从而滑动的视图基类滑动事件处理
直接上代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31@Override
public void onTouchMoveEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastTouchY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
//手指滑动过程中的滑动步长
mDelY = event.getRawY() - mLastTouchY;
viewMove(mDelY, mIsPullRestoreEnable);
mLastTouchY = event.getRawY();
break;
case MotionEvent.ACTION_UP:
int offset = 0;
if(mDelY > 0) {//hide,下拉
if(!mIsPullRestoreEnable) {//当前不允许下拉恢复
return;
}
offset = mContentView.getHideOffset();
} else {//show, 上拉
offset = mContentView.getShowOffset();
}
if(offset <= mPageHeadView.getNeedMoveHeight() / 2) {//没有滑过二分之一高度
slip(-mDelY, mIsPullRestoreEnable);
} else {
slip(mDelY, mIsPullRestoreEnable);
}
break;
}
}
1 | /** |
上面的代码中我把手指每次的滑动距离当做ContentView
的滑动步长,再根据ContentView
的滑动步长计算其他视图的当前滑动步长,计算具体方式可以看代码中的注释。
其实上面代码里的实现步骤还是比较清晰简单的,主要是如下几个步骤:
- 在
MotionEvent.MOVE
中先得到当前手指滑动的距离,此距离作为ContentView
的此次滑动步长 - 根据
ContentView
的滑动步长计算其他三个视图的滑动步长 - 根据当前是上滑还是下滑,判断是否滑动结束,没有结束则按照计算得到的步长继续对View进行滑动处理
- 当手指松开后,在
MotionEvent.UP
中判断ContentView
的滑动距离是否达到了PageHeadView
高度的一半,从而决定是继续同方向滑动至结束还是恢复到原状态 - 手指松开后,最终调用slip方法开始自动循环滑动处理,直至滑动结束
原创文章,转载请出处注明。
下面是我的个人公众号,欢迎关注交流