滾動回彈效果分析:
首先,創建一個類,繼承scrollview,重寫ontouch事件,實現伸縮回彈效果。
scroollview節點下只能有一個子節點,這個子節點就是我們要移動的view布局。
第一步:獲取要操作的子view布局
第二步:重寫onTouch事件監聽
分析具體事件:
觀察分析得出結論:
讓布局移動每一次拉動的Y軸一半的距離,然后松手滾動[攜帶動畫]回到原來的位置。
下拉或者上拉的時候,記錄按下時的Y軸位置
action_down
移動過程中的處理:
計算上一次與本次的Y軸(拉動距離)[而不是按下時候的Y值,和現在移動到的Y值,是每上一次和本次的Y值比較
判斷是否需要移動布局的情況:Y軸的一個距離偏移
//2種情況,隨着布局的拖動, inner.getMeasuredHeight()的值是變化的
//inner.getMeasuredHeight()與getHeight()的區別:
當屏幕可以包裹內容的時候,他們的值相等
當view的高度超出屏幕時,getMeasuredHeight()是實際View的大小,與屏幕無關,getHeight的大小此時則是屏幕的大小。
此時,getMeasuredHeight() = getHeight+超出部分。
抬起的處理:布局回滾到正常位置
移動動畫回滾到正常位置(*:動畫執行期間,不允許拖拉操作)
距離:-的滾動距離
public class MyScrollview extends ScrollView { //要操作的布局 private View innerView; private float y; private Rect normal = new Rect(); private boolean animationFinish = true; public MyScrollview(Context context) { super(context, null); } public MyScrollview(Context context, AttributeSet attrs) { super(context, attrs); } public MyScrollview(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onFinishInflate() { int childCount = getChildCount(); if (childCount > 0) { innerView = getChildAt(0); } } @Override public boolean onTouchEvent(MotionEvent ev) { if (innerView == null) { return super.onTouchEvent(ev); } else { commonTouchEvent(ev); } return super.onTouchEvent(ev); } /** * 自定義touch事件處理 * * @param ev */ private void commonTouchEvent(MotionEvent ev) { if (animationFinish) { int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: y = ev.getY(); break; case MotionEvent.ACTION_MOVE: float preY = y == 0 ? ev.getY() : y; float nowY = ev.getY(); int detailY = (int) (preY - nowY); y = nowY; //操作view進行拖動detailY的一半 if (isNeedMove()) { //布局改變位置之前,記錄一下正常狀態的位置 if (normal.isEmpty()) { normal.set(innerView.getLeft(), innerView.getTop(), innerView.getRight(), innerView.getBottom()); } innerView.layout(innerView.getLeft(), innerView.getTop() - detailY / 2, innerView.getRight(), innerView.getBottom() - detailY / 2); } break; case MotionEvent.ACTION_UP: y = 0; //布局回滾到原來的位置 if (isNeedAnimation()) { animation(); } break; } } } private void animation() { TranslateAnimation ta = new TranslateAnimation(0, 0, 0, normal.top - innerView.getTop()); ta.setDuration(200); ta.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { animationFinish = false; } @Override public void onAnimationEnd(Animation animation) { innerView.clearAnimation(); innerView.layout(normal.left, normal.top, normal.right, normal.bottom); normal.setEmpty(); animationFinish = true; } @Override public void onAnimationRepeat(Animation animation) { } }); innerView.startAnimation(ta); } /** * 判斷是否需要回滾 * * @return */ private boolean isNeedAnimation() { return !normal.isEmpty(); } /** * 判斷是否需要移動 * * @return */ private boolean isNeedMove() { int offset = innerView.getMeasuredHeight() - getHeight(); int scrollY = getScrollY(); Log.e("zoubo", "getMeasuredHeight:" + innerView.getMeasuredHeight() + "----getHeight:" + getHeight()); Log.e("zoubo", "offset:" + offset + "----scrollY:" + scrollY); if (scrollY == 0 || scrollY == offset) { return true; } return false; } }