9.视差特效、回弹动画、overScrollBy
视差特效
* 应用场景: 微信朋友圈, QQ空间, 微博个人展示,向下拉出,松开回弹
* 功能实现:
> 1. 重写overScrollBy
> 2. 松手之后执行动画, 类型估值器
.
activity_main
-
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <com.itheima.parallaxdemo.ui.MyListView android:id="@+id/lv" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
view_header
-
<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/iv" android:layout_width="match_parent" android:layout_height="160dp" android:scaleType="centerCrop" android:src="@drawable/parallax_img" /> </LinearLayout>
MyListView
-
/** * 视差特效ListView * overScrollBy * @author poplar * */ public class MyListView extends ListView { private static final String TAG = "TAG"; private int mOriginalHeight; private int drawableHeight; private ImageView mImage; public MyListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public MyListView(Context context, AttributeSet attrs) { super(context, attrs); } public MyListView(Context context) { super(context); } /** * 设置ImageView图片, 拿到引用 * @param mImage */ public void setParallaxImage(ImageView mImage) { this.mImage = mImage; mOriginalHeight = mImage.getHeight(); // 160 drawableHeight = mImage.getDrawable().getIntrinsicHeight(); // 488,图片的高,而不是显示的高 Log.d(TAG, "height: " + mOriginalHeight + " drawableHeight: " + drawableHeight); } @Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { // deltaY : 竖直方向的瞬时偏移量 / 变化量 dx 顶部到头下拉为-, 底部到头上拉为+ // scrollY : 竖直方向的偏移量 / 变化量 // scrollRangeY : 竖直方向滑动的范围 // maxOverScrollY : 竖直方向最大滑动范围 // isTouchEvent : 是否是手指触摸滑动, true为手指, false为惯性 Log.d(TAG, "deltaY: " +deltaY + " scrollY: " + scrollY + " scrollRangeY: " + scrollRangeY + " maxOverScrollY: " + maxOverScrollY + " isTouchEvent: " + isTouchEvent); // 手指拉动 并且 是下拉 if(isTouchEvent && deltaY < 0){ // 把拉动的瞬时变化量的绝对值交给Header, 就可以实现放大效果 if(mImage.getHeight() <= drawableHeight){ int newHeight = (int) (mImage.getHeight() + Math.abs(deltaY / 3.0f)); // 高度不超出图片最大高度时,才让其生效 mImage.getLayoutParams().height = newHeight; mImage.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: // 执行回弹动画, 方式一: 属性动画\值动画 // 从当前高度mImage.getHeight(), 执行动画到原始高度mOriginalHeight final int startHeight = mImage.getHeight(); final int endHeight = mOriginalHeight; // valueAnimator(startHeight, endHeight); // 执行回弹动画, 方式二: 自定义Animation ResetAnimation animation = new ResetAnimation(mImage, startHeight, endHeight); startAnimation(animation); break; } return super.onTouchEvent(ev); } private void valueAnimator(final int startHeight, final int endHeight) { ValueAnimator mValueAnim = ValueAnimator.ofInt(1);//不起作用,写几都行 mValueAnim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator mAnim) { float fraction = mAnim.getAnimatedFraction(); // percent 0.0 -> 1.0 Log.d(TAG, "fraction: " +fraction); Integer newHeight = evaluate(fraction, startHeight, endHeight);//固值器 mImage.getLayoutParams().height = newHeight;//设置imageview高度 mImage.requestLayout(); } }); mValueAnim.setInterpolator(new OvershootInterpolator()); mValueAnim.setDuration(500); mValueAnim.start(); } public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int startInt = startValue; return (int)(startInt + fraction * (endValue - startInt)); } }
ResetAnimation
-
public class ResetAnimation extends Animation { private final ImageView mImage; private final int startHeight; private final int endHeight; public ResetAnimation(ImageView mImage, int startHeight, int endHeight) { this.mImage = mImage; this.startHeight = startHeight; this.endHeight = endHeight; setInterpolator(new OvershootInterpolator()); //设置动画执行时长 setDuration(500); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { // interpolatedTime 0.0f -> 1.0f Integer newHeight = evaluate(interpolatedTime, startHeight, endHeight); mImage.getLayoutParams().height = newHeight;//设置imageview高 mImage.requestLayout(); super.applyTransformation(interpolatedTime, t); } /** * 类型估值器 * @param fraction * @param startValue * @param endValue * @return */ public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int startInt = startValue; return (int)(startInt + fraction * (endValue - startInt)); } }
-
MainActivity
-
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final MyListView mListView = (MyListView) findViewById(R.id.lv); mListView.setOverScrollMode(View.OVER_SCROLL_NEVER); // 加Header final View mHeaderView = View.inflate(MainActivity.this, R.layout.view_header, null); final ImageView mImage = (ImageView) mHeaderView.findViewById(R.id.iv); mListView.addHeaderView(mHeaderView); mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // 当布局填充结束之后, 此方法会被调用 mListView.setParallaxImage(mImage); mHeaderView.getViewTreeObserver().removeGlobalOnLayoutListener(this); } }); // 填充数据 mListView.setAdapter(new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, Cheeses.NAMES)); } }