滾動回彈效果分析:
首先,創建一個類,繼承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;
}
}
