0
点赞
收藏
分享

微信扫一扫

Android仿“知乎”隐藏标题栏、回答详情页动画效果


2014已经远去,2015年的目标很简单,就是继续熟悉Android的上层API,虽然偶尔会为了某个问题去研究下FrameWork的代码,但是对于我们这种新手来说,只有对上层的API用的熟练了,才能更好的往下研究原理。所以,今年的任务就是继续学习和研究Android的上层API的使用,顺便写一篇毕业论文,然后毕个业。

    OK,从这篇开始,咱们就开始【凯子哥带你夯实应用层】系列,如果你有想要实现的界面效果,或者是有一些开发中的疑问,请私信我,如果我觉得比较好的话,会自己实现一下,然后写blog和大家分享实现思路。

    废话不多说,咱们第一篇文章就是模仿“知乎”的回答详情页的动画效果,先上个原版的效果图,咱们就是要做出这个效果

 

    在实现之前,我们先根据上面的动画效果,研究下需求,因为gif帧数有限,所以不是很连贯,推荐你直接下载一个知乎,找到这个界面自己玩玩

☞当文章往上移动到一定位置之后,最上面的标题栏Bar和问题布局Title是会隐藏的,回答者Author布局不会隐藏

☞当文章往下移动移动到一定位置之后,原先隐藏的标题栏Bar和问题布局Title会下降显示

☞当文章往上移动的时候,下部隐藏的Tools布局会上升显示

☞当文章往下移动的时候,如果Tools布局是显示的,则隐藏

☞当标题栏Bar和问题布局Title下降显示的时候,Title是从Bar的下面出来的,有个遮挡的效果

☞当快速滑动内容到达底部的时候,隐藏的Tools会显示出来

☞当快速滑动内容到顶部的时候,隐藏的Bar和Title也会显示出来

 

    不分析不知道,这样一个简单地效果,经过分析需要完成不少东西呢,那么下面根据要实现的需求,咱们分析一下解决方案。

    在做这种仿界面之前,我们可以使用ADT带的View Hierarchy工具看一下“知乎”原生是怎么实现的

Android仿“知乎”隐藏标题栏、回答详情页动画效果_java

 

    从右边的分析图可以看出,知乎的这个界面,内容用的WebView,这很正常,因为用户的回答里面格式比较复杂,用WebView是最好的解决方案,而标题栏是一个VIew,是ActionBar还是自定义View呢,不得而知,下面是就是一个LinearLayout包了4个ToggleButton,布局很简单,我们没有WebView,所以使用ScrollView代替,上面的布局直接ImageView了,设置个src,模拟一个布局。

    其实布局很简单,咱们一个效果一个效果的来实现。

    首先是下面的Tools如何显示和隐藏呢?当然是用动画了!什么动画呢?能实现的有属性动画和帧动画,属性动画能够真实的改变View的属性,帧动画只是视觉上移动了,View的实际属性并不改变,这两种都可以,我们这里使用属性动画

 



1. /**
2.      * 显示工具栏
3.      */  
4. private void showTools() {  
5.   
6. "y", img_tools.getY(),  
7.                 img_tools.getY() - img_tools.getHeight());  
8.         anim.setDuration(TIME_ANIMATION);  
9.         anim.start();  
10.   
11. false;  
12.     }  
13.   
14. /**
15.      * 隐藏工具栏
16.      */  
17. private void hideTools() {  
18.   
19. "y", img_tools.getY(),  
20.                 img_tools.getY() + img_tools.getHeight());  
21.         anim.setDuration(TIME_ANIMATION);  
22.         anim.start();  
23.   
24. true;  
25.   
26.     }



    那么什么时候调用呢?从上面的需求分析中,我们知道,用户手指下拉的时候,Tools显示,反之隐藏,那么我们就可以监听ScrollView的onTouch,判断手指方向,实现动画效果的调用

 

 



 



