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

2020-10-22 10:28:08 浏览数 (1)

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

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

代码语言:javascript复制
/** 
  * 当滑动的超出上,下,左,右最大范围时回调 
  * 
  * @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的布局文件:

代码语言:javascript复制
<?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代码,整体代码还是比较简短的.

代码语言:javascript复制
/** 
* 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); 
} 
} 

看看自定义动画:

代码语言:javascript复制
/** 
* 自定义动画 
* 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测试类:

代码语言:javascript复制
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); 
} 
} 

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

0 人点赞