转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992

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工具看一下“知乎”原生是怎么实现的



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

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

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

[java] view plain copy
  1. /**
  2. *显示工具栏
  3. */
  4. privatevoidshowTools(){
  5. ObjectAnimatoranim=ObjectAnimator.ofFloat(img_tools,"y",img_tools.getY(),
  6. img_tools.getY()-img_tools.getHeight());
  7. anim.setDuration(TIME_ANIMATION);
  8. anim.start();
  9. isToolsHide=false;
  10. }
  11. /**
  12. *隐藏工具栏
  13. */
  14. privatevoidhideTools(){
  15. ObjectAnimatoranim=ObjectAnimator.ofFloat(img_tools,"y",img_tools.getY(),
  16. img_tools.getY()+img_tools.getHeight());
  17. anim.setDuration(TIME_ANIMATION);
  18. anim.start();
  19. isToolsHide=true;
  20. }

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

[java] view plain copy
  1. mScroller.setOnTouchListener(newView.OnTouchListener(){
  2. @Override
  3. publicbooleanonTouch(Viewv,MotionEventevent){
  4. switch(event.getAction()){
  5. caseMotionEvent.ACTION_DOWN:
  6. lastY=event.getY();
  7. break;
  8. caseMotionEvent.ACTION_MOVE:
  9. floatdisY=event.getY()-lastY;
  10. //垂直方向滑动
  11. if(Math.abs(disY)>viewSlop){
  12. //是否向上滑动
  13. isUpSlide=disY<0;
  14. //实现底部tools的显示与隐藏
  15. if(isUpSlide){
  16. if(!isToolsHide)
  17. hideTools();
  18. }else{
  19. if(isToolsHide)
  20. showTools();
  21. }
  22. }
  23. break;
  24. }
  25. returnfalse;
  26. }
  27. });

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


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

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

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

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

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



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

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

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

[java] view plain copy
  1. //获取Bar和Title的高度,完成auther布局的margenTop设置
  2. ViewTreeObserverviewTreeObserver=fl_top.getViewTreeObserver();
  3. viewTreeObserver.addOnPreDrawListener(newViewTreeObserver.OnPreDrawListener(){
  4. @Override
  5. publicbooleanonPreDraw(){
  6. if(!hasMeasured){
  7. FrameLayout.LayoutParamslayoutParams=newFrameLayout.LayoutParams(FrameLayout
  8. .LayoutParams.MATCH_PARENT,FrameLayout.LayoutParams.WRAP_CONTENT);
  9. layoutParams.setMargins(0,img_bar.getHeight()+tv_title.getHeight(),0,0);
  10. img_author.setLayoutParams(layoutParams);
  11. hasMeasured=true;
  12. }
  13. returntrue;
  14. }
  15. });

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

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

[java] view plain copy
  1. /**
  2. *Createdbyzhaokaiqiangon15/2/26.
  3. */
  4. publicclassMyScrollViewextendsScrollView{
  5. privateBottomListenerbottomListener;
  6. privateonScrollListenerscrollListener;
  7. publicMyScrollView(Contextcontext){
  8. this(context,null);
  9. }
  10. publicMyScrollView(Contextcontext,AttributeSetattrs){
  11. super(context,attrs);
  12. }
  13. protectedvoidonScrollChanged(intl,intt,intoldl,intoldt){
  14. super.onScrollChanged(l,t,oldl,oldt);
  15. if(getScrollY()+getHeight()>=computeVerticalScrollRange()){
  16. if(null!=bottomListener){
  17. bottomListener.onBottom();
  18. }
  19. }
  20. if(null!=scrollListener){
  21. scrollListener.onScrollChanged(l,t,oldl,oldt);
  22. }
  23. }
  24. publicvoidsetBottomListener(BottomListenerbottomListener){
  25. this.bottomListener=bottomListener;
  26. }
  27. publicvoidsetScrollListener(onScrollListenerscrollListener){
  28. this.scrollListener=scrollListener;
  29. }
  30. publicinterfaceonScrollListener{
  31. publicvoidonScrollChanged(intl,intt,intoldl,intoldt);
  32. }
  33. publicinterfaceBottomListener{
  34. publicvoidonBottom();
  35. }
  36. }

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


