android仿QQ个人主页下拉回弹效果

先看效果:

效果不错吧!

进入主题之前,先了解ImageView的scaleType的center_crop,网络上说的已经很清楚了 : 以下抄自网络:

1.Android:scaleType=”centerCrop”
以填满整个ImageView为目的,将原图的中心对准ImageView的中心,等比例放大原图,直到填满ImageView为止(指的是ImageView的宽和高都要填满),原图超过ImageView的部分作裁剪处理。

均衡的缩放图像(保持图像原始比例),使图片的两个坐标(宽、高)都大于等于 相应的视图坐标(负的内边距)。图像则位于视图的中央。 在XML 中可以使用的语法:android:scaleType=”centerCrop”。

不说废话,直接进入主题!!

思路

1.先将topView的布局和listview平级,然后将topview以及topview包裹的imageView中传listview,即一般是activity的layout
2.重写listView的ontoucEvent()方法,但不做任何拦截,只在action时,控制imageView以及topView的高度,使其重新layout然后重新布局就可以了。
3.以上是大概思路,这里具体分析:当action_down时记录其初始位置,action_move时得到dy,通过dy来判断是上啦还是下拉:
(1)dy>0,则是下拉,不断重新设置topView和imageView的高度,又因为imageView的scaleType=center_crop,所以图片会按照这个规则进行等比拉伸,当到达图片最大时就会有不断放大的过程
当松开手或者手指移出屏幕外时(action_up|action_outside|action_cancel)时让其回到初始位置,并伴着回弹过程,这里通过自定义动画让其具备回弹效果
(2)dy<0,则是上拉,上推的过程,由于topView和Imageview不具备滚动的效果,所以上推也是通过控制topView和ImageView的高度,并且当TopVIew和ImageView滑出屏幕时就不在更改高度防止不断的绘制,提高性能。
ok,大体思路就这样。具体分析代码如下:

实现:

activity的xml
stretch_act.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

 <!--这是topView-->
 <RelativeLayout
  android:id="@+id/rl_top"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  >

  <!--这是imageView,一定要设置scaleType为centerCrop-->
  <ImageView
   android:id="@+id/iv_stretch_pic"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:scaleType="centerCrop"
   android:src="@drawable/stretch_s"
   />

  <TextView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_alignBottom="@id/iv_stretch_pic"
   android:text="你最美,你最酷…………^^"
   android:textSize="16sp"/>

 </RelativeLayout>
<!--这是自定义的listview-->
 <com.example.zwr.myapplication.widget.StretchListView
  android:id="@+id/listview"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:cacheColorHint="@null"
  android:divider="@null"
  android:listSelector="#00000000"/>

</LinearLayout>

看看StretchListView:

/***
  * @param topView
  * @param imgResId 图片id
  */
 public void setTopView(View topView, int imgResId) {
  if (null != topView) {
   this.topView = topView;
   imageView = (ImageView) topView.findViewById(imgResId);
  }
 }

通过这个对外的方法,将topView以及ImageView的id传进来

分析:重新ListView的onTouchEvent():

ACTION_DOWN:
  case MotionEvent.ACTION_DOWN:
    startY = ev.getRawY();
    if (!hadInit) {//初始化,只要初始化一次就够了
     childAt0Top = getChildAt(0).getTop();
     ivInitHeight = imageView.getHeight();
     hadInit = true;
    }
    break;

只是进行一些初始化操作:

1. startY:相对于屏幕顶部的高度
 2. childAt0Top,获取listview的第一个view的top距离、
 3. ivInitHeight:获取ImageView的初始高度,即刚进来时的高度
ACTION_MOVE:

