Android development: ScrollerView of Parallax effect, adapted from ParallaxListView
Recently, in the project, we used a Parallax effect modeled on Path and searched for it. on github, there was a similar effect, but it was a listview, and a headerView on the top was added, this effect is implemented, but what I need is ScrollerView. Therefore, the code is modified to implement the Parallax effect under ScrollerView. See the following:
Before reading the following code, you can view the source code on Github.
I have made a lot of deletions to the original code, which only achieves the desired effect and looks simple and easy to understand. I am most afraid of the code that is bypassed. Let's look at the core implementation code:
public class ParallaxScollView extends ScrollView implements OnScrollListener { public final static double NO_ZOOM = 1; public final static double ZOOM_X2 = 2; private ImageView mImageView; private int mImageViewHeight = -1; private int mDefaultImageViewHeight = 0; private int originImageViewHeight; private int mMaxHeight; private interface OnOverScrollByListener { public boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent); } private interface OnTouchEventListener { public void onTouchEvent(MotionEvent ev); } public ParallaxScollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } public ParallaxScollView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public ParallaxScollView(Context context) { super(context); init(context); } public void init(Context context) { mDefaultImageViewHeight = context.getResources().getDimensionPixelSize(R.dimen.size_default_height); originImageViewHeight = context.getResources().getDimensionPixelSize(R.dimen.size_default_height); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { boolean isCollapseAnimation = false; isCollapseAnimation = scrollByListener.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent) || isCollapseAnimation; /*return isCollapseAnimation ? true : super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, 0, isTouchEvent);*/ return false; } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); View firstView = (View) mImageView.getParent(); // firstView.getTop < getPaddingTop means mImageView will be covered by top padding, // so we can layout it to make it shorter if (firstView.getTop() < getPaddingTop() && mImageView.getHeight() > mImageViewHeight) { mImageView.getLayoutParams().height = Math.max(mImageView.getHeight() - (getPaddingTop() - firstView.getTop()), mImageViewHeight); // to set the firstView.mTop to 0, // maybe use View.setTop() is more easy, but it just support from Android 3.0 (API 11) firstView.layout(firstView.getLeft(), 0, firstView.getRight(), firstView.getHeight()); mImageView.requestLayout(); } } @Override public boolean onTouchEvent(MotionEvent ev) { touchListener.onTouchEvent(ev); return super.onTouchEvent(ev); } public void setParallaxImageView(ImageView iv) { mImageView = iv; mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP); } public void setViewsBounds(double zoomRatio) { if (mImageViewHeight == -1) { mImageViewHeight = mImageView.getHeight(); if (mImageViewHeight <= 0) { mImageViewHeight = mDefaultImageViewHeight; } double ratio = ((double) mImageView.getDrawable().getIntrinsicWidth()) / ((double) mImageView.getWidth()); } } private OnOverScrollByListener scrollByListener = new OnOverScrollByListener() { @Override public boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { if (isTouchEvent) { if (true) { mImageView.getLayoutParams().height = mImageView.getHeight() - deltaY / 2; mImageView.requestLayout(); } } return false; } }; private OnTouchEventListener touchListener = new OnTouchEventListener() { @Override public void onTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_UP) { if (mImageViewHeight - 1 < mImageView.getHeight()) { ResetAnimimation animation = new ResetAnimimation( mImageView, mImageViewHeight); animation.setDuration(300); mImageView.startAnimation(animation); } } } }; public class ResetAnimimation extends Animation { int targetHeight; int originalHeight; int extraHeight; View mView; protected ResetAnimimation(View view, int targetHeight) { this.mView = view; this.targetHeight = targetHeight; originalHeight = view.getHeight(); extraHeight = this.originalHeight - originImageViewHeight; Log.i(debug, target heitht + targetHeight + original height + originalHeight + extraheight + extraHeight); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { int newHeight; newHeight = (int) (originImageViewHeight + extraHeight * (1 - interpolatedTime)); mView.getLayoutParams().height = newHeight; mView.requestLayout(); } }}
Article 2: reference the ParallaxScollView in xml:
In the last step, reference ParallaxScrollerView in the activity and set the imageview:
mImageView = (ImageView) findViewById(R.id.headview); scrollView = (ParallaxScollView) findViewById(R.id.parallax_scroll_view); scrollView.setParallaxImageView(mImageView);
You can also implement the cool Parallax effect on ScrollerView.