package com.cheng.animationstudy.customview.ucbrowser; import android.content.Context; import android.graphics.Color; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; import android.view.animation.DecelerateInterpolator; import android.widget.ScrollView; import android.widget.TextView; import com.cheng.animationstudy.R; import com.cheng.utils.ViewFinder; import com.nineoldandroids.animation.Animator; import com.nineoldandroids.animation.ObjectAnimator; import com.nineoldandroids.view.ViewHelper; /** * */ public class PullLayout extends ScrollView { private View mTopRL; private View mWeaterLL; private View mContentLL; private TextView mTV; private EyeView mEV; private ObjectAnimator mObjectAnimator; private float mLastY = -1; private float mDetalY = -1; private int mRange; private int mTVHeight; private int mTVWidth; private boolean mIsTouchOrRunning; private boolean mIsActionCancel; public PullLayout(Context context) { super(context); } public PullLayout(Context context, AttributeSet attrs) { super(context, attrs); } public PullLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onFinishInflate() { super.onFinishInflate(); setVerticalScrollBarEnabled(false); mTopRL = ViewFinder.findViewById(this, R.id.rl_top); mContentLL = ViewFinder.findViewById(this, R.id.ll_content); mTV = ViewFinder.findViewById(this, R.id.tv); mEV = ViewFinder.findViewById(this, R.id.ev); mWeaterLL = ViewFinder.findViewById(this, R.id.ll_weather); mTopRL.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @SuppressWarnings("deprecation") @Override public void onGlobalLayout() { mTopRL.getViewTreeObserver().removeGlobalOnLayoutListener(this); mRange = mTopRL.getHeight(); scrollTo(0, mRange); mTopRL.getLayoutParams().height = mRange; } }); mTV.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @SuppressWarnings("deprecation") @Override public void onGlobalLayout() { mTV.getViewTreeObserver().removeGlobalOnLayoutListener(this); mTVHeight = mTV.getHeight(); mTVWidth = mTV.getWidth(); ViewHelper.setTranslationY(mContentLL, mTVHeight); } }); mEV.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { close(); } }); mTV.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { open(); } }); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mIsActionCancel = false; mIsTouchOrRunning = true; mLastY = ev.getY(); break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { if (mObjectAnimator != null && mObjectAnimator.isRunning()) { ev.setAction(MotionEvent.ACTION_UP); mIsActionCancel = true; } if (mIsActionCancel && ev.getAction() != MotionEvent.ACTION_DOWN) { return false; } if (ev.getActionIndex() != 0 && getScrollY() < mRange) { ev.setAction(MotionEvent.ACTION_UP); mIsActionCancel = true; } switch (ev.getAction()) { case MotionEvent.ACTION_MOVE: mIsTouchOrRunning = true; if (getScrollY() != 0) { mDetalY = 0; mLastY = ev.getY(); } else { mDetalY = ev.getY() - mLastY; if (mDetalY > 0) { setT((int) -mDetalY / 5); return true; } } break; case MotionEvent.ACTION_UP: mIsTouchOrRunning = false; if (getScrollY() < mRange) { if (mDetalY != 0) { reset(); } else { toggle(); } return true; } break; } return super.onTouchEvent(ev); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (t > mRange) { return; } else if (!mIsTouchOrRunning && t != mRange) { scrollTo(0, mRange); } else { animateScroll(t); } } public void setT(int t) { scrollTo(0, t); if (t < 0) { animatePull(t); } } private void animateScroll(int t) { float percent = (float) t / mRange; ViewHelper.setTranslationY(mTopRL, t); ViewHelper.setTranslationY(mContentLL, mTVHeight * percent); ViewHelper.setScaleX(mTV, 2 - percent); ViewHelper.setScaleY(mTV, 2 - percent); ViewHelper.setTranslationX(mTV, mTVWidth * (1 - percent) / 2f); ViewHelper.setTranslationY(mTV, t + mTVHeight * (1 - percent) / 2f); ViewHelper.setTranslationY(mEV, -t / 2); ViewHelper.setTranslationY(mWeaterLL, -t / 2); mEV.setRadius((int) (mRange * 0.25f * (1 - percent))); mTV.setTextColor(evaluate(percent, Color.WHITE, Color.BLACK)); } private void animatePull(int t) { mTopRL.getLayoutParams().height = mRange - t; mTopRL.requestLayout(); float percent = (float) t / mRange; ViewHelper.setScaleX(mEV, 1 - percent); ViewHelper.setScaleY(mEV, 1 - percent); ViewHelper.setScaleX(mTV, 2 - percent); ViewHelper.setScaleY(mTV, 2 - percent); ViewHelper.setTranslationX(mTV, mTVWidth * (1 - percent) / 2f); ViewHelper.setTranslationY(mWeaterLL, t / 2); } private Integer evaluate(float fraction, Object startValue, Integer endValue) { int startInt = (Integer) startValue; int startA = (startInt >> 24) & 0xff; int startR = (startInt >> 16) & 0xff; int startG = (startInt >> 8) & 0xff; int startB = startInt & 0xff; int endInt = (Integer) endValue; int endA = (endInt >> 24) & 0xff; int endR = (endInt >> 16) & 0xff; int endG = (endInt >> 8) & 0xff; int endB = endInt & 0xff; return (int) ((startA + (int) (fraction * (endA - startA))) << 24) | (int) ((startR + (int) (fraction * (endR - startR))) << 16) | (int) ((startG + (int) (fraction * (endG - startG))) << 8) | (int) ((startB + (int) (fraction * (endB - startB)))); } public void toggle() { if (isOpen()) { close(); } else { open(); } } private Status status; public enum Status { Open, Close; } public boolean isOpen() { return status == Status.Open; } private void reset() { if (mObjectAnimator != null && mObjectAnimator.isRunning()) { return; } mObjectAnimator = ObjectAnimator.ofInt(this, "t", (int) -mDetalY / 5, 0); mObjectAnimator.setDuration(150); mObjectAnimator.start(); } public void close() { if (mObjectAnimator != null && mObjectAnimator.isRunning()) { return; } mObjectAnimator = ObjectAnimator.ofInt(this, "t", getScrollY(), mRange); mObjectAnimator.setInterpolator(new DecelerateInterpolator()); mObjectAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator arg0) { mIsTouchOrRunning = true; } @Override public void onAnimationRepeat(Animator arg0) { } @Override public void onAnimationEnd(Animator arg0) { mIsTouchOrRunning = false; status = Status.Close; } @Override public void onAnimationCancel(Animator arg0) { } }); mObjectAnimator.setDuration(250); mObjectAnimator.start(); } public void open() { if (mObjectAnimator != null && mObjectAnimator.isRunning()) { return; } mObjectAnimator = ObjectAnimator.ofInt(this, "t", getScrollY(), (int) (-getScrollY() / 2.2f), 0); mObjectAnimator.setInterpolator(new DecelerateInterpolator()); mObjectAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator arg0) { mIsTouchOrRunning = true; } @Override public void onAnimationRepeat(Animator arg0) { } @Override public void onAnimationEnd(Animator arg0) { mIsTouchOrRunning = false; status = Status.Open; } @Override public void onAnimationCancel(Animator arg0) { } }); mObjectAnimator.setDuration(400); mObjectAnimator.start(); } }