case MotionEvent.ACTION_MOVE:
    Log.d(TAG, "dy = " + dy);
    dy = ev.getRawY() - startY;
    if (dy > 0 && 0 == getFirstVisiblePosition() &&
    childAt0Top == getChildAt(0).getTop()) {//(1)手指从上往下拉:下拉
     int tempDy = (int) (dy + 0.5);
     //一定也要给topView增加一定的高度,否则从上啦到下拉就不会显示
     imageView.getLayoutParams().height = imageView.getHeight() + tempDy;
     topView.getLayoutParams().height = topView.getHeight() + tempDy;
     topView.requestLayout();
     isChangedHeight = true;

    } else {//(2)手指从下往上拉:上拉
     int tempDy = (int) (dy - 0.5);
     int currHeight = imageView.getHeight();
     float translationY = getNegativeMaxValue(tempDy, -currHeight, 0);
     if (translationY <= 0 && currHeight > 0) {
      LinearLayout.LayoutParams lp =
      (LinearLayout.LayoutParams) topView.getLayoutParams();
      //一定要减去titleBar,如果没有去掉Winow.xxx.Title,还要减去这个高度,否则会显示不全
      lp.height = topView.getHeight() + (int) translationY;
      topView.requestLayout();//
      isChangedHeight = true;
     }
    }
    //用这个getRawY而不是用getY,是因为listview也会随着改变,
    //而getY获取的就是listview本身的Y,所以基本是变化不大的,
    // 而使用getRawY相对于屏幕的距离,保证滑动了多大的距离就改变多大的距离
    startY = ev.getRawY();
    break;

当下拉时:主要条件如下:

1.dy > 0 && 0 == getFirstVisiblePosition() && childAt0Top ==
getChildAt(0).getTop()
意思是当下拉时,并且listview的第一个位置显示全了,才能下拉放大图片,这是避免,listview已经发生滚动了,需要回到初始位置才能下拉放大,否则会出现,立即下拉放大,体验不好
2.当上拉时 主要条件
if (translationY <= 0 && currHeight > 0)
currHeight>0:当前ImageView的高度,如果已经滚动到顶部或者超出,则不再进行滚动,防止已经滚出屏幕不可视了,还在进行滚动。
translationY<=0: 这个值是滚动的距离,这个距离不能超过ImageView的高度,由于上拉时dy是负值,所以要判断是否小于0;其主要方法如下:

 float translationY = getNegativeMaxValue(tempDy, -currHeight, 0);
 /***
  * 手指上移过程dy是负数
  * 返回负数最大值:0是最大值,不可以超过
  *
  * @param value   移动的最终距离:上次的位置+当次移动的偏移量之和,就是本次要移动的最终的偏移量
  * @param canMoveMaxValue 可移动的最大值
  * @param maxValue
  * @return
  */
 public static float getNegativeMaxValue(float value,float canMoveMaxValue, float maxValue) {
  return Math.min(maxValue, Math.max(canMoveMaxValue, value));
 }

ACTION_UP:

case MotionEvent.ACTION_OUTSIDE:
   case MotionEvent.ACTION_CANCEL:
   case MotionEvent.ACTION_UP:
    if (isChangedHeight) {
     if (imageView.getHeight() > ivInitHeight) {// (1)手指从上往下拉:下拉
      ResetAnimation resetAnimation =
      new ResetAnimation(ivInitHeight, imageView, topView);
      resetAnimation.setDuration(200);
      imageView.startAnimation(resetAnimation);
     } else {//(2)手指从下往上拉:上拉。。。这个不用处理。。。因为上拉后松开让其topview固定

     }
     isChangedHeight = false;
    }
    break;

