android 用户指引动画,小红书安卓客户端酷炫引导页实现分析

在android开发技术周报看到了小红书引导页的开源实现,觉得做的很不错,在这里分析一下它的实现,共同学习~

效果

0818b9ca8b590ca3270a3433284dd417.png

页面组成

0818b9ca8b590ca3270a3433284dd417.png

Touch事件传递

Req-1 ChildViewPager#image 和 ChildViewPager#text能同时响应滑动事件。

Req-2 点击“跳过”按钮进入LoginAnimFragment。

Req-3 Text1滑到Text2时,ChildViewPager#image不滑动,FirstFragment播放动画。

Req-4 Text2滑到Text3时,切换到SecondFragment并播放动画。

Req-5 Text3滑到Text4时,切换到ThirdFragment并播放动画。

Req-6 从Text4再向右滑,切换到LoginAnimFragment。

Req-7 从LoginAnimFragment向左滑不会滑到WelcomeAnimFragment。

触摸事件拦截

为了实现Req-1(ChildViewPager#image 和 ChildViewPager#text能同时响应滑动事件),需要在ParentViewPager拦截触摸事件自己来处理touch事件。

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

if (mLoginPageLock) {

requestDisallowInterceptTouchEvent(true);

return super.onInterceptTouchEvent(ev);

}

return true;

}

requestDisallowInterceptTouchEvent(true);的作用是禁止调用onInterceptTouchEvent方法,也就是不再拦截触摸事件。实现了Req-7(从LoginAnimFragment向左滑不会滑到WelcomeAnimFragment)。

那么问题来了,可以把

requestDisallowInterceptTouchEvent(true);

return super.onInterceptTouchEvent(ev);

这两句能换成一句return false吗?试了一下效果跟上面两句是一样的,会不会出现负面影响未知。

处理触摸事件

ParentViewPager将事件拦截后,在onTouchEvent里做处理。

Req-2:“跳过”按钮

/** * 由于子View被拦截,这里通过计算跳过按钮的坐标手动处理跳过click事件 */

if (mTvSkipLocation == null) {

mTvSkipLocation = new int[2];

mWelcomAnimFragment.tv_skip.getLocationOnScreen(mTvSkipLocation);

}

if (left == 0) {

margin = DisplayUtil.dip2px(getContext(), 10);

left = mTvSkipLocation[0] - margin;

top = mTvSkipLocation[1] - margin;

right = mTvSkipLocation[0] + mWelcomAnimFragment.tv_skip.getWidth() + margin;

bottom = mTvSkipLocation[1] + mWelcomAnimFragment.tv_skip.getHeight() + margin;

}

if (mCx - left > 0 && right - mCx > 0 && mCy - top > 0 && bottom - mCy > 0 && !mLoginPageLock) {

} else {

mSkipFlag = false;

}

if (ev.getAction() == MotionEvent.ACTION_UP) {

if (mCx - left > 0 && right - mCx > 0 && mCy - top > 0 && bottom - mCy > 0 && !mLoginPageLock && mSkipFlag) {

if (mWelcomAnimFragment.mWelcomAnimFragmentInterface != null) {

mWelcomAnimFragment.mWelcomAnimFragmentInterface.onSkip();

}

}

mSkipFlag = true;

mCx = 0;

mCy = 0;

}

ViewPager控件

/** * touch事件由顶层viewpager捕捉,手动分发到两个子viewpager */

if (mWelcomAnimFragment.imageViewPager != null && !mWelcomAnimFragment.imageViewPager.mIsLockScoll) {

mWelcomAnimFragment.imageViewPager.onTouchEvent(ev);

}

if (mWelcomAnimFragment.textViewPager != null) {

mWelcomAnimFragment.textViewPager.onTouchEvent(ev);

}

if (mWelcomAnimFragment.mIsMoveParent) {

return super.onTouchEvent(ev);

}

