package com.todayinfo.ui.component.pulltozoomview; import android.content.Context; import android.content.res.TypedArray; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; import android.widget.AbsListView; import android.widget.Adapter; import android.widget.AdapterView; import android.widget.FrameLayout; import android.widget.ListAdapter; import android.widget.ListView; /** * Author: ZhuWenWu Version V1.0 Date: 2014/11/7 18:01. Description: * Modification History: Date Author Version Description * ------------------------ * ----------------------------------------------------------- 2014/11/7 * ZhuWenWu 1.0 1.0 Why & What is modified: */ public class PullToZoomListViewEx extends PullToZoomBase<ListView> implements AbsListView.OnScrollListener { private static final String TAG = PullToZoomListViewEx.class .getSimpleName(); private FrameLayout mHeaderContainer; private int mHeaderHeight; private ScalingRunnable mScalingRunnable; 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))); } }; public PullToZoomListViewEx(Context context) { this(context, null); } public PullToZoomListViewEx(Context context, AttributeSet attrs) { super(context, attrs); mRootView.setOnScrollListener(this); mScalingRunnable = new ScalingRunnable(); } /** * 是否显示headerView * * @param isHideHeader * true: show false: hide */ @Override public void setHideHeader(boolean isHideHeader) { if (isHideHeader != isHideHeader()) { super.setHideHeader(isHideHeader); if (isHideHeader) { removeHeaderView(); } else { updateHeaderView(); } } } @Override public void setHeaderView(View headerView) { if (headerView != null) { this.mHeaderView = headerView; updateHeaderView(); } } @Override public void setZoomView(View zoomView) { if (zoomView != null) { this.mZoomView = zoomView; updateHeaderView(); } } /** * 移除HeaderView 如果要兼容API 9,需要修改此处逻辑,API 11以下不支持动态添加header */ private void removeHeaderView() { if (mHeaderContainer != null) { mRootView.removeHeaderView(mHeaderContainer); } } /** * 更新HeaderView 先移除-->再添加zoomView、HeaderView -->然后添加到listView的head 如果要兼容API * 9,需要修改此处逻辑,API 11以下不支持动态添加header */ private void updateHeaderView() { if (mHeaderContainer != null) { mRootView.removeHeaderView(mHeaderContainer); mHeaderContainer.removeAllViews(); if (mZoomView != null) { mHeaderContainer.addView(mZoomView); } if (mHeaderView != null) { mHeaderContainer.addView(mHeaderView); } mHeaderHeight = mHeaderContainer.getHeight(); mRootView.addHeaderView(mHeaderContainer); } } public void setAdapter(ListAdapter adapter) { mRootView.setAdapter(adapter); } public void setOnItemClickListener(AdapterView.OnItemClickListener listener) { mRootView.setOnItemClickListener(listener); } /** * 创建listView 如果要兼容API9,需要修改此处 * * @param context * 上下文 * @param attrs * AttributeSet * @return ListView */ @Override protected ListView createRootView(Context context, AttributeSet attrs) { ListView lv = new ListView(context, attrs); // Set it to this so it can be used in ListActivity/ListFragment lv.setId(android.R.id.list); return lv; } /** * 重置动画,自动滑动到顶部 */ @Override protected void smoothScrollToTop() { Log.d(TAG, "smoothScrollToTop --> "); mScalingRunnable.startAnimation(200L); } /** * zoomView动画逻辑 * * @param newScrollValue * 手指Y轴移动距离值 */ @Override protected void pullHeaderToZoom(int newScrollValue) { Log.d(TAG, "pullHeaderToZoom --> newScrollValue = " + newScrollValue); Log.d(TAG, "pullHeaderToZoom --> mHeaderHeight = " + mHeaderHeight); if (mScalingRunnable != null && !mScalingRunnable.isFinished()) { mScalingRunnable.abortAnimation(); } ViewGroup.LayoutParams localLayoutParams = mHeaderContainer .getLayoutParams(); localLayoutParams.height = Math.abs(newScrollValue) + mHeaderHeight; mHeaderContainer.setLayoutParams(localLayoutParams); } @Override protected boolean isReadyForPullStart() { return isFirstItemVisible(); } private boolean isFirstItemVisible() { final Adapter adapter = mRootView.getAdapter(); if (null == adapter || adapter.isEmpty()) { return true; } else { /** * This check should really just be: * mRootView.getFirstVisiblePosition() == 0, but PtRListView * internally use a HeaderView which messes the positions up. For * now we'll just add one to account for it and rely on the inner * condition which checks getTop(). */ if (mRootView.getFirstVisiblePosition() <= 1) { final View firstVisibleChild = mRootView.getChildAt(0); if (firstVisibleChild != null) { return firstVisibleChild.getTop() >= mRootView.getTop(); } } } return false; } @Override public void handleStyledAttributes(TypedArray a) { mHeaderContainer = new FrameLayout(getContext()); if (mZoomView != null) { mHeaderContainer.addView(mZoomView); } if (mHeaderView != null) { mHeaderContainer.addView(mHeaderView); } mRootView.addHeaderView(mHeaderContainer); } /** * 设置HeaderView高度 * * @param width * 宽 * @param height * 高 */ public void setHeaderViewSize(int width, int height) { if (mHeaderContainer != null) { Object localObject = mHeaderContainer.getLayoutParams(); if (localObject == null) { localObject = new AbsListView.LayoutParams(width, height); } ((ViewGroup.LayoutParams) localObject).width = width; ((ViewGroup.LayoutParams) localObject).height = height; mHeaderContainer .setLayoutParams((ViewGroup.LayoutParams) localObject); mHeaderHeight = height; } } public void setHeaderLayoutParams(AbsListView.LayoutParams layoutParams) { if (mHeaderContainer != null) { mHeaderContainer.setLayoutParams(layoutParams); mHeaderHeight = layoutParams.height; } } protected void onLayout(boolean paramBoolean, int paramInt1, int paramInt2, int paramInt3, int paramInt4) { super.onLayout(paramBoolean, paramInt1, paramInt2, paramInt3, paramInt4); Log.d(TAG, "onLayout --> "); if (mHeaderHeight == 0 && mHeaderContainer != null) { mHeaderHeight = mHeaderContainer.getHeight(); } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { Log.d(TAG, "onScrollStateChanged --> "); } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (mZoomView != null && !isHideHeader() && isPullToZoomEnabled()) { float f = mHeaderHeight - mHeaderContainer.getBottom(); Log.d(TAG, "onScroll --> f = " + f); if (isParallax()) { if ((f > 0.0F) && (f < mHeaderHeight)) { int i = (int) (0.65D * f); mHeaderContainer.scrollTo(0, -i); } else if (mHeaderContainer.getScrollY() != 0) { mHeaderContainer.scrollTo(0, 0); } } } } class ScalingRunnable implements Runnable { protected long mDuration; protected boolean mIsFinished = true; protected float mScale; protected long mStartTime; ScalingRunnable() { } public void abortAnimation() { mIsFinished = true; } public boolean isFinished() { return mIsFinished; } public void run() { if (mZoomView != null) { float f2; ViewGroup.LayoutParams localLayoutParams; if ((!mIsFinished) && (mScale > 1.0D)) { float f1 = ((float) SystemClock.currentThreadTimeMillis() - (float) mStartTime) / (float) mDuration; f2 = mScale - (mScale - 1.0F) * PullToZoomListViewEx.sInterpolator .getInterpolation(f1); localLayoutParams = mHeaderContainer.getLayoutParams(); Log.d(TAG, "ScalingRunnable --> f2 = " + f2); if (f2 > 1.0F) { localLayoutParams.height = ((int) (f2 * mHeaderHeight)); mHeaderContainer.setLayoutParams(localLayoutParams); post(this); return; } mIsFinished = true; } } } public void startAnimation(long paramLong) { if (mZoomView != null) { mStartTime = SystemClock.currentThreadTimeMillis(); mDuration = paramLong; mScale = ((float) (mHeaderContainer.getBottom()) / mHeaderHeight); mIsFinished = false; post(this); } } } }