1. mScroller.setOnTouchListener(new View.OnTouchListener() {  
2. @Override  
3. public boolean onTouch(View v, MotionEvent event) {  
4.   
5.   
6. switch (event.getAction()) {  
7.   
8. case MotionEvent.ACTION_DOWN:  
9.                         lastY = event.getY();  
10. break;  
11. case MotionEvent.ACTION_MOVE:  
12.   
13. float disY = event.getY() - lastY;  
14.   
15. //垂直方向滑动  
16. if (Math.abs(disY) > viewSlop) {  
17. //是否向上滑动  
18. 0;  
19.   
20. //实现底部tools的显示与隐藏  
21. if (isUpSlide) {  
22. if (!isToolsHide)  
23.                                     hideTools();  
24. else {  
25. if (isToolsHide)  
26.                                     showTools();  
27.                             }  
28.                         }  
29.   
30. break;  
31.                 }  
32.   
33. return false;  
34.             }  
35.         });



     用变量isToolsHide放置代码重复调用。

 

 

    下面的Tools的问题解决了,我们再看一下上面的布局动画如何来实现。上面的思路和下面一样,也是通过属性动画,完成位置的移动,移动的布局有Bar、Title和根布局。为什么答题人布局Author不移动呢?因为根布局必须移动,否则就会挡住下面的文字内容,根布局的移动会让子布局都跟着移动,所以只移动根布局即可。

    对了,为了更大范围的现实文本,“知乎”的WebView是占据整个布局的,其他布局都是在根布局FrameLayout里面,所以,在首次加载的时候,下面的文本在开头需要留出一定的间隔,防止被遮挡,当上面的布局隐藏之后,就没有问题了。

    在简单分析之后,我再给出实现的布局的代码

 


1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
2. android:layout_width="match_parent"  
3. android:layout_height="match_parent"  
4. android:background="@android:color/white"  
5. >  
6.   
7.   
8. <com.socks.zhihudetail.MyScrollView  
9. android:id="@+id/scroller"  
10. android:layout_width="match_parent"  
11. android:layout_height="wrap_content"  
12. >  
13.   
14. <TextView  
15. android:layout_width="match_parent"  
16. android:layout_height="match_parent"  
17. android:textSize="16sp"  
18. android:textColor="@android:color/black"  
19. android:text="@string/hello_world"/>  
20.   
21. </com.socks.zhihudetail.MyScrollView>  
22.   
23.   
24. <FrameLayout  
25. android:id="@+id/ll_top"  
26. android:layout_width="match_parent"  
27. android:layout_height="wrap_content"  
28. android:background="@android:color/white"  
29. android:orientation="vertical"  
30. android:layout_gravity="top">  
31.   
32. <ImageView  
33. android:id="@+id/img_author"  
34. android:layout_width="match_parent"  
35. android:layout_height="80dp"  
36. android:scaleType="fitXY"  
37. android:src="@drawable/bg_author"/>  
38.   
39. <TextView  
40. android:id="@+id/tv_title"  
41. android:layout_width="match_parent"  
42. android:layout_height="wrap_content"  
43. android:layout_marginTop="55dp"  
44. android:text="为什么美国有那么多肌肉极其强大的肌肉男?"  
45. android:textSize="18sp"  
46. android:background="#DBDBDB"  
47. android:gravity="center|left"  
48. android:paddingLeft="15dp"  
49. android:paddingRight="15dp"  
50. android:paddingTop="5dp"  
51. android:paddingBottom="5dp"  
52. android:textColor="@android:color/darker_gray"  
53. />  
54.   
55. <ImageView  
56. android:id="@+id/img_bar"  
57. android:layout_width="match_parent"  
58. android:layout_height="55dp"  
59. android:scaleType="fitXY"  
60. android:src="@drawable/bg_actionbar"/>  
61.   
62. </FrameLayout>  
63.   
64. <ImageView  
65. android:id="@+id/img_tools"  
66. android:layout_width="match_parent"  
67. android:layout_height="wrap_content"  
68. android:scaleType="fitXY"  
69. android:layout_gravity="bottom"  
70. android:src="@drawable/bg_bottom"/>  
71.   
72.   
73. </FrameLayout>



    效果图如下,文本留了一些空行,保证不被遮挡。

 

Android仿“知乎”隐藏标题栏、回答详情页动画效果_java_02

 

    有的同学看了上面的效果图可能会疑惑,这里为什么没有答题人的布局呢?

    其实是这样的,为了模拟上部的布局显示时,Title从Bar下面出现的效果,所以特意这样设计的。我试过用linearLayout实现,效果也是可以实现的,但是当Title往下移动显示的时候,会覆盖在Bar上面,这也很好理解,LinearLayout没有层次顺序,所以会遮挡。我试过View.bringToFront(),试图把Bar的布局提高层次,但是这样会导致布局的紊乱,在首次加载的时候,Bar会显示在最下面,是因为提高层次之后,Bar的布局重新计算,所以不按照LinearLayout的布局规则来了。无奈之下,换成了Framelayout,但是又出现了问题,Bar的高度可以设置,但是Title的高度会随着文本的增加而改变,这样一来,最下面Author的布局的位置就不能设置了,因为不知道距离上面多远,所以我们只能在代码里面动态的计算Bar和Title的高度,然后在界面加载的时候,动态的给Author的布局设置MargenTop,保证位置的正确。

    因为在onCreate里面,还没有开始View的绘制,所以得不到控件的真实高度,我们可以用下面的方法,获取这个时期的高度

 



 


1. //获取Bar和Title的高度,完成auther布局的margenTop设置  
2.         ViewTreeObserver viewTreeObserver = fl_top.getViewTreeObserver();  
3. new ViewTreeObserver.OnPreDrawListener() {  
4. @Override  
5. public boolean onPreDraw() {  
6.   
7. if (!hasMeasured) {  
8. new FrameLayout.LayoutParams(FrameLayout  
9.                             .LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);  
10. 0, img_bar.getHeight() + tv_title.getHeight(), 0, 0);  
11.                     img_author.setLayoutParams(layoutParams);  
12. true;  
13.                 }  
14. return true;  
15.             }  
16.         });


     获取了高度之后,我们就可以正确地设置位置了。但是,如果保证上面的布局随着我们的内容的移动,而改变现实状态呢?

 

    经过我手动直观测试,知乎的这个界面是根据一个固定的值,来改变显示状态的,因此,我们可以监听ScrollView的滑动距离,来判断。但是ScrollView并没有给我们这样一个监听器,咋办?重写!

 


 



 