isChangedHeight:当发生ImageView发生改变,并且是下拉时,这是松开手或者手指移出屏幕,则让其回弹到初始位置;这里是通过自定义动画来改变其变化的高度,达到回弹效果 代码如下

 /**
  * 自定义回弹动画,使imageView和topView过渡回弹到初始位置
  */
 static class ResetAnimation extends Animation {
  private View topView;
  private int topCurrHeight;

  private ImageView ivStretch;
  private int ivInitHeight;
  private int ivCurrHeight;

  public ResetAnimation(int ivInitHeiht, ImageView ivStretch, View topView) {

   this.ivInitHeight = ivInitHeiht;
   this.ivCurrHeight = ivStretch.getHeight();
   this.topCurrHeight = topView.getHeight();
   this.ivStretch = ivStretch;
   this.topView = topView;
  }

  @Override
  protected void applyTransformation(float interpolatedTime, Transformation t) {
   int dy = (int) ((ivCurrHeight - ivInitHeight) * interpolatedTime);
   Log.d(TAG, "anim dy = " + dy);
   ivStretch.getLayoutParams().height = ivCurrHeight - dy;
   topView.getLayoutParams().height = topCurrHeight - dy;
   topView.requestLayout();
  }
 }

其实主要是applyTransformation(float interpolatedTime, Transformation t) 这个方法

主要是通过这个渐变因子interpolatedTime来控制,其值范围是(0~1) 所以计算渐变的高度如下
int dy = (int) ((ivCurrHeight - ivInitHeight) * interpolatedTime);
然后一定要记得调用topView.requestLayout(),让其重新布局绘制。这样就完成了,所有代码,也就一百行代码左右,是不是很简单。而且通过这个demo,可以很好的拓展到scrollview中。

注意:
网上有些demo是通过overScrollBy()这个方法中搞事情,因为

/***
  * 这个方法是在滑出屏幕时回调,但是由于android系统国内厂商修改的面目全非,有些机型是不会回调的,比如vivo
  * 所以不要使用这个方法搞事情
  *
  * @param scrollX
  * @param scrollY
  */
 @Override
 protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,
 int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY,
 boolean isTouchEvent) {
  Log.d(TAG, "deltax = " + deltaX + " deltaY = " + deltaY);
  return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
   scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
 }

其自带dy,还有一些其它的参数,应有尽有。但是由于android系统是开源的导致有些系统是无法回调这个方法的,以至于无法实现回弹效果(比如:vivoX5)等等。所以在onTouchEvent()搞事情才是王道
ok!,以上有什么问题,请不吝指正!!!

