##效果图:
实现思路:
因为是ListView的基本功能上进行拓展,所以首先需要继承ListView。
然后给ListView添加HeadView,HeadView的布局中是一个ImageView
在ImageView中放入一张比ImageView大的图片,
重写ListView的overScrollBy()方法,下拉过程中,通过获取下拉的手滑动的距离,逐渐增加ImageView的高度值,
重写onTouchEvent()方法,当抬起手的时候,通过ValueAnimator使得ImageView返回到的初始的高度。
##代码实现:
首先,暴漏出一个方法来传入HeadView中的ImageView控件, 并在此方法中获取到ImageView的高度和ImageView中图片的真实高度。
public void setImageView(ImageView iv){
this.mImage = iv;
// 获取ImageView里面图片的真实高度
mIntrinsicHeight = iv.getDrawable().getIntrinsicHeight();
// 获取ImageView的最初高度
mIvHeight = iv.getLayoutParams().height;
}
重写overScrollBy()方法,关于overScrollBy()方法的具体解释,请看我的另一篇文章TODO
在此方法中,如果图片到达了上边界,并且手还在往下拉动,则增加ImageView的高度,不断调用requestLayout()方法进行重新绘制ImageView.
可以判断,如果拉动的距离大于图片告诉百分比(可以设置提供对外的方法。让使用者自己去设置)
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
Log.d("test", "deltaY : " + deltaY + ", scrollY : " + scrollY + ", scrollRangeY : " + scrollRangeY + ", maxOverScrollY : " + maxOverScrollY + ", isTouchEvent : " + isTouchEvent);
// 如果到达了边界,并且手还在往下拉
if(isTouchEvent){
// 到达了上边界
if(deltaY < 0){
int oldHeight = mImage.getLayoutParams().height; // 获取到ImageView的高度
int newHeight = oldHeight + Math.abs(deltaY); // 每次手指移动,都在上次高度的基础上,加上手指移动的距离
if(newHeight > mIntrinsicHeight * 0.995f){ // 判断如果拖动出的图片的高度大于了其真实高度百分之99.5,则不能再拖动
newHeight = (int) (mIntrinsicHeight * 0.995f);
}
mImage.getLayoutParams().height = newHeight;
mImage.requestLayout();
}
}
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
到此时,就能实现下拉的功能了,还差松开手后自动回弹
我们知道,监听松开手在onTouchEvent()中实现。我们看看在onTouchEvent()中需要做什么操作
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP: // 当抬起手时,图片返回到原来的高度
va = ValueAnimator.ofInt(mImage.getLayoutParams().height, mIvHeight);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int ivHeight = (int) animation.getAnimatedValue(); // 获取到控件高度
mImage.getLayoutParams().height = ivHeight;
mImage.requestLayout();
}
});
va.setInterpolator(new OvershootInterpolator(2));
va.start();
break;
case MotionEvent.ACTION_DOWN: // 当按下时,如果动画在执行,则取消
if (va != null) {
if (va.isRunning()) {
va.cancel();
}
}
mImage.requestLayout();
break;
}
return super.onTouchEvent(ev);
}
在MotionEvent.ACTION_UP抬起手的时候,需要用ValueAnimator.ofInt(mImage.getLayoutParams().height, mIvHeight)
方法实现数值模拟变化过程,这个过程中,起始值为ImaggView控件此时的高度(此时已经被下拉),结束值为ImageView最初的高度(未下拉之前的高度)。
然后开启动画,把这些模拟数据不断设置给ImageView的height,然后调用requestLayout方法不断重新绘制。同时,为了用户体验,我使用的差值器setInterpolator(new OvershootInterpolator(2));关于差值器
Interpolator请看我的另一片文章TODO
我们注意到,在MotionEvent.ACTION_DOWN我们的也做了操作,这个操作是,但动画回弹到一半时,我们用手马上再次下拉,我们就停止当前动画,以便去响应再次下拉的操作。
##结束语:
这个控件最核心的方法是overScrollBy(),这个方法具体请看