1. /**
2.  * Created by zhaokaiqiang on 15/2/26.
3.  */  
4. public class MyScrollView extends ScrollView {  
5.   
6. private BottomListener bottomListener;  
7.   
8. private onScrollListener scrollListener;  
9.   
10.   
11. public MyScrollView(Context context) {  
12. this(context, null);  
13.     }  
14.   
15. public MyScrollView(Context context, AttributeSet attrs) {  
16. super(context, attrs);  
17.     }  
18.   
19. protected void onScrollChanged(int l, int t, int oldl, int oldt) {  
20. super.onScrollChanged(l, t, oldl, oldt);  
21. if (getScrollY() + getHeight() >= computeVerticalScrollRange()) {  
22.   
23. if (null != bottomListener) {  
24.                 bottomListener.onBottom();  
25.             }  
26.   
27.         }  
28.   
29. if (null != scrollListener) {  
30.             scrollListener.onScrollChanged(l, t, oldl, oldt);  
31.         }  
32.   
33.   
34.     }  
35.   
36. public void setBottomListener(BottomListener bottomListener) {  
37. this.bottomListener = bottomListener;  
38.     }  
39.   
40. public void setScrollListener(onScrollListener scrollListener) {  
41.   
42. this.scrollListener = scrollListener;  
43.   
44.     }  
45.   
46.   
47. public interface onScrollListener {  
48.   
49. public void onScrollChanged(int l, int t, int oldl, int oldt);  
50.   
51.     }  
52.   
53.   
54. public interface BottomListener {  
55.   
56. public void onBottom();  
57.   
58.     }  
59.   
60.   
61. }



    我们只需要重写onScrollChange()方法即可,在里面不光可以时时的得到位置的变化,还添加了一个BottomListener接口来监听滑动到底部的事件,写好之后就很简单了

 

 

 


 


