这是今天这篇文章完成之后的最终效果(棒棒糖以及棒棒糖之前):是youtube视频,没法看。暂时连不上。。。
这个机制在我们这里会导致一个问题,启动应用之后,在屏幕可见范围内,我们只有一张卡片可见(估计作者的屏幕比较小),当我们滚动的时候,RecyclerView找不到可以重用的view了,它将创建一个新的,因此在滑动到第二个feed的时候就会有一定的延时,但是第二个feed之后的滚动是流畅的,因为这个时候RecyclerView已经有能重用的view了。
下面是实际效果: 在重写getExtraLayoutSpace()之前
在feed_item.xml中将下面的drawable作为按钮的背景
res/drawable-v21/btn_feed_action.xml
我当然知道可以使用诸如mimic ripple effect 这样的库来兼容老设备,但是没有一个库达到了该有的效果。它们需要添加额外的代码(比如添加额外的布局来包裹),在Lollipop版本上无法使用原生的Ripple 效果,性能问题等等。
But why is so hard to copy Ripple effect into pre-21 Android?
但是为什么在pre-21的设备上复制Ripple 效果会这么难呢?
随着app的布局日益复杂,UI需要更多的时间去绘制、测量。现在问题来了,如果我们的动画执行到一半,而开启了另外一个UI任务(比如为新的activity inflat布局),但是只有一个UI线程,那么动画将被停止。
Lollipop中所引入的Render线程将解决这个问题。Render线程通过将渲染分成两部分来解决,简单的来说就是:我们有一个被UI线程创建的动画单元的列表,这些动画将被甩到独立的render线程当中。因此在执行开销较大的UI操作的时候,动画也能继续下去。
这就是ripple效果的工作原理。他们是在render线程中执行的。所以不会被打开新的activity这样的操作打断。
所以没法在pre-21的安卓系统上完全实现ripple效果。
res/drawable/btn_feed_action.xml:
注:评论发布按钮对应的类是SendCommentButton.java,这一节中讲解的内容都是在这个类中。结合代码更容易看明白。
关于这个按钮SendCommentButton,我将用到下面几个android元素:
ViewAnimator:作为SendCommentButton的基类,它有一个对我们非常有用的特性,可以设置子view切换时的进入和退出效果。如果你对ViewAnimator很陌生,其直接子类ViewFlipper, ViewSwitcher或间接子类ImageSwitcher, TextSwitcher应该很熟悉。
自定义view:包括xml以及inflate xml的代码
标签消除冗余的view
发送状态:
滑出顶端
从顶端滑入
完成状态:
从底部滑入
滑出底部
现在来实现button的布局:
因为ViewAnimator本身是一个FrameLayout,因此需要使用标签来减少了一层view。
最后,我们只有一个需求了,当button切换到完成状态之后,隔两秒它会自动切换回去。
代码很简单:
init()方法(29行)将前面创建的布局inflate给了ViewAnimator。顺便可以去看下这篇文章proper Layout Inflation,里面讲解了些很可能被你忽略的细节。
为了防止在activity结束的时候按钮的状态还没有切换回来,onDetachedFromWindow()去掉了回调方法revertStateRunnable()。
其余的都非常简单,通过setInAnimation() and setOutAnimation()两个方法来实现进入和退出动画。
好了,刚刚我们完成了SendCommentButton 的实现。注:这部分最好根据文中提到的变量名方法名对照代码理解。
下面是两种xml的代码:
res/drawable-v21/btn_send_comment.xml:
res/drawable/btn_send_comment.xml:
Implementation is pretty simple - we have to create shake animation and custom CycleInterpolator for repeating this animation. Everything is in a few lines of code:
res/anim/shake_error.xml:
实现很简单-创建一个振动动画并使用自定义的CycleInterpolator插值器来重复播放这个动画,只有几行代码:
res/anim/cycle_2.xml:
通过如下的代码来将这个动画应用到按钮中: btnSendComment.startAnimation(AnimationUtils.loadAnimation(this, R.anim.shake_error)); 这就是今天所讲的全部内容了,这里是含有SendCommentButton类的最后一次提交:last commit 。下篇文章中我们将继续实现概念视频中的效果.
源码下载:repository.
本文原出处:http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0209/2451.html
初始化
没有什么大书特书的,我们只需为feed卡片元素中的按钮(喜欢以及评论按钮)加上图标就可以了。这一步的代码提交在这里this commit.完了之后,我们还是不忙着去实现新的东西(新的UI元素),还需要、、、修正bug以及优化性能
是啊,即便是小如InstaMaterial 这样的demo级应用,你也总能找到提升的空间。Toolbar theme
首先我们遗漏了Toolbar的样式,这就是为什么menu按钮(Toolbar左边的按钮)的按下颜色是默认的深色。如下: (作者的目的是做的和视频一模一样,即便是颜色的深浅,个人认为没必要这么较真是吧) 下载按钮(ToolBar右边的按钮)的按下颜色是浅色的,因为它是使用的带selector的自定义view,selector的定义如下 menu_item_view.xmlandroid:background="@drawable/btn_default_light"但是这只在Lollipop上有效果(这里翻译可能有误,原文是This inconsistency appears only in Android Lollipop ),解决的办法很简单。只需在activity_comments.xml 和activity_main.xml的ToolBar控件中加上一行代码:
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"注:在toolbar中是这样使用的:
这样Toolbar上的所有元素都将有着继承自Dark.ActionBar主题的样式。 顺便说下,如果你对android主题和样式的定义感兴趣,想知道他们的区别,这篇文章值得一读-Styling Views on Android (Without Going Crazy). 按照上面的做了之后,menu的按下效果看起来就是这个样子了(只在Lollipop 中):layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:elevation="@dimen/default_elevation" app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
RecyclerView 元素的预加载
另一个问题是在app启动之后feed列表的滚动不太顺滑。几乎每次在滚到第二个卡片的时候都有卡顿。幸好,造成这个问题的原因简单。RecyclerView (以及其他基于adapter的view,比如ListView、GridView等)使用了缓存机制重用子view(简而言之就是,系统只将屏幕可见范围之内的元素保存在内存中,在滚动的时候不断的重用这些内存中已经存在的view,而不是新建view)。这个机制在我们这里会导致一个问题,启动应用之后,在屏幕可见范围内,我们只有一张卡片可见(估计作者的屏幕比较小),当我们滚动的时候,RecyclerView找不到可以重用的view了,它将创建一个新的,因此在滑动到第二个feed的时候就会有一定的延时,但是第二个feed之后的滚动是流畅的,因为这个时候RecyclerView已经有能重用的view了。
如何解决这个问题?
好在本例使用的是LinearLayoutManager ,因此很简单。只需重写getExtraLayoutSpace()方法。根据官方文档的描述getExtraLayoutSpace将返回LayoutManager应该预留的额外空间(显示范围之外,应该额外缓存的空间)。LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this) { @Override protected int getExtraLayoutSpace(RecyclerView.State state) { return 300; } };
下面是实际效果: 在重写getExtraLayoutSpace()之前
Feed 卡片上的按钮
下面就让我们开始卡片上那些按钮(目前只有喜欢和评论按钮)的工作吧.从视频中的效果来看是非常赞的,圆形扩散的selectorAndroid Lollipop中的Ripple效果
按钮所使用的Selector无非就是棒棒糖中介绍的Ripple效果-一种水波扩散效果。已经有无数关于Ripple效果的文章,这里就不赘述了,我只给出两篇文章的链接:- Ripples – Part 1 and Ripples – Part 2 from Styling Android blog
- Implementing Material Design in Your Android app from official Android Developers Blog
在feed_item.xml中将下面的drawable作为按钮的背景
res/drawable-v21/btn_feed_action.xml
不要在L之前的设备上使用ripples效果
我承诺过我将实现概念视频中的所有效果,但是有时候,去做一件达不到预期的事情,还不如不做。在pre-21的设备上实现ripple就是一件达不到预期的事情。我当然知道可以使用诸如mimic ripple effect 这样的库来兼容老设备,但是没有一个库达到了该有的效果。它们需要添加额外的代码(比如添加额外的布局来包裹),在Lollipop版本上无法使用原生的Ripple 效果,性能问题等等。
But why is so hard to copy Ripple effect into pre-21 Android?
但是为什么在pre-21的设备上复制Ripple 效果会这么难呢?
Ripple揭秘
在棒棒糖版本之前,整个UI都是在UI主线程中管理的。几乎每个人都知道ANR对话框,NetworkOnMainThreadException,我们也是知道“不要将耗时操作放在UI线程中,仅仅在UI线程中显示操作结果”这条黄金定律。一切都看似可行,除了那句“整个UI都是被UI主线程所管理的”。随着app的布局日益复杂,UI需要更多的时间去绘制、测量。现在问题来了,如果我们的动画执行到一半,而开启了另外一个UI任务(比如为新的activity inflat布局),但是只有一个UI线程,那么动画将被停止。
Lollipop中所引入的Render线程将解决这个问题。Render线程通过将渲染分成两部分来解决,简单的来说就是:我们有一个被UI线程创建的动画单元的列表,这些动画将被甩到独立的render线程当中。因此在执行开销较大的UI操作的时候,动画也能继续下去。
这就是ripple效果的工作原理。他们是在render线程中执行的。所以不会被打开新的activity这样的操作打断。
所以没法在pre-21的安卓系统上完全实现ripple效果。
老版本上的兼容“ripple”效果
因为不能在pre-21上实现ripple,那么我们就实现类似的效果就可以了。我们创建了一个带进入退出渐变的圆形的selector,尽管没有ripple那么花哨,但是看起来还是可以.res/drawable/btn_feed_action.xml:
评论发布按钮
评论发布按钮非常有趣。正如你在视频中看到的,按钮可以在两个状态之间做简单的动画切换,并且点击的时候有ripple效果。注:评论发布按钮对应的类是SendCommentButton.java,这一节中讲解的内容都是在这个类中。结合代码更容易看明白。
关于这个按钮SendCommentButton,我将用到下面几个android元素:
ViewAnimator:作为SendCommentButton的基类,它有一个对我们非常有用的特性,可以设置子view切换时的进入和退出效果。如果你对ViewAnimator很陌生,其直接子类ViewFlipper, ViewSwitcher或间接子类ImageSwitcher, TextSwitcher应该很熟悉。
自定义view:包括xml以及inflate xml的代码
实现
好了,现在来开始实现,从动画开始。实际上我们需要四个动画,发送状态两个(进入和退出),完成状态两个(进入和退出,不过是相反的方向):发送状态:
滑出顶端
从顶端滑入
完成状态:
从底部滑入
滑出底部
现在来实现button的布局:
因为ViewAnimator本身是一个FrameLayout,因此需要使用
最后,我们只有一个需求了,当button切换到完成状态之后,隔两秒它会自动切换回去。
代码很简单:
init()方法(29行)将前面创建的布局inflate给了ViewAnimator。顺便可以去看下这篇文章proper Layout Inflation,里面讲解了些很可能被你忽略的细节。
为了防止在activity结束的时候按钮的状态还没有切换回来,onDetachedFromWindow()去掉了回调方法revertStateRunnable()。
其余的都非常简单,通过setInAnimation() and setOutAnimation()两个方法来实现进入和退出动画。
好了,刚刚我们完成了SendCommentButton 的实现。注:这部分最好根据文中提到的变量名方法名对照代码理解。
设计
和feed中的操作按钮一样,我们需要为SendCommentButton准备两个selector。棒棒糖的设备我们使用ripple效果。pre-21的设备我们制造出标准的按下效果和阴影效果就可以了,就像在第一篇文章中对浮动操作按钮的做法。下面是两种xml的代码:
res/drawable-v21/btn_send_comment.xml:
res/drawable/btn_send_comment.xml:
错误振动提示
最后,我们将增加一个视频中没有出现的效果-当发送评论时如果输入框中没有内容,则播放振动动画。下面是效果图:Implementation is pretty simple - we have to create shake animation and custom CycleInterpolator for repeating this animation. Everything is in a few lines of code:
res/anim/shake_error.xml:
实现很简单-创建一个振动动画并使用自定义的CycleInterpolator插值器来重复播放这个动画,只有几行代码:
res/anim/cycle_2.xml:
xml version="1.0" encoding="utf-8"?>
通过如下的代码来将这个动画应用到按钮中: btnSendComment.startAnimation(AnimationUtils.loadAnimation(this, R.anim.shake_error)); 这就是今天所讲的全部内容了,这里是含有SendCommentButton类的最后一次提交:last commit 。下篇文章中我们将继续实现概念视频中的效果.
源码下载:repository.
本文原出处:http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0209/2451.html
收藏的用户(0) X
正在加载信息~
推荐阅读
最新回复 (0)
站点信息
- 文章2295
- 用户1336
- 访客10465655
每日一句
Wisdom begins in wonder.
智慧始于好奇。
智慧始于好奇。
新会员