視差特效
* 應用場景: 微信朋友圈, 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)); } }
