Android通过overScrollBy实现下拉视差特效

发布时间 - 2026-01-11 02:37:36    点击率:

overScrollBy实现下拉视差特效,效果图如下

先来分析overScrollBy方法的使用,它是View的方法,参数有点多:

/** 
  * 当滑动的超出上,下,左,右最大范围时回调 
  * 
  * @param deltaX     x方向的瞬时偏移量,左边到头,向右拉为负,右边到头,向左拉为正 
  * @param deltaY     y方向的瞬时偏移量,顶部到头,向下拉为负,底部到头,向上拉为正 
  * @param scrollX    水平方向的永久偏移量 
  * @param scrollY    竖直方向的永久偏移量 
  * @param scrollRangeX  水平方向滑动的范围 
  * @param scrollRangeY  竖直方向滑动的范围 
  * @param maxOverScrollX 水平方向最大滑动范围 
  * @param maxOverScrollY 竖直方向最大滑动范围 
  * @param isTouchEvent  是否是手指触摸滑动, true为手指, false为惯性 
  * @return 
  */ 
  @Override 
  protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, 
                 int scrollRangeX, int scrollRangeY, int maxOverScrollX, 
                 int maxOverScrollY, boolean isTouchEvent) { 
    return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, 
        scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, 
        isTouchEvent); 
  } 

大致步骤如下:

1.这整体是一个ListView,所以需要自定义一个ListView.
2.处理头部布局文件,将其以HeaderView的方式添加到自定义的ListView中
3.需要获取HeaderView的ImageView的初始高度和ImageView中图片的高度.因为这2个高度将决定下来的时候图片拉出的范围,以及松手后图片回弹的动画效果.对应控件宽高的获取,有兴趣的可以看这篇文章浅谈自定义View的宽高获取
4.在overScrollBy方法内通过修改ImageView的LayoutParams的height值来显示更多的图片内容.
5.在onTouchEvent方法内处理ACTION_UP事件,使ImageView有回弹的动画效果,这里介绍2种方式,分别是属性动画和自定义动画.

好了,先来看HeaderView的布局文件:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:orientation="vertical" > 
  <ImageView 
    android:id="@+id/imageView" 
    android:layout_width="match_parent" 
    android:layout_height="160dp" 
    <span style="color:#ff0000;">android:scaleType="centerCrop"</span> 
    android:src="@drawable/header" /> 
</LinearLayout> 

没什么特别的,就是一个ImageView,通过src设置了一张图片,这里唯一要将的就是scaleType属性,我这边设置了centerCrop,以图片的最小的边开始截取,因为这里选择的图片是高度大于宽度的,所以裁剪的时候会保留完整的宽度,中心裁剪,如下图所示:

自定义ListView代码,整体代码还是比较简短的.

/** 
 * Created by mChenys on 2015/12/23. 
 */ 
public class MyListView extends ListView { 
  private ImageView mHeaderIv; //HeaderView 的ImageView 
  private int mOriginalHeight; //最初ImageView的高度 
  private int mDrawableHeight;//ImageView中图片的高度 
 
  public MyListView(Context context) { 
    this(context, null); 
  } 
 
  public MyListView(Context context, AttributeSet attrs) { 
    this(context, attrs, 0); 
  } 
 
  public MyListView(Context context, AttributeSet attrs, int defStyleAttr) { 
    super(context, attrs, defStyleAttr); 
    init(); 
  } 
 
