/** * ScrollView反彈效果的實現 */public class BounceScrollView extends ScrollView {private View inner;// 孩子Viewprivate float y;// 點擊時y座標private Rect normal = new Rect();// 矩形(這裡只是個形式,只是用於判斷是否需要動畫.)private boolean isCount = false;// 是否開始計算public BounceScrollView(Context context, AttributeSet attrs) {super(context, attrs);}/*** * 根據 XML 產生視圖工作完成.該函數在產生視圖的最後調用,在所有子視圖添加完之後. 即使子類覆蓋了 onFinishInflate * 方法,也應該調用父類的方法,使該方法得以執行. */@Overrideprotected void onFinishInflate() {if (getChildCount() > 0) {inner = getChildAt(0);}}/*** * 監聽touch */@Overridepublic boolean onTouchEvent(MotionEvent ev) {if (inner != null) {commOnTouchEvent(ev);}return super.onTouchEvent(ev);}/*** * 觸摸事件 * * @param ev */public void commOnTouchEvent(MotionEvent ev) {int action = ev.getAction();switch (action) {case MotionEvent.ACTION_DOWN:break;case MotionEvent.ACTION_UP:// 手指鬆開.if (isNeedAnimation()) {animation();isCount = false;}break;/*** * 排除出第一次移動計算,因為第一次無法得知y座標, 在MotionEvent.ACTION_DOWN中擷取不到, * 因為此時是MyScrollView的touch事件傳遞到到了LIstView的孩子item上面.所以從第二次計算開始. * 然而我們也要進行初始化,就是第一次移動的時候讓滑動距離歸0. 之後記錄準確了就正常執行. */case MotionEvent.ACTION_MOVE:final float preY = y;// 按下時的y座標float nowY = ev.getY();// 時時y座標int deltaY = (int) (preY - nowY);// 滑動距離if (!isCount) {deltaY = 0; // 在這裡要歸0.}y = nowY;// 當滾動到最上或者最下時就不會再滾動,這時移動布局if (isNeedMove()) {// 初始化頭部矩形if (normal.isEmpty()) {// 儲存正常的布局位置normal.set(inner.getLeft(), inner.getTop(),inner.getRight(), inner.getBottom());}Log.e("jj", "矩形:" + inner.getLeft() + "," + inner.getTop()+ "," + inner.getRight() + "," + inner.getBottom());// 移動布局inner.layout(inner.getLeft(), inner.getTop() - deltaY / 2,inner.getRight(), inner.getBottom() - deltaY / 2);}isCount = true;break;default:break;}}/*** * 回縮動畫 */public void animation() {// 開啟移動動畫TranslateAnimation ta = new TranslateAnimation(0, 0, inner.getTop(),normal.top);ta.setDuration(200);inner.startAnimation(ta);// 設定回到正常的布局位置inner.layout(normal.left, normal.top, normal.right, normal.bottom);Log.e("jj", "迴歸:" + normal.left + "," + normal.top + "," + normal.right+ "," + normal.bottom);normal.setEmpty();}// 是否需要開啟動畫public boolean isNeedAnimation() {return !normal.isEmpty();}/*** * 是否需要移動布局 inner.getMeasuredHeight():擷取的是控制項的總高度 * * getHeight():擷取的是螢幕的高度 * * @return */public boolean isNeedMove() {int offset = inner.getMeasuredHeight() - getHeight();int scrollY = getScrollY();Log.e("jj", "scrolly=" + scrollY);// 0是頂部,後面那個是底部if (scrollY == 0 || scrollY == offset) {return true;}return false;}}
使用方法
<com.techrare.view.BounceScrollView android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@color/tab_chart_bg" android:scrollbars="none" > <LinearLayout android:layout_width="fill_parent" android:layout_height="match_parent" android:gravity="center_horizontal" android:orientation="vertical" android:paddingLeft="20dp" android:paddingRight="20dp" > <span style="white-space:pre"> </span><!-- 這裡可以盡情的布局 --> </LinearLayout> </com.techrare.view.BounceScrollView>
原文地址http://blog.csdn.net/h7870181/article/details/8960430