您的位置:首页 > 移动开发 > Android开发

Android简易实战教程--第五十四话《视差特效》

2017-10-26 11:19 274 查看
查看更多进阶知识,可以关注我的公众号,微信搜索:Android小菜
这个简易实战教程系列专栏发现已经大半年没更新了啊,赶紧添点东西。
本文实现一个视差特效效果,动态效果如下:



代码十分简单,自定义View代码添加在下面:

public class ParallaxListView extends ListView {

private static final String TAG = "ParallaxListView";
private ImageView ParallaxHeaderImageView;//头部视视差效果的图片
private int mOldHeight;//此时ImageView的高度
private int mRealHeight;//图片的真正高度

public ParallaxListView(Context context) {
this(context,null);
}

public ParallaxListView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public void setHeadImageView(ImageView ivHeadImageView) {
this.ParallaxHeaderImageView = ivHeadImageView;
mOldHeight = ParallaxHeaderImageView.getMeasuredHeight();
//        int height = ParallaxHeaderImageView.getHeight();
//        Log.e(TAG,mOldHeight+"----"+height);//两个值一样
mRealHeight = ParallaxHeaderImageView.getDrawable().getIntrinsicHeight();
}

@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
System.out.println("deltaY: "+ deltaY + " scrollY: " + scrollY
+ " scrollRangeY: " + scrollRangeY
+ " maxOverScrollY: " + maxOverScrollY
+ " isTouchEvent: " + isTouchEvent);
// deltaY : 竖直方向的瞬时偏移量, 顶部下拉- , 底部上拉+, 速度越快绝对值越大.
// scrollY : 竖直方向ListView滚动的距离, 顶部- , 底部+
// scrollRangeY : 竖直方向滚动距离
// maxOverScrollY : 最大的超出滚动范围
// isTouchEvent : 是否是手指触摸到顶部/底部. true触摸, false惯性
if(deltaY <0 && isTouchEvent){
int newHeight = ParallaxHeaderImageView.getHeight() + Math.abs(deltaY)/2;//减少拖动的距离
setCurrentImageViewHeight(newHeight);
}
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}

private void setCurrentImageViewHeight(int newHeight) {
ParallaxHeaderImageView.getLayoutParams().height = newHeight;
ParallaxHeaderImageView.requestLayout();
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
int startHeight = ParallaxHeaderImageView.getHeight();
ValueAnimator animator = ValueAnimator.ofInt(startHeight, mOldHeight);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 500 毫秒内多次调用
// 0.0 -> 1.0
System.out.println("animation: " + animation.getAnimatedFraction());

// 模拟出来的中间值
Object animatedValue = animation.getAnimatedValue();
System.out.println("animation: " + animatedValue);

// 让新的高度生效
setCurrentImageViewHeight((Integer)animatedValue);
}
});
animator.setInterpolator(new OvershootInterpolator(4));
animator.setDuration(500);
animator.start();
break;

default:
break;
}
return super.onTouchEvent(ev);
}
}
代码解析:通过setHeadImageView方法传入一头布局,然后拿到当前ImageView的高度和图片的真实高度(注意:真实高度和控件的高度是不一样的)。然后重写overScrollBy方法,这个方法会对ListView滑动到边缘时候做一些判断,具体的参数含义如下:
        // deltaY : 竖直方向的瞬时偏移量, 顶部下拉- , 底部上拉+, 速度越快绝对值越大.
        // scrollY : 竖直方向ListView滚动的距离, 顶部- , 底部+
        // scrollRangeY : 竖直方向滚动距离
        // maxOverScrollY : 最大的超出滚动范围
        // isTouchEvent : 是否是手指触摸到顶部/底部. true触摸, false惯性

因为是在头部下拉,所以判断deltaY<0 且是触摸状态(非惯性滑动状态),才去处理事件。得到最新的ImageView的高度【因为手指下滑有一个滑动了多少的绝对值,类加后就是当前ImagView的当前高度】,并把最新的高度设置给ImageView。这样就实现了ImagView的放大效果了。
那么松开手要做的逻辑,肯定是放在onTouchEvent的UP事件里面了,主要做的工作就是,记录松开手时候ImagView的高度,使用属性动画把ImagView控件不断地缩小,这里还使用了插值器,让ImagView恢复原来高度值后有一个弹性效果。这样整个简易的自定义View就完成了。
然后看看客户端如何使用这个控件:
MainActivity的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.itydl.a04.MainActivity">

<com.itydl.a04.view.ParallaxListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

</RelativeLayout>
然后在onCreat方法中:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ParallaxListView) findViewById(R.id.lv);
mHeadView = View.inflate(this, R.layout.layout_head,null);
mIvHead = (ImageView) mHeadView.findViewById(R.id.iv);
mListView.addHeaderView(mHeadView);

mIvHead.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mListView.setHeadImageView(mIvHead);
mIvHead.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});

mListView.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,Strings.NAMES));
}
拿到ListView的实例,给其设置数据进去。在试图绘制完毕的时候,把拿到的一张图片设置到刚才的自定义View中去了。然后运行后,就是开头的效果了。


最后可能会存在一点点的bug:

if(deltaY <0 && isTouchEvent){
int newHeight = ParallaxHeaderImageView.getHeight() + Math.abs(deltaY)/2;//减少拖动的距离
if(newHeight <= mRealHeight){
//只有在滑动的距离小于图片的真正高度的时候才去重新设置,超过了图片的高度就不去设置了。
setCurrentImageViewHeight(newHeight);
}
}
超过图片的实际高度不应该再往下下拉设置高度给ImagView了吧。如果觉得有点作用记得加个关注哈,要想看更高级的文章,欢迎关注微信公众号“Android小菜”

代码下载链接地址:源码下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: