大家能看到上面两张图片当中有什么区别吗?猜对了没有奖,到最后我会跟大家说他们的区别在哪里。首先这是个listview嵌套一张图片布局,而且下拉松开手图片能够自动回弹到原来的位置,具体实现如下:</p><p>首先创建一个类去集成一个listview,重写里面带有两个参数的构造方法,然后在布局文件中引用这个自定义ListView。新建一个布局放入图片,例如我的布局代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/myImage"
android:src="@mipmap/parallax_img"
android:scaleType="centerCrop"//scaleType有8种模式,每种模式的效果请参考如下两张图片,这里我使用的是centerCrop
android:layout_width="match_parent"
android:layout_height="160dip" />
</LinearLayout>
MainActivity中主要代码如下:
myListView = (AutoListView) findViewById(R.id.myListView);
//加载图片布局
View view = View.inflate(this, R.layout.view_head, null);
myListView.addHeaderView(view);
myImage = (ImageView) view.findViewById(R.id.myImage);
//给listview填充数据
myListView.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,Cheeses.NAMES));//新建一个类CHeeses里面存放NAMES字符串数组
myImage.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//当布局填充完执行此方法
myListView.parallaxImage(myImage);//新创建一个方法,然后在自定义Listview中实现该方法
myImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
接下来就在自定义的ListView中去实现parallasImage(myImage)方法了:
public void parallaxImage(ImageView myImage) {
this.myImage = myImage;
//图片原始高度
oriHeight = myImage.getHeight();
int measuredHeight = myImage.getMeasuredHeight();//图片测量的高度
int intrinsicHeight = myImage.getDrawable().getIntrinsicHeight();
}
到底oriHeight,measureHeight和intrinsicHeight有什么区别可以打一下日志看看,oriHeight代表图片原始的高度是一成不变的,其他两个高度这里用不上,大家可以了解一下
然后重写onScrollBy()方法
protected boolean overScrollBy(int deltaX,
int deltaY,//顶部到头往下拉-,底部到头往上拉+
int scrollX,
int scrollY,//垂直方向的偏移量
int scrollRangeX,
int scrollRangeY,//垂直方向偏移的范围
int maxOverScrollX,
int maxOverScrollY,//垂直方向最大的偏移量
boolean isTouchEvent) {//是否是触摸,true表示触摸,false表示惯性
if (isTouchEvent && deltaY < 0) {
int newHeight = myImage.getHeight() + Math.abs(deltaY);//图片现在的高度值加上往下拉deltaY的绝对值就是一个新的高度值
myImage.getLayoutParams().height = newHeight;//把得到的高度值赋值给图片的高度
myImage.requestLayout();//重新绘制布局
}
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
什么事顶部到头?就是默认listview打开界面的时候Listview就在与屏幕顶部对齐,相反底部到头就是listview一直往下拉拉到最后时与屏幕底部对齐,这里我们需要观察Y轴也就是垂直方向各个值的变化,例如上方加注释的各个变量值,大家可以打下日志观察每个值的变化是怎么样的,可以看到deltaY在顶部时如果往下deltay就变成负数,相反底部往上拉就变成了正数,所以这里我们可以利用往下拉的变化量对图片进行处理。
到这里图片只能往下拉变大,不能自动回弹到初始位置没有动画效果,这里我们就会用到ontouchEnvent(),
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
final int start = myImage.getHeight();//图片现在的高度
final int end = oriHeight;//图片原始的高度,无论放大缩小,初始的高度值不变是个常亮
ValueAnimator valueAnimator = ValueAnimator.ofInt(1);//属性动画ObjectAnimator的父类就是ValueAnimator,ofInt()里面的值为随意Int值
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {//添加监听,监听图片实时的高度值的变化
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedFraction = animation.getAnimatedFraction();//通过调用getAnimatedFraction()方法得到一个图片变化的百分比
Integer evaluate = evaluate(animatedFraction, start, end);//调用evalueate()方法
myImage.getLayoutParams().height = evaluate;//得到的值赋给图片的高度
myImage.requestLayout();//重新绘制布局
}
});
valueAnimator.setInterpolator(new OvershootInterpolator(4));//图片的回弹效果,里面的值根据效果而填充
valueAnimator.setDuration(500);
valueAnimator.start();
break;
}
return super.onTouchEvent(ev);
这里说一下,evaluate()方法是在接口TypeEvaluator中找到的,按住Ctrl鼠标点击TypeEvaluator进入,然后Ctrl+H进入类RectEvaluator可以找到evaluate()方法,只需要把这个方法全部复制过来就行了,源代码如下:
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
return startValue + (int) ((endValue - startValue) * fraction);
}
到这里其实上面的效果已经实现了,因为上面使用的属性动画完成的,所以这里也可以使用自定义动画来实现。
首先新建一个类继承Animation,然后在OntouchEvent()方法中实现:
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
final int start = myImage.getHeight();
final int end = oriHeight;
AutoAnimation auto = new AutoAnimation(myImage,start,end);//传入图片等参数,在自定义的Animation中实现该构造方法
startAnimation(auto);
break;
}
return super.onTouchEvent(ev);
在自定义动画代码中构造方法如下:
public AutoAnimation(ImageView myImage, int start, int end) {
this.myImage = myImage;
this.start = start;
this.end = end;
}
然后重写applyTransformation()方法,该方法如下:
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
Integer evaluate = evaluate(interpolatedTime, start, end);//调用evaluate()方法,传入三个值,所以还需要将刚才复制过来的evaluate()粘贴过来
myImage.getLayoutParams().height = evaluate;//将新的值赋值给图片的高度
myImage.requestLayout();//重新绘制布局
}
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
return startValue + (int) ((endValue - startValue) * fraction);
}
最后在构造方法中加入setDuration(500); setInterpolator(new OvershootInterpolator(4));这两段代码就可以实现一样的效果了。
好了,还记得我问大家上面的两种效果有什么不同的地方吗?其实这两种效果几乎都是一样的,唯一不同的就是第一个效果当它往下拉的时候顶部会出现一块蓝色的区域,不知道大家是否注意到这个小细节?如图所示
为了去掉这个蓝色的区域可以在我们的MainActivity中加入一段代码即可:myListView.setOverScrollMode(View.OVER_SCROLL_NEVER);
好了以上就是对这个效果的全部实现。