  /** 
   * 设置头部和获取高度信息 
   */ 
  private void init() { 
    //初始化头部文件 
    View headerView = View.inflate(getContext(), R.layout.view_header, null); 
    mHeaderIv = (ImageView) headerView.findViewById(R.id.imageView); 
    //将其添加到ListView的头部 
    addHeaderView(headerView); 
    //通过设置监听来获取控件的高度 
    mHeaderIv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 
      @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 
      @Override 
      public void onGlobalLayout() { 
        //只需监听一次,否则之后的onLayout方法回调的时候还是会回调这里 
        mHeaderIv.getViewTreeObserver().removeOnGlobalLayoutListener(this); 
        mOriginalHeight = mHeaderIv.getMeasuredHeight();//获取ImageView的初始高度 
        mDrawableHeight = mHeaderIv.getDrawable().getIntrinsicHeight();//获取ImageView中图片的高度 
      } 
    }); 
    //去掉下拉到头部后的蓝色线 
    setOverScrollMode(OVER_SCROLL_NEVER); 
  } 
 
  /** 
   * 当滑动的超出上,下,左,右最大范围时回调 
   * 
   * @param deltaX     x方向的瞬时偏移量,左边到头,向右拉为负,右边到头,向左拉为正 
   * @param deltaY     y方向的瞬时偏移量,顶部到头,向下拉为负,底部到头,向上拉为正 
   * @param scrollX    水平方向的永久偏移量 
   * @param scrollY    竖直方向的永久偏移量 
   * @param scrollRangeX  水平方向滑动的范围 
   * @param scrollRangeY  竖直方向滑动的范围 
   * @param maxOverScrollX 水平方向最大滑动范围 
   * @param maxOverScrollY 竖直方向最大滑动范围 
   * @param isTouchEvent  是否是手指触摸滑动, true为手指, false为惯性 
   * @return 
   */ 
  @Override 
  protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, 
                  int scrollRangeX, int scrollRangeY, int maxOverScrollX, 
                  int maxOverScrollY, boolean isTouchEvent) { 
    // 手指拉动并且是下拉 
    if (isTouchEvent && deltaY < 0) { 
      // 把拉动的瞬时变化量的绝对值交给Header, 就可以实现放大效果 
      if (mHeaderIv.getHeight() <= mDrawableHeight) { 
        // 高度不超出图片最大高度时,才让其生效 
        int newHeight = (int) (mHeaderIv.getHeight() + Math.abs(deltaY / 3.0f));//这里除以3是为了达到视差的效果 
        mHeaderIv.getLayoutParams().height = newHeight; 
        //此方法必须调用,调用后会重新调用onMeasure和onLayout方法进行测量和定位 
        mHeaderIv.requestLayout(); 
      } 
    } 
    return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 
  } 
 
  @Override 
  public boolean onTouchEvent(MotionEvent ev) { 
    switch (ev.getAction()) { 
      case MotionEvent.ACTION_UP: 
        // 执行回弹动画, 方式一: 属性动画\值动画 
        //获取ImageView在松手时的高度 
        int currHeight = mHeaderIv.getHeight(); 
        // 从当前高度mHeaderIv.getHeight(), 执行动画到原始高度mOriginalHeight 
        ValueAnimator animator = ValueAnimator.ofInt(currHeight, mOriginalHeight); 
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 
          @Override 
          public void onAnimationUpdate(ValueAnimator animation) { 
            int value = (int) animation.getAnimatedValue(); 
            mHeaderIv.getLayoutParams().height = value; 
            //此方法必须调用,调用后会重新调用onMeasure和onLayout方法进行测量和定位 
            mHeaderIv.requestLayout(); 
          } 
        }); 
        animator.setDuration(500); 
        animator.setInterpolator(new OvershootInterpolator()); 
        animator.start(); 
 
        //方式二,通过自定义动画 
        /*ResetAnimation animation = new ResetAnimation(mHeaderIv, mHeaderIv.getHeight(), mOriginalHeight); 
        startAnimation(animation);*/ 
        break; 
    } 
    return super.onTouchEvent(ev); 
  } 
} 

看看自定义动画:

/** 
 * 自定义动画 
 * Created by mChenys on 2015/12/24. 
 */ 
public class ResetAnimation extends Animation { 
  private final ImageView headerIv; //要执行动画的目标ImageView 
  private final int startHeight;//执行动画的开始时的高度 
  private final int endHeight;//执行动画结束时的高度 
  private IntEvaluator mEvaluator; //整型估值器 
 
  /** 
   * 构造方法初始化 
   * 
   * @param headerIv  应用动画的目标控件 
   * @param startHeight 开始的高度 
   * @param endHeight  结束的高度 
   */ 
  public ResetAnimation(ImageView headerIv, int startHeight, int endHeight) { 
    this.headerIv = headerIv; 
    this.startHeight = startHeight; 
    this.endHeight = endHeight; 
    //定义一个int类型的类型估值器,用于获取实时变化的高度值 
    mEvaluator = new IntEvaluator(); 
    //设置动画持续时间 
    setDuration(500); 
    //设置插值器 
    setInterpolator(new OvershootInterpolator()); 
  } 
 
