package com.marshalchen.common.uimodule.pulltozoomview;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import com.marshalchen.ultimateandroiduicomponent.R;
/**
* Author: ZhuWenWu
* Version V1.0
* Date: 2014/9/1 10:50.
* Description:
* Modification History:
* Date Author Version Description
* -----------------------------------------------------------------------------------
* 2014/9/1 ZhuWenWu 1.0 1.0
* Why & What is modified:
*/
public class PullToZoomScrollView extends ScrollView {
private static final String TAG = PullToZoomScrollView.class.getSimpleName();
private static final Interpolator sInterpolator = new Interpolator() {
public float getInterpolation(float paramAnonymousFloat) {
float f = paramAnonymousFloat - 1.0F;
return 1.0F + f * (f * (f * (f * f)));
}
};
private View mContentView;//中间View
private View mHeadView;//头部View
private View mZoomView;//缩放拉伸View
private FrameLayout mContentContainer;
private FrameLayout mHeaderContainer;
private FrameLayout mZoomContainer;
private LinearLayout mRootContainer;
private OnScrollViewChangedListener mOnScrollListener;
private OnScrollViewZoomListener onScrollViewZoomListener;
private ScalingRunnable mScalingRunnable;
private int mScreenHeight;
private int mZoomHeight;
private int mZoomWidth;
private int mActivePointerId = -1;
private float mLastMotionY = -1.0F;
private float mLastScale = -1.0F;
private float mMaxScale = -1.0F;
private boolean isHeaderTop = true;
private boolean isEnableZoom = true;
private boolean isParallax = false;
public PullToZoomScrollView(Context context) {
this(context, null);
}
public PullToZoomScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PullToZoomScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
private void init(AttributeSet attrs) {
mHeaderContainer = new FrameLayout(getContext());
mZoomContainer = new FrameLayout(getContext());
mContentContainer = new FrameLayout(getContext());
mRootContainer = new LinearLayout(getContext());
mRootContainer.setOrientation(LinearLayout.VERTICAL);
if (attrs != null) {
LayoutInflater mLayoutInflater = LayoutInflater.from(getContext());
//初始化状态View
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.PullToZoomScrollView);
int zoomViewResId = a.getResourceId(R.styleable.PullToZoomScrollView_scrollZoomView, 0);
if (zoomViewResId > 0) {
mZoomView = mLayoutInflater.inflate(zoomViewResId, null, false);
mZoomContainer.addView(mZoomView);
mHeaderContainer.addView(mZoomContainer);
}
int headViewResId = a.getResourceId(R.styleable.PullToZoomScrollView_scrollHeadView, 0);
if (headViewResId > 0) {
mHeadView = mLayoutInflater.inflate(headViewResId, null, false);
mHeaderContainer.addView(mHeadView);
}
int contentViewResId = a.getResourceId(R.styleable.PullToZoomScrollView_scrollContentView, 0);
if (contentViewResId > 0) {
mContentView = mLayoutInflater.inflate(contentViewResId, null, false);
mContentContainer.addView(mContentView);
}
a.recycle();
}
DisplayMetrics localDisplayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics);
mScreenHeight = localDisplayMetrics.heightPixels;
mZoomWidth = localDisplayMetrics.widthPixels;
mScalingRunnable = new ScalingRunnable();
mRootContainer.addView(mHeaderContainer);
mRootContainer.addView(mContentContainer);
mRootContainer.setClipChildren(false);
mHeaderContainer.setClipChildren(false);
addView(mRootContainer);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
}
public void setEnableZoom(boolean isEnableZoom) {
this.isEnableZoom = isEnableZoom;
}
public void setParallax(boolean isParallax) {
this.isParallax = isParallax;
}
public void setOnScrollListener(OnScrollViewChangedListener mOnScrollListener) {
this.mOnScrollListener = mOnScrollListener;
}
public void setOnScrollViewZoomListener(OnScrollViewZoomListener onScrollViewZoomListener) {
this.onScrollViewZoomListener = onScrollViewZoomListener;
}
public void setContentContainerView(View view) {
if (mContentContainer != null) {
mContentContainer.removeAllViews();
mContentView = view;
mContentContainer.addView(view);
}
}
public void setHeaderContainer(View view) {
if (mHeaderContainer != null && view != null) {
mHeaderContainer.removeAllViews();
mHeadView = view;
if (mZoomView != null && mZoomContainer != null) {
mZoomContainer.removeAllViews();
mZoomContainer.addView(mZoomView);
mHeaderContainer.addView(mZoomContainer);
}
mHeaderContainer.addView(mHeadView);
}
}
public void setZoomView(View view) {
if (mZoomContainer != null && view != null) {
this.mZoomView = view;
mZoomContainer.removeAllViews();
mZoomContainer.addView(mZoomView);
if (mHeaderContainer != null) {
mHeaderContainer.removeAllViews();
mHeaderContainer.addView(mZoomContainer);
if (mHeadView != null) {
mHeaderContainer.addView(mHeadView);
}
}
}
}
public void showHeaderView() {
if (mZoomView != null || mHeadView != null) {
mHeaderContainer.setVisibility(VISIBLE);
}
}
public void hideHeaderView() {
if (mZoomView != null || mHeadView != null) {
mHeaderContainer.setVisibility(GONE);
}
}
public FrameLayout getZoomContainer() {
return mZoomContainer;
}
public FrameLayout getHeaderContainer() {
return mHeaderContainer;
}
public View getZoomView() {
return mZoomView;
}
public View getContentView() {
return mContentView;
}
public View getHeadView() {
return mHeadView;
}
public void setZoomHeight(int mZoomHeight) {
this.mZoomHeight = mZoomHeight;
}
public LinearLayout getRootContainer() {
return mRootContainer;
}
private void endScaling() {
if (mZoomContainer.getBottom() >= mZoomHeight) {
Log.d(TAG, "endScaling");
}
mScalingRunnable.startAnimation(200L);
}
public boolean onInterceptTouchEvent(MotionEvent event) {
return super.onInterceptTouchEvent(event);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mZoomHeight == 0) {
if (mZoomContainer != null) {
mZoomHeight = mZoomContainer.getHeight();
mZoomWidth = mZoomContainer.getWidth();
}
}
}
@Override
protected void onScrollChanged(int left, int top, int oldLeft, int oldTop) {
super.onScrollChanged(left, top, oldLeft, oldTop);
if (isEnableZoom) {
isHeaderTop = getScrollY() <= 0;
Log.d(TAG, "onScrollChanged --> ");
if (isParallax) {
float f = mZoomHeight - mZoomContainer.getBottom() + getScrollY();
Log.d(TAG, "f = " + f);
if ((f > 0.0F) && (f < mZoomHeight)) {
int i = (int) (0.65D * f);
mHeaderContainer.scrollTo(0, -i);
} else if (mHeaderContainer.getScrollY() != 0) {
mHeaderContainer.scrollTo(0, 0);
}
}
}
if (mOnScrollListener != null) {
mOnScrollListener.onScrollChanged(left, top, oldLeft, oldTop);
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
Log.d(TAG, "onTouchEvent --> action = " + (0xFF & ev.getAction()));
if (isHeaderTop && isEnableZoom) {
switch (0xFF & ev.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_OUTSIDE:
if (!mScalingRunnable.isFinished()) {
mScalingRunnable.abortAnimation();
}
mLastMotionY = ev.getY();
mActivePointerId = ev.getPointerId(0);
mMaxScale = (mScreenHeight / mZoomHeight);
mLastScale = (mZoomContainer.getBottom() / mZoomHeight);
if (onScrollViewZoomListener != null) {
onScrollViewZoomListener.onStart();
}
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "mActivePointerId = " + mActivePointerId);
int j = ev.findPointerIndex(this.mActivePointerId);
if (j == -1) {
Log.e(TAG, "Invalid pointerId = " + mActivePointerId + " in onTouchEvent");
} else {
if (mLastMotionY == -1.0F) {
mLastMotionY = ev.getY(j);
}
if (mZoomContainer.getBottom() >= mZoomHeight) {
LayoutParams localLayoutParams = (LayoutParams) mZoomContainer.getLayoutParams();
ViewGroup.LayoutParams headLayoutParams = mHeaderContainer.getLayoutParams();
float f = ((ev.getY(j) - mLastMotionY + mZoomContainer.getBottom()) / mZoomHeight - mLastScale) / 2.0F + mLastScale;
if ((mLastScale <= 1.0D) && (f < mLastScale)) {
localLayoutParams.height = mZoomHeight;
localLayoutParams.width = mZoomWidth;
localLayoutParams.gravity = Gravity.CENTER;
headLayoutParams.height = mZoomHeight;
mZoomContainer.setLayoutParams(localLayoutParams);
mHeaderContainer.setLayoutParams(headLayoutParams);
return super.onTouchEvent(ev);
}
mLastScale = Math.min(Math.max(f, 1.0F), mMaxScale);
localLayoutParams.height = ((int) (mZoomHeight * mLastScale));
localLayoutParams.width = ((int) (mZoomWidth * mLastScale));
localLayoutParams.gravity = Gravity.CENTER;
headLayoutParams.height = ((int) (mZoomHeight * mLastScale));
if (localLayoutParams.height < mScreenHeight) {
mZoomContainer.setLayoutParams(localLayoutParams);
mHeaderContainer.setLayoutParams(headLayoutParams);
}
mLastMotionY = ev.getY(j);
return true;
}
this.mLastMotionY = ev.getY(j);
}
break;
case MotionEvent.ACTION_UP:
reset();
endScaling();
if (onScrollViewZoomListener != null) {
onScrollViewZoomListener.onFinish();
}
break;
case MotionEvent.ACTION_CANCEL:
int i = ev.getActionIndex();
mLastMotionY = ev.getY(i);
mActivePointerId = ev.getPointerId(i);
break;
case MotionEvent.ACTION_POINTER_DOWN:
onSecondaryPointerUp(ev);
mLastMotionY = ev.getY(ev.findPointerIndex(mActivePointerId));
break;
}
}
return super.onTouchEvent(ev);
}
private void onSecondaryPointerUp(MotionEvent paramMotionEvent) {
int i = (paramMotionEvent.getAction()) >> 8;
if (paramMotionEvent.getPointerId(i) == mActivePointerId)
if (i != 0) {
mLastMotionY = paramMotionEvent.getY(0);
mActivePointerId = paramMotionEvent.getPointerId(0);
}
}
private void reset() {
this.mActivePointerId = -1;
this.mLastMotionY = -1.0F;
this.mMaxScale = -1.0F;
this.mLastScale = -1.0F;
}
class ScalingRunnable implements Runnable {
long mDuration;
boolean mIsFinished = true;
float mScale;
long mStartTime;
ScalingRunnable() {
}
public void abortAnimation() {
mIsFinished = true;
}
public boolean isFinished() {
return mIsFinished;
}
public void run() {
float f2;
LayoutParams localLayoutParams;
ViewGroup.LayoutParams headLayoutParams;
if ((!mIsFinished) && (mScale > 1.0D)) {
float f1 = ((float) SystemClock.currentThreadTimeMillis() - (float) mStartTime) / (float) mDuration;
f2 = mScale - (mScale - 1.0F) * sInterpolator.getInterpolation(f1);
localLayoutParams = (LayoutParams) mZoomContainer.getLayoutParams();
headLayoutParams = mHeaderContainer.getLayoutParams();
if (f2 > 1.0F) {
Log.d(TAG, "f2 > 1.0");
localLayoutParams.height = ((int) (f2 * mZoomHeight));
localLayoutParams.width = ((int) (f2 * mZoomWidth));
localLayoutParams.gravity = Gravity.CENTER;
mZoomContainer.setLayoutParams(localLayoutParams);
headLayoutParams.height = ((int) (f2 * mZoomHeight));
mHeaderContainer.setLayoutParams(headLayoutParams);
post(this);
return;
}
mIsFinished = true;
}
}
public void startAnimation(long paramLong) {
mStartTime = SystemClock.currentThreadTimeMillis();
mDuration = paramLong;
mScale = ((float) (mZoomContainer.getBottom()) / mZoomHeight);
mIsFinished = false;
post(this);
}
}
public interface OnScrollViewChangedListener {
public void onScrollChanged(int left, int top, int oldLeft, int oldTop);
}
public interface OnScrollViewZoomListener {
public void onStart();
public void onFinish();
}
}