通过mIsLockScoll控制imageViewPager是否响应触摸事件,实现了Req-3(Text1滑到Text2时,ChildViewPager#image不滑动,FirstFragment播放动画);

通过mIsMoveParent实现了Req-6(从Text4再向右滑,切换到LoginAnimFragment)。

动画

下面来看一看酷炫的动画是怎么实现的。

0818b9ca8b590ca3270a3433284dd417.png

这三个Fragment分别是LoginAnimImageFristFragment,LoginAnimImageSecondFragment,LoginAnimImageThridFragment。

以LoginAnimImageFristFragment为例做分析。

先看资源:

0818b9ca8b590ca3270a3433284dd417.png

0818b9ca8b590ca3270a3433284dd417.png

需要从第一个item滚动到第三个item的头部,涉及到如下问题:

图片缩放到恰当的比例;

精确滚动到第三个item头部;

经过一系列的计算,播放动画的代码如下:

@Override

public void playInAnim() {

if (mAnimStartY < 0) {

mAnimStartY = ViewHelper.getY(iv_scroll);

}

if(mObjectAnimator == null){

mObjectAnimator = ObjectAnimator.ofFloat(iv_scroll, "y", mMarginTopHeigth, -scrollMarginTopHeigth+mMarginTopHeigth);

}

if(mAnimatorSet == null){

mAnimatorSet = new AnimatorSet();

}

if(mAnimatorSet.isRunning()){

mAnimatorSet.cancel();

}

mAnimatorSet.play(mObjectAnimator);

mAnimatorSet.setDuration(3000);

mAnimatorSet.start();

}

第二、三页的动画实现类似(看到各种长宽计算有没有晕晕的感觉???刚看到代码我是晕了,找到一种更好的开发动画的方式就好了。。。)

总结

这个例子可以帮助你深入理解Touch事件的传递机制;

为了精确控制动画要进行繁杂的计算,无非是缩放、移动、旋转、隐藏,理解了就不觉得难;

感谢小红书的分享,感谢github,感谢所有支持开源的个人和组织~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是可以实现利用滚动视图实现效果很不错的用户指引界面功能。支持引导面的自动滑动和手动滑动。两个面之间的过渡是渐隐渐现(淡入淡出 fade in and fade out)的动画效果。指引面的文字有固定文字标题和随着面切换而切换的文字标题。这种用户指引界面用在Path 3.0 App中。由于用了auto layout,所以只支持iOS 6.0 以上。若在iOS 5.0或以下运行,面切换会有跳动的感觉。 使用方法: 设置每个面的图片以及相应的文字: // Init the pages texts, and pictures. ICETutorialPage *layer1 = [[ICETutorialPage alloc] initWithSubTitle:@"Picture 1" description:@"Champs-Elysées by night" pictureName:@"tutorial_background_00@2x.jpg"]; ICETutorialPage *layer2 = [[ICETutorialPage alloc] initWithSubTitle:@"Picture 2" description:@"The Eiffel Tower with cloudy weather" pictureName:@"tutorial_background_01@2x.jpg"]; [...] // Load into an array. NSArray *tutorialLayers = @[layer1,layer2,layer3,layer4,layer5]; 加载指引面: self.viewController = [[ICETutorialController alloc] initWithNibName:@"ICETutorialController_iPhone" bundle:nil andPages:tutorialLayers]; // Set the common styles, and start scrolling (auto scroll, and looping enabled by default) [self.viewController setCommonPageSubTitleStyle:subStyle]; [self.viewController setCommonPageDescriptionStyle:descStyle]; // Set button 1 action. [self.viewController setButton1Block:^(UIButton *button){ NSLog(@"Button 1 pressed."); }]; // Set button 2 action, stop the scrolling. __unsafe_unretained typeof(self) weakSelf = self; [self.viewController setButton2Block:^(UIButton *button){ NSLog(@"Button 2 pressed."); NSLog(@"Auto-scrolling stopped."); [weakSelf.viewController stopScrolling]; }]; // Run it. [self.viewController star
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值