Demo:android个人主页下拉回弹

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android ReboundScrollView仿IOS拖拽回弹效果

    初衷: 其实github上有很多这种ScrollView的项目,但是不得不说功能太多太乱了,我就只是想要一个简单效果的ScrollView,另外监听下滑动距离而已,想想还是自己写了个. 这里先说下思路吧,如果不愿意看的朋友可以直接跳过这一步,看下面的代码: Android 原生的ScrollView是不支持拉出屏幕外,并且也没有回弹效果的,用户友好度却不不太好,不知道为什么不那么设计. 我想做的事情正如上面所述: 1.希望能拉出屏幕外 2.松手后希望控件回弹 我的思路是对ScrollView的子

  • Android下拉刷新ListView——RTPullListView(demo)

    下拉刷新在越来越多的App中使用,已经形成一种默认的用户习惯,遇到列表显示的内容时,用户已经开始习惯性的拉拉.在交互习惯上已经形成定性.之前在我的文章<IOS学习笔记34-EGOTableViewPullRefresh实现下拉刷新>中介绍过如何在IOS上实现下拉刷新的功能.今天主要介绍下在Android上实现下拉刷新的Demo,下拉控件参考自Github上开源项目PullToRefresh,并做简单修改.最终效果如下:                         工程结构如下: 使用过程中

  • Android中使用RecyclerView实现下拉刷新和上拉加载

    推荐阅读:使用RecyclerView添加Header和Footer的方法                       RecyclerView的使用之HelloWorld RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.本文给大家介绍如何为RecyclerView添加下拉刷新和上拉加载,过去在ListView当中添加下拉刷新和上拉加载是非常方便的利用addHeaderView和addFooterVie

  • Android编程ViewPager回弹效果实例分析

    本文实例讲述了Android编程ViewPager回弹效果.分享给大家供大家参考,具体如下: 其实在我们很多应用中都看到当ViewPager滑到第一页或者最后一页的时候,如果再滑动的时候,就会有一个缓冲的过程,也就是回弹效果.之前在研究回弹效果的时候,也顺便实现了ViewPager的回弹效果,其实也很简单,一下是实现代码,注释比较少: package com.freesonfish.viewpager_2; import android.content.Context; import andro

  • Android PullToRefreshLayout下拉刷新控件的终结者

    说到下拉刷新控件,网上版本有很多,很多软件也都有下拉刷新功能.有一个叫XListView的,我看别人用过,没看过是咋实现的,看这名字估计是继承自ListView修改的,不过效果看起来挺丑的,也没什么扩展性,太单调了.看了QQ2014的列表下拉刷新,发现挺好看的,我喜欢,贴一下图看一下qq的下拉刷新效果: 不错吧?嗯,是的.一看就知道实现方式不一样.咱们今天就来实现一个下拉刷新控件.由于有时候不仅仅是ListView需要下拉刷新,ExpandableListView和GridView也有这个需求,

  • Android下拉刷新上拉加载控件(适用于所有View)

    前面写过一篇关于下拉刷新控件的文章下拉刷新控件终结者:PullToRefreshLayout,后来看到好多人还有上拉加载更多的需求,于是就在前面下拉刷新控件的基础上进行了改进,加了上拉加载的功能.不仅如此,我已经把它改成了对所有View都通用!可以随心所欲使用这两个功能~~ 我做了一个大集合的demo,实现了ListView.GridView.ExpandableListView.ScrollView.WebView.ImageView.TextView的下拉刷新和上拉加载.后面会提供demo的

  • Android实现三级联动下拉框 下拉列表spinner的实例代码

    主要实现办法:动态加载各级下拉值的适配器 在监听本级下拉框,当本级下拉框的选中值改变时,随之修改下级的适配器的绑定值              XML布局: 复制代码 代码如下: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_w

  • Android RecyclerView上拉加载更多功能回弹实现代码

    实现原理是使用RecyclerView的OnTouchListener方法监听滑动 在adapter里面增加两项footview 其中date.size为显示的加载条,可以自定义,date.size+1为空白的View,我们设置其高度为0 我们通过LinearLayoutManager的 findLastVisibleItemPosition判断显示的最后一条数据,如果是空白view,表示加载条已经完全展示,松开即可刷新. 回弹效果是通过在滑动时动态改变空白view的高度,达到阻尼效果 ,回弹时

  • Android UI设计系列之自定义ListView仿QQ空间阻尼下拉刷新和渐变菜单栏效果(8)

    好久没有写有关UI的博客了,刚刚翻了一下之前的博客,最近一篇有关UI的博客:Android UI设计系列之自定义Dialog实现各种风格的对话框效果(7) ,实现各种风格效果的对话框,在那篇博客写完后由于公司封闭开发封网以及其它原因致使博客中断至今,中断这么久很是惭愧,后续我会尽量把该写的都补充出来.近来项目有个需求,要做个和QQ空间类似的菜单栏透明度渐变和下拉刷新带有阻尼回弹的效果.于是花点时间动手试了试,基本上达到了QQ空间的效果,截图如下: 通过观察QQ空间的运行效果,发现当往上滚动时菜单

  • Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能(附源码)

    最近项目中需要用到ListView下拉刷新的功能,一开始想图省事,在网上直接找一个现成的,可是尝试了网上多个版本的下拉刷新之后发现效果都不怎么理想.有些是因为功能不完整或有Bug,有些是因为使用起来太复杂,十全十美的还真没找到.因此我也是放弃了在网上找现成代码的想法,自己花功夫编写了一种非常简单的下拉刷新实现方案,现在拿出来和大家分享一下.相信在阅读完本篇文章之后,大家都可以在自己的项目中一分钟引入下拉刷新功能. 首先讲一下实现原理.这里我们将采取的方案是使用组合View的方式,先自定义一个布局

随机推荐