android中仿qq最新版抽屉,Android 自定义View实现抽屉效果
Android 自定義View實現抽屜效果
說明
這個自定義View,沒有處理好多點觸摸問題
View跟著手指移動,沒有采用傳統的scrollBy方法,而是通過不停地重新布局子View的方式,來使得子View產生滾動效果menuView.layout(menuLeft, 0, menuLeft + menuWidth, menuHeight);
相應的,由于沒有使用scrollBy方法,就沒有產生getScrollX值,所以不能通過Scroller的startScroll方法來完成手指離開后的平滑滾動效果,而是使用了Animation動畫的applyTransformation方法來完成插值,從而實現動畫效果
主要算法是:動畫當前值=起始值+(目標值-起始值)*interpolatedTime
其中interpolatedTime是一個0.0f~1.0f的數字,系統自己插值計算好了(默認是線性變化的),當然你可以自己寫插值器
/**
* 由于上面不能使用scrollBy,那么這里就不能使用Scroller這個類來完成平滑移動了,還好我們有動畫
*/
class MyAnimation extends Animation {
private int viewCurrentLfet;
private int viewStartLfet;
private int viewTargetLfet;
private int viewWidth;
private View view;
private int cha;
public MyAnimation(View view, int viewStartLfet, int viewTargetLfet, int viewWidth) {
this.view = view;
this.viewStartLfet = viewStartLfet;
this.viewTargetLfet = viewTargetLfet;
this.viewWidth = viewWidth;
cha = viewTargetLfet - viewStartLfet;
setDuration(Math.abs(cha));
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
viewCurrentLfet = (int) (viewStartLfet + cha * interpolatedTime);
view.layout(viewCurrentLfet, 0, viewCurrentLfet + viewWidth, menuHeight);
}
}
完整代碼
package com.sunshine.choutidemo;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.Transformation;
/**
* Created by a on 2016/8/15.
*/
public class ChouTiView extends ViewGroup {
private View mainView;
private View menuView;
private int menuWidth;
private int downX;
private int lastX;
private int moveX;
private int deltaX;
private int menuLeft;
private int mainLeft;
private int menuHeight;
private int mainWidth;
private int mainHeight;
private int menuLeftBorder;
private int mainLeftBorder;
private int menuRightBorder;
private int mainRightBorder;
private int mMaxVelocity;
private VelocityTracker mVelocityTracker;
private int mPointerId;
private float velocityX;
private float velocityY;
public ChouTiView(Context context) {
super(context);
init();
}
public ChouTiView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// 0.獲得此次最大速率
mMaxVelocity = ViewConfiguration.get(getContext()).getMaximumFlingVelocity();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mainView.measure(widthMeasureSpec, heightMeasureSpec);
menuView.measure(widthMeasureSpec, heightMeasureSpec);
// 獲得子View的正確寬度(只能獲取具體的數字值),但是不能這樣獲取高度,因為這里match—parent為-1
menuWidth = menuView.getLayoutParams().width;
menuLeft = (int) (-menuWidth * 0.5);
menuLeftBorder = (int) (-menuWidth * 0.5);
menuRightBorder = 0;
mainLeft = 0;
mainLeftBorder = 0;
mainRightBorder = menuWidth;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
menuHeight = b;
mainWidth = r;
mainHeight = b;
mainView.layout(l, t, r, b);
menuView.layout(menuLeft, t, menuLeft + menuWidth, b);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mainView = getChildAt(1);
menuView = getChildAt(0);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getActionMasked();
acquireVelocityTracker(event); //1.向VelocityTracker添加MotionEvent
final VelocityTracker verTracker = mVelocityTracker;
switch (action) {
case MotionEvent.ACTION_DOWN:
//2.求第一個觸點的id, 此時可能有多個觸點,但至少一個
// 獲取索引為0的手指id
mPointerId = event.getPointerId(0);
downX = (int) event.getX();
lastX = downX;
break;
case MotionEvent.ACTION_MOVE:
// 獲取當前手指id所對應的索引,雖然在ACTION_DOWN的時候,我們默認選取索引為0
// 的手指,但當有第二個手指觸摸,并且先前有效的手指up之后,我們會調整有效手指
// 屏幕上可能有多個手指,我們需要保證使用的是同一個手指的移動軌跡,
// 因此此處不能使用event.getActionIndex()來獲得索引
final int pointerIndex = event.findPointerIndex(mPointerId);
moveX = (int) event.getX(pointerIndex);
deltaX = moveX - lastX;
// 把觸摸移動引起的增量,體現在menu和main的左側left上
menuLeft = (int) (menuLeft + deltaX * 0.43);//讓菜單移動的慢一點
mainLeft = mainLeft + deltaX;
// 讓菜單根據手指增量移動,考慮兩側邊界問題(通過不停地layout實現移動效果)
// 為何不適用scrollBy,因為scrollBy移動的是外層的大View,現在需求是分別移動這個大view內的兩個小View
// scrollBy的話,會讓菜單和主頁面同時移動,不會產生錯位效果,
// 你會想,那讓小view自己scrollBy,這樣也是不行的,
// 因為讓小view,例如menu調用scrollBy的話,會讓menu自己的邊框在動,
// 看上去,是menu內部的文字在移動,但是menu并沒有在外層的大View里移動
// 說的很拗口,但是真的不能用scrollBy
if (menuLeft >= menuRightBorder) {
menuLeft = menuRightBorder;
} else if (menuLeft <= menuLeftBorder) {
menuLeft = menuLeftBorder;
}
menuView.layout(menuLeft, 0, menuLeft + menuWidth, menuHeight);
// 讓主頁面根據手指增量移動,考慮兩側邊界問題
if (mainLeft >= mainRightBorder) {
mainLeft = mainRightBorder;
} else if (mainLeft <= mainLeftBorder) {
mainLeft = mainLeftBorder;
}
mainView.layout(mainLeft, 0, mainLeft + mainWidth, mainHeight);
lastX = moveX;
break;
case MotionEvent.ACTION_UP:
//3.求偽瞬時速度
verTracker.computeCurrentVelocity(1000, mMaxVelocity);
velocityX = verTracker.getXVelocity(mPointerId);
Log.e("qwe", velocityX + "/" + mMaxVelocity);
if (velocityX > 1000) {
smoothToMenu();
} else if (velocityX < -2000) {
smoothToMain();
} else {
// 判斷松手的位置,如果大于1/2.5的菜單寬度就打開菜單,否則打開主頁面
if (mainLeft > menuWidth / 2.5) {
Log.e("qqq", "顯示菜單");
smoothToMenu();
} else {
Log.e("qqq", "顯示主頁面");
smoothToMain();
}
}
// 4.ACTION_UP釋放VelocityTracker,交給其他控件使用
releaseVelocityTracker();
break;
case MotionEvent.ACTION_CANCEL:
// 4.ACTION_UP釋放VelocityTracker,交給其他控件使用
releaseVelocityTracker();
case MotionEvent.ACTION_POINTER_UP:
// 獲取離開屏幕的手指的索引
int pointerIndexLeave = event.getActionIndex();
int pointerIdLeave = event.getPointerId(pointerIndexLeave);
if (mPointerId == pointerIdLeave) {
// 離開屏幕的正是目前的有效手指,此處需要重新調整,并且需要重置VelocityTracker
int reIndex = pointerIndexLeave == 0 ? 1 : 0;
mPointerId = event.getPointerId(reIndex);
// 調整觸摸位置,防止出現跳動
downX = (int) event.getX(reIndex);
// y = event.getY(reIndex);
releaseVelocityTracker();
}
releaseVelocityTracker();
break;
}
return true;
}
private void smoothToMain() {
MyAnimation menuAnimation = new MyAnimation(menuView, menuLeft, menuLeftBorder, menuWidth);
MyAnimation mainAnimation = new MyAnimation(mainView, mainLeft, mainLeftBorder, mainWidth);
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(menuAnimation);
animationSet.addAnimation(mainAnimation);
startAnimation(animationSet);
//一定記得更新menu和main的左側狀態,這影響到了,再次手指觸摸時候的動畫,否則突變
menuLeft = menuLeftBorder;
mainLeft = mainLeftBorder;
}
private void smoothToMenu() {
MyAnimation menuAnimation = new MyAnimation(menuView, menuLeft, menuRightBorder, menuWidth);
MyAnimation mainAnimation = new MyAnimation(mainView, mainLeft, mainRightBorder, mainWidth);
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(menuAnimation);
animationSet.addAnimation(mainAnimation);
startAnimation(animationSet);
//一定記得更新menu和main的左側狀態,這影響到了,再次手指觸摸時候的動畫,否則突變
menuLeft = menuRightBorder;
mainLeft = mainRightBorder;
}
/**
* @param event 向VelocityTracker添加MotionEvent
* @see android.view.VelocityTracker#obtain()
* @see android.view.VelocityTracker#addMovement(MotionEvent)
*/
private void acquireVelocityTracker(final MotionEvent event) {
if (null == mVelocityTracker) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
}
/**
* 釋放VelocityTracker
*
* @see android.view.VelocityTracker#clear()
* @see android.view.VelocityTracker#recycle()
*/
private void releaseVelocityTracker() {
if (null != mVelocityTracker) {
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
/**
* 由于上面不能使用scrollBy,那么這里就不能使用Scroller這個類來完成平滑移動了,還好我們有動畫
*/
class MyAnimation extends Animation {
private int viewCurrentLfet;
private int viewStartLfet;
private int viewTargetLfet;
private int viewWidth;
private View view;
private int cha;
public MyAnimation(View view, int viewStartLfet, int viewTargetLfet, int viewWidth) {
this.view = view;
this.viewStartLfet = viewStartLfet;
this.viewTargetLfet = viewTargetLfet;
this.viewWidth = viewWidth;
cha = viewTargetLfet - viewStartLfet;
setDuration(Math.abs(cha));
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
viewCurrentLfet = (int) (viewStartLfet + cha * interpolatedTime);
view.layout(viewCurrentLfet, 0, viewCurrentLfet + viewWidth, menuHeight);
}
}
}
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
總結
以上是生活随笔為你收集整理的android中仿qq最新版抽屉,Android 自定义View实现抽屉效果的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 京东白条怎么还款 通过这几种途径
- 下一篇: android网络请求回调管理,Andr