[html] view plain copy
  1. mScroller.setBottomListener(this);
  2. mScroller.setScrollListener(this);

[java] view plain copy
  1. /**
  2. *显示上部的布局
  3. */
  4. privatevoidshowTop(){
  5. ObjectAnimatoranim1=ObjectAnimator.ofFloat(img_bar,"y",img_bar.getY(),
  6. 0);
  7. anim1.setDuration(TIME_ANIMATION);
  8. anim1.start();
  9. ObjectAnimatoranim2=ObjectAnimator.ofFloat(tv_title,"y",tv_title.getY(),
  10. img_bar.getHeight());
  11. anim2.setInterpolator(newDecelerateInterpolator());
  12. anim2.setDuration(TIME_ANIMATION+200);
  13. anim2.start();
  14. ObjectAnimatoranim4=ObjectAnimator.ofFloat(fl_top,"y",fl_top.getY(),
  15. 0);
  16. anim4.setDuration(TIME_ANIMATION);
  17. anim4.start();
  18. isTopHide=false;
  19. }
  20. /**
  21. *隐藏上部的布局
  22. */
  23. privatevoidhideTop(){
  24. ObjectAnimatoranim1=ObjectAnimator.ofFloat(img_bar,"y",0,
  25. -img_bar.getHeight());
  26. anim1.setDuration(TIME_ANIMATION);
  27. anim1.start();
  28. ObjectAnimatoranim2=ObjectAnimator.ofFloat(tv_title,"y",tv_title.getY(),
  29. -tv_title.getHeight());
  30. anim2.setDuration(TIME_ANIMATION);
  31. anim2.start();
  32. ObjectAnimatoranim4=ObjectAnimator.ofFloat(fl_top,"y",0,
  33. -(img_bar.getHeight()+tv_title.getHeight()));
  34. anim4.setDuration(TIME_ANIMATION);
  35. anim4.start();
  36. isTopHide=true;
  37. }
  38. @Override
  39. publicvoidonBottom(){
  40. if(isToolsHide){
  41. showTools();
  42. }
  43. }
  44. @Override
  45. publicvoidonScrollChanged(intl,intt,intoldl,intoldt){
  46. if(t<=dp2px(TOP_DISTANCE_Y)&&isTopHide&&isAnimationFinish){
  47. showTop();
  48. Log.d(TAG,"显示");
  49. }elseif(t>dp2px(TOP_DISTANCE_Y)&&!isTopHide&&isAnimationFinish){
  50. hideTop();
  51. Log.d(TAG,"隐藏");
  52. }
  53. }

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


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

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


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

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

这里给出两种方案。

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

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


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

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

更多相关文章

  1. Android(安卓)抽象布局include merge Viewstub
  2. Android(安卓)Fragment使用
  3. Android(安卓)数据存储 利用SQLiteDatabase实现简单的学生管理
  4. Android实现拨打电话功能
  5. 【Android】界面布局之 LinearLayout(线性布局)
  6. Android那点事-系列之(一)Parcelable和Serializable的区别与使用
  7. Android(安卓)之数据的传递与传回
  8. Android:自定义滚动边缘(EdgeEffect)效果
  9. Android四大布局及属性

随机推荐

  1. android 工程横竖屏设置无效 cocos2dx v3
  2. 【Android每周专题】Android中的逆向工程
  3. Android关于ThreadLocal的思考和总结
  4. [置顶] Android(安卓)从硬件到应用:一步一
  5. 【专题报道】Google I/O开发者大会
  6. Android(安卓)自定义控件入门篇之自定义
  7. Android(安卓)进程保活手段分析
  8. Android:多语言对应
  9. Android创建和使用数据库
  10. Android(安卓)中LayoutInflater(布局加载