1. mScroller.setBottomListener(this);  
2. mScroller.setScrollListener(this);


 


 

1. /**
2.      * 显示上部的布局
3.      */  
4. private void showTop() {  
5.   
6. "y", img_bar.getY(),  
7. 0);  
8.         anim1.setDuration(TIME_ANIMATION);  
9.         anim1.start();  
10.   
11. "y", tv_title.getY(),  
12.                 img_bar.getHeight());  
13. new DecelerateInterpolator());  
14. 200);  
15.         anim2.start();  
16.   
17. "y", fl_top.getY(),  
18. 0);  
19.         anim4.setDuration(TIME_ANIMATION);  
20.         anim4.start();  
21.   
22. false;  
23.     }  
24.   
25.   
26. /**
27.      * 隐藏上部的布局
28.      */  
29. private void hideTop() {  
30.   
31. "y", 0,  
32.                 -img_bar.getHeight());  
33.         anim1.setDuration(TIME_ANIMATION);  
34.         anim1.start();  
35.   
36. "y", tv_title.getY(),  
37.                 -tv_title.getHeight());  
38.         anim2.setDuration(TIME_ANIMATION);  
39.         anim2.start();  
40.   
41. "y", 0,  
42.                 -(img_bar.getHeight() + tv_title.getHeight()));  
43.         anim4.setDuration(TIME_ANIMATION);  
44.         anim4.start();  
45.   
46. true;  
47.     }  
48.   
49. @Override  
50. public void onBottom() {  
51. if (isToolsHide) {  
52.             showTools();  
53.         }  
54.     }  
55.   
56. @Override  
57. public void onScrollChanged(int l, int t, int oldl, int oldt) {  
58.   
59. if (t <= dp2px(TOP_DISTANCE_Y) && isTopHide && isAnimationFinish) {  
60.             showTop();  
61. "显示");  
62. else if (t > dp2px(TOP_DISTANCE_Y) && !isTopHide && isAnimationFinish) {  
63.             hideTop();  
64. "隐藏");  
65.         }  
66.     }


    我们只需要根据当前的位置,来实现布局的显示和隐藏就可以啦!

 

 

    OK,这篇文章就到这里,如果你有疑问或者是建议,都可以评论或者是私信。

    下载地址:https://github.com/ZhaoKaiQiang/ZhiHuDetailDemo

 

----------------------------------------分割线---------------------------------------------

    咱们上面咱们是用ScrollView代替的WebView,有的同学可能问了,如果也要用WebView咋办呢?

    这里给出两种方案。

    第一种,JS和Android交互,用JS传递网页的移动位置,没试过,只是一个思路,不知道能不能实现。

    第二种,就是集成WebView,他也有onScrollChanged(),剩下的就不用多说了~

----------------------------------------分割线--------------------------------------------- 

 

昨天又体验了下,发现知乎还有个功能,就是点击屏幕,可以实现上部布局和Tools布局的隐藏和显示,这个其实也不难,我们可以自己定义一个GestureDetector,然后实现Gesturedetector.SimpleOnGestureListener,onDown返回true,然后在onSingleTapConfirmed里面调用hideXXX和showXXX即可,仅提供思路,剩下的就自己去做吧,你一定可以的!

举报

相关推荐

0 条评论