  /** 
   * 在指定的时间内一直执行该方法,直到动画结束 
   * interpolatedTime:0-1 标识动画执行的进度或者百分比 
   * 
   * @param interpolatedTime 
   * @param t 
   */ 
  @Override 
  protected void applyTransformation(float interpolatedTime, Transformation t) { 
    int currHeight = mEvaluator.evaluate(interpolatedTime, startHeight, endHeight); 
    //通过LayoutParams不断的改变其高度 
    headerIv.getLayoutParams().height = currHeight; 
    //此方法必须调用,调用后会重新调用onMeasure和onLayout方法进行测量和定位 
    headerIv.requestLayout(); 
  } 
} 

MainActivity测试类:

public class MainActivity extends AppCompatActivity { 
 
  @Override 
  protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    MyListView listView = new MyListView(this); 
    listView.setDividerHeight(1); 
    listView.setSelector(new ColorDrawable()); 
    listView.setCacheColorHint(Color.TRANSPARENT); 
    listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, Cheeses.NAMES)); 
    setContentView(listView); 
  } 
} 

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


# Android  # overScrollBy  # 下拉视差  # Android自定义view实现阻尼效果的加载动画  # Android UI设计系列之自定义ListView仿QQ空间阻尼下拉刷新和渐变菜单栏效果(8)  # 自定义  # 偏移量  # 左拉  # 回调  # 后会  # 将其  # 估值  # 拉出  # 是一个  # 好了  # 只需  # 它是  # 时间内  # 有兴趣  # 所示  # 要将  # 这篇文章  # 浅谈  # 拉到  # 先来 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Python企业级消息系统教程_KafkaRabbitMQ高并发应用  大连网站制作公司哪家好一点,大连买房网站哪个好?  制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?  Laravel队列由Redis驱动怎么配置_Laravel Redis队列使用教程  如何在 Go 中优雅地映射具有动态字段的 JSON 对象到结构体  Laravel如何实现文件上传和存储?(本地与S3配置)  如何快速搭建二级域名独立网站?  Laravel路由Route怎么设置_Laravel基础路由定义与参数传递规则【详解】  如何快速生成专业多端适配建站电话?  如何在景安云服务器上绑定域名并配置虚拟主机?  Laravel如何配置中间件Middleware_Laravel自定义中间件拦截请求与权限校验【步骤】  php嵌入式断网后怎么恢复_php检测网络重连并恢复硬件控制【操作】  香港服务器如何优化才能显著提升网站加载速度?  如何在局域网内绑定自建网站域名?  如何用花生壳三步快速搭建专属网站?  简单实现Android验证码  微信小程序 input输入框控件详解及实例(多种示例)  安克发布新款氮化镓充电宝:体积缩小 30%,支持 200W 输出  如何挑选最适合建站的高性能VPS主机?  详解Huffman编码算法之Java实现  如何在阿里云通过域名搭建网站?  大连企业网站制作公司,大连2025企业社保缴费网上缴费流程?  bootstrap日历插件datetimepicker使用方法  JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)  JS去除重复并统计数量的实现方法  C++时间戳转换成日期时间的步骤和示例代码  浅谈javascript alert和confirm的美化  CSS3怎么给轮播图加过渡动画_transition加transform实现【技巧】  html5的keygen标签为什么废弃_替代方案说明【解答】  Linux系统运维自动化项目教程_Ansible批量管理实战  潮流网站制作头像软件下载,适合母子的网名有哪些?  如何用搬瓦工VPS快速搭建个人网站?  Laravel定时任务怎么设置_Laravel Crontab调度器配置  微信小程序 scroll-view组件实现列表页实例代码  微博html5版本怎么弄发语音微博_语音录制入口及时长限制操作【教程】  Laravel怎么设置路由分组Prefix_Laravel多级路由嵌套与命名空间隔离【步骤】  大连 网站制作,大连天途有线官网?  桂林网站制作公司有哪些,桂林马拉松怎么报名?  香港服务器建站指南:免备案优势与SEO优化技巧全解析  如何解决hover在ie6中的兼容性问题  Laravel怎么在Blade中安全地输出原始HTML内容  怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?  创业网站制作流程,创业网站可靠吗?  Laravel storage目录权限问题_Laravel文件写入权限设置  Windows驱动无法加载错误解决方法_驱动签名验证失败处理步骤  Laravel怎么自定义错误页面_Laravel修改404和500页面模板  大型企业网站制作流程,做网站需要注册公司吗?  Laravel的辅助函数有哪些_Laravel常用Helpers函数提高开发效率  javascript中的try catch异常捕获机制用法分析  为什么php本地部署后css不生效_静态资源加载失败修复技巧【技巧】