阻尼滑动--可以滑动过度的ScrollView(OverScrollView)

   

    贴上一个我自己用过的阻尼滑动的ScrollView,像QQ里面那种滑动效果,虽然不是我写的,但是我觉得还可以,贴出来做个记录,有用到的时候免得到处去找。

    代码如下:

/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */



import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.FocusFinder;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.animation.AnimationUtils;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
import android.widget.Scroller;
import android.view.View.OnTouchListener;

import java.lang.reflect.Field;
import java.util.List;


/**
 * Layout container for a view hierarchy that can be scrolled by the user,
 * allowing it to be larger than the physical display. A ScrollView is a
 * {@link FrameLayout}, meaning you should place one child in it containing the
 * entire contents to scroll; this child may itself be a layout manager with a
 * complex hierarchy of objects. A child that is often used is a
 * {@link LinearLayout} in a vertical orientation, presenting a vertical array
 * of top-level items that the user can scroll through.
 * 
 * <p>
 * The {@link TextView} class also takes care of its own scrolling, so does not
 * require a ScrollView, but using the two together is possible to achieve the
 * effect of a text view within a larger container.
 * 
 * <p>
 * ScrollView only supports vertical scrolling.
 */
public class OverScrollView extends FrameLayout implements OnTouchListener
{
	private static final String TAG = OverScrollView.class.getName();
	static final int ANIMATED_SCROLL_GAP = 250;

	static final float MAX_SCROLL_FACTOR = 0.5f;
	private static final float SCROLL_RATIO = 0.3f;// 阻尼系数 
	static final float OVERSHOOT_TENSION = 0.75f;

	private long mLastScroll;

	private final Rect mTempRect = new Rect();
	private Scroller mScroller;

	protected Context mContext;

	Field mScrollYField;
	Field mScrollXField;

	boolean hasFailedObtainingScrollFields;
	int prevScrollY;
	boolean isInFlingMode = false;

	DisplayMetrics metrics;
	LayoutInflater inflater;
	protected View child;

	private Runnable overScrollerSpringbackTask;

	/**
	 * Flag to indicate that we are moving focus ourselves. This is so the code
	 * that watches for focus changes initiated outside this ScrollView knows
	 * that it does not have to do anything.
	 */
	private boolean mScrollViewMovedFocus;

	/**
	 * Position of the last motion event.
	 */
	private float mLastMotionY;

	/**
	 * True when the layout has changed but the traversal has not come through
	 * yet. Ideally the view hierarchy would keep track of this for us.
	 */
	private boolean mIsLayoutDirty = true;

	/**
	 * The child to give focus to in the event that a child has requested focus
	 * while the layout is dirty. This prevents the scroll from being wrong if
	 * the child has not been laid out before requesting focus.
	 */
	private View mChildToScrollTo = null;

	/**
	 * True if the user is currently dragging this ScrollView around. This is
	 * not the same as 'is being flinged', which can be checked by
	 * mScroller.isFinished() (flinging begins when the user lifts his finger).
	 */
	private boolean mIsBeingDragged = false;

	/**
	 * Determines speed during touch scrolling
	 */
	private VelocityTracker mVelocityTracker;

	/**
	 * When set to true, the scroll view measure its child to make it fill the
	 * currently visible area.
	 */
	private boolean mFillViewport;

	/**
	 * Whether arrow scrolling is animated.
	 */
	private boolean mSmoothScrollingEnabled = true;

	private int mTouchSlop;
	private int mMinimumVelocity;
	private int mMaximumVelocity;

	/**
	 * ID of the active pointer. This is used to retain consistency during
	 * drags/flings if multiple pointers are used.
	 */
	private int mActivePointerId = INVALID_POINTER;

	/**
	 * Sentinel value for no current active pointer. Used by
	 * {@link #mActivePointerId}.
	 */
	private static final int INVALID_POINTER = -1;

	public OverScrollView(Context context)
	{
		this(context, null);
		mContext = context;
		initBounce();
	}

	public OverScrollView(Context context, AttributeSet attrs)
	{

		this(context, attrs, 0);
		mContext = context;
		initBounce();
	}

	public OverScrollView(Context context, AttributeSet attrs, int defStyle)
	{
		super(context, attrs, defStyle);
		mContext = context;

		initScrollView();
		setFillViewport(true);
		initBounce();
	}

