InstaMaterial概念设计(3):feed卡片上的按钮、评论按钮

这篇文章是实现InstaMaterial的一部分,今天我们将仔细讲解上篇文章中跳过的细节。也就是说我们实现的还是视频中9-13秒这个时间段。

这是今天这篇文章完成之后的最终效果(棒棒糖以及棒棒糖之前):是youtube视频,没法看。暂时连不上。。。

初始化

没有什么大书特书的,我们只需为feed卡片元素中的按钮(喜欢以及评论按钮)加上图标就可以了。这一步的代码提交在这里this commit.完了之后,我们还是不忙着去实现新的东西(新的UI元素),还需要、、、

修正bug以及优化性能

是啊,即便是小如InstaMaterial 这样的demo级应用,你也总能找到提升的空间。

Toolbar theme

首先我们遗漏了Toolbar的样式,这就是为什么menu按钮(Toolbar左边的按钮)的按下颜色是默认的深色。如下:

(作者的目的是做的和视频一模一样,即便是颜色的深浅,个人认为没必要这么较真是吧)

下载按钮(ToolBar右边的按钮)的按下颜色是浅色的,因为它是使用的带selector的自定义view,selector的定义如下

menu_item_view.xml

但是这只在Lollipop上有效果(这里翻译可能有误,原文是This inconsistency appears only in Android Lollipop ),解决的办法很简单。只需在activity_comments.xml 和activity_main.xml的ToolBar控件中加上一行代码:

注:在toolbar中是这样使用的:

这样Toolbar上的所有元素都将有着继承自Dark.ActionBar主题的样式。

顺便说下,如果你对android主题和样式的定义感兴趣,想知道他们的区别,这篇文章值得一读-Styling Views on Android (Without Going Crazy).

按照上面的做了之后,menu的按下效果看起来就是这个样子了(只在Lollipop 中):

RecyclerView 元素的预加载

另一个问题是在app启动之后feed列表的滚动不太顺滑。几乎每次在滚到第二个卡片的时候都有卡顿。幸好,造成这个问题的原因简单。RecyclerView (以及其他基于adapter的view,比如ListView、GridView等)使用了缓存机制重用子view(简而言之就是,系统只将屏幕可见范围之内的元素保存在内存中,在滚动的时候不断的重用这些内存中已经存在的view,而不是新建view)。

这个机制在我们这里会导致一个问题,启动应用之后,在屏幕可见范围内,我们只有一张卡片可见(估计作者的屏幕比较小),当我们滚动的时候,RecyclerView找不到可以重用的view了,它将创建一个新的,因此在滑动到第二个feed的时候就会有一定的延时,但是第二个feed之后的滚动是流畅的,因为这个时候RecyclerView已经有能重用的view了。

如何解决这个问题?

好在本例使用的是LinearLayoutManager ,因此很简单。只需重写getExtraLayoutSpace()方法。根据官方文档的描述getExtraLayoutSpace将返回LayoutManager应该预留的额外空间(显示范围之外,应该额外缓存的空间)。

下面是实际效果:

在重写getExtraLayoutSpace()之前

重写之后:

Feed 卡片上的按钮

下面就让我们开始卡片上那些按钮(目前只有喜欢和评论按钮)的工作吧.从视频中的效果来看是非常赞的,圆形扩散的selector

Android Lollipop中的Ripple效果

按钮所使用的Selector无非就是棒棒糖中介绍的Ripple效果-一种水波扩散效果。已经有无数关于Ripple效果的文章,这里就不赘述了,我只给出两篇文章的链接:

我们项目中实现Ripple的方法是非常简单的:

在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的代码

<merge>标签消除冗余的view

实现

好了,现在来开始实现,从动画开始。实际上我们需要四个动画,发送状态两个(进入和退出),完成状态两个(进入和退出,不过是相反的方向):

发送状态:

滑出顶端

从顶端滑入

完成状态:

从底部滑入

滑出底部

现在来实现button的布局:

因为ViewAnimator本身是一个FrameLayout,因此需要使用<merge>标签来减少了一层view。

最后,我们只有一个需求了,当button切换到完成状态之后,隔两秒它会自动切换回去。

代码很简单:

init()方法(29行)将前面创建的布局inflate给了ViewAnimator。顺便可以去看下这篇文章proper Layout Inflation,里面讲解了些很可能被你忽略的细节。

为了防止在activity结束的时候按钮的状态还没有切换回来,onDetachedFromWindow()去掉了回调方法revertStateRunnable()。

其余的都非常简单,通过setInAnimation() and setOutAnimation()两个方法来实现进入和退出动画。

好了,刚刚我们完成了SendCommentButton 的实现。注:这部分最好根据文中提到的变量名方法名对照代码理解。

selector

和feed中的操作按钮一样,我们需要为SendCommentButton准备两个selector。棒棒糖的设备我们使用ripple效果。pre-21的设备我们制造出标准的按下效果和阴影效果就可以了,就像在第一篇文章中对浮动操作按钮的做法。

下面是两种xml的代码:

res/drawable-v21/btn_send_comment.xml:

btn_send_comment_v21.xml hosted with ❤ by GitHub

res/drawable/btn_send_comment.xml:

btn_send_comment.xml hosted with ❤ by GitHub

错误振动提示

最后,我们将增加一个视频中没有出现的效果-当发送评论时如果输入框中没有内容,则播放振动动画。下面是效果图:

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插值器来重复播放这个动画,只有几行代码:

shake_error.xml hosted with ❤ by GitHub

res/anim/cycle_2.xml:

cycle_2.xml hosted with ❤ by GitHub

通过如下的代码来将这个动画应用到按钮中:

这就是今天所讲的全部内容了,这里是含有SendCommentButton类的最后一次提交:last commit 。下篇文章中我们将继续实现概念视频中的效果。

源码

讨论中例子的源码在这里:repository.

1 收藏 评论

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部