	private void initBounce()
	{
		metrics = this.mContext.getResources().getDisplayMetrics();

		// init the bouncy scroller, and make sure the layout is being drawn
		// after the top padding
		mScroller = new Scroller(getContext(), new OvershootInterpolator(OVERSHOOT_TENSION));
		overScrollerSpringbackTask = new Runnable()
		{
			@Override
			public void run()
			{
				// scroll till after the padding
				mScroller.computeScrollOffset();
				scrollTo(0, mScroller.getCurrY());

				if (!mScroller.isFinished())
				{
					post(this);
				}
			}
		};
		prevScrollY = getPaddingTop();

		try
		{
			mScrollXField = View.class.getDeclaredField("mScrollX");
			mScrollYField = View.class.getDeclaredField("mScrollY");

		} catch (Exception e)
		{
			hasFailedObtainingScrollFields = true;
		}
	}

	private void SetScrollY(int value)
	{
		if (mScrollYField != null)
		{
			try
			{
				mScrollYField.setInt(this, value);
			} catch (Exception e)
			{
			}
		}
	}

	private void SetScrollX(int value)
	{
		if (mScrollXField != null)
		{
			try
			{
				mScrollXField.setInt(this, value);
			} catch (Exception e)
			{
			}
		}
	}

	public void initChildPointer()
	{
		Log.i(TAG,"initChildPointer");
		child = getChildAt(0);
		child.setPadding(0, 1500, 0, 1500);

	}

	@Override
	protected float getTopFadingEdgeStrength()
	{
		if (getChildCount() == 0)
		{
			return 0.0f;
		}

		final int length = getVerticalFadingEdgeLength();
		if (getScrollY() < length)
		{
			return getScrollY() / (float) length;
		}

		return 1.0f;
	}

	@Override
	protected float getBottomFadingEdgeStrength()
	{
		if (getChildCount() == 0)
		{
			return 0.0f;
		}

		final int length = getVerticalFadingEdgeLength();
		final int bottomEdge = getHeight() - getPaddingBottom();
		final int span = getChildAt(0).getBottom() - getScrollY() - bottomEdge;
		if (span < length)
		{
			return span / (float) length;
		}

		return 1.0f;
	}

	/**
	 * @return The maximum amount this scroll view will scroll in response to an
	 *         arrow event.
	 */
	public int getMaxScrollAmount()
	{
		return (int) (MAX_SCROLL_FACTOR * (getBottom() - getTop()));
	}

	private void initScrollView()
	{
		mScroller = new Scroller(getContext());
		setFocusable(true);
		setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
		setWillNotDraw(false);
		final ViewConfiguration configuration = ViewConfiguration.get(mContext);
		mTouchSlop = configuration.getScaledTouchSlop();
		mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
		mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();

		setOnTouchListener(this);
		
		post(new Runnable()
		{
			public void run()
			{
				scrollTo(0, child.getPaddingTop());
			}
		});
	}

	@Override
	public void addView(View child)
	{
		if (getChildCount() > 0)
		{
			throw new IllegalStateException("ScrollView can host only one direct child");
		}

		super.addView(child);
		initChildPointer();
	}

	@Override
	public void addView(View child, int index)
	{
		if (getChildCount() > 0)
		{
			throw new IllegalStateException("ScrollView can host only one direct child");
		}

		super.addView(child, index);
		initChildPointer();
	}

	@Override
	public void addView(View child, ViewGroup.LayoutParams params)
	{
		if (getChildCount() > 0)
		{
			throw new IllegalStateException("ScrollView can host only one direct child");
		}

		super.addView(child, params);
		initChildPointer();
	}

	@Override
	public void addView(View child, int index, ViewGroup.LayoutParams params)
	{
		if (getChildCount() > 0)
		{
			throw new IllegalStateException("ScrollView can host only one direct child");
		}

		super.addView(child, index, params);
	}

	/**
	 * @return Returns true this ScrollView can be scrolled
	 */
	private boolean canScroll()
	{
		View child = getChildAt(0);
		if (child != null)
		{
			int childHeight = child.getHeight();
			return getHeight() < childHeight + getPaddingTop() + getPaddingBottom();
		}
		return false;
	}

	/**
	 * Indicates whether this ScrollView's content is stretched to fill the
	 * viewport.
	 * 
	 * @r
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值