您好,登錄后才能下訂單哦!
怎么在Android中通過自定義View實(shí)現(xiàn)一個(gè)抽屜效果?很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
Android 自定義View實(shí)現(xiàn)抽屜效果
說明
主要算法是:動(dòng)畫當(dāng)前值=起始值+(目標(biāo)值-起始值)*interpolatedTime
其中interpolatedTime是一個(gè)0.0f~1.0f的數(shù)字,系統(tǒng)自己插值計(jì)算好了(默認(rèn)是線性變化的),當(dāng)然你可以自己寫插值器
/** * 由于上面不能使用scrollBy,那么這里就不能使用Scroller這個(gè)類來完成平滑移動(dòng)了,還好我們有動(dòng)畫 */ 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的正確寬度(只能獲取具體的數(shù)字值),但是不能這樣獲取高度,因?yàn)檫@里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.求第一個(gè)觸點(diǎn)的id, 此時(shí)可能有多個(gè)觸點(diǎn),但至少一個(gè) // 獲取索引為0的手指id mPointerId = event.getPointerId(0); downX = (int) event.getX(); lastX = downX; break; case MotionEvent.ACTION_MOVE: // 獲取當(dāng)前手指id所對(duì)應(yīng)的索引,雖然在ACTION_DOWN的時(shí)候,我們默認(rèn)選取索引為0 // 的手指,但當(dāng)有第二個(gè)手指觸摸,并且先前有效的手指up之后,我們會(huì)調(diào)整有效手指 // 屏幕上可能有多個(gè)手指,我們需要保證使用的是同一個(gè)手指的移動(dòng)軌跡, // 因此此處不能使用event.getActionIndex()來獲得索引 final int pointerIndex = event.findPointerIndex(mPointerId); moveX = (int) event.getX(pointerIndex); deltaX = moveX - lastX; // 把觸摸移動(dòng)引起的增量,體現(xiàn)在menu和main的左側(cè)left上 menuLeft = (int) (menuLeft + deltaX * 0.43);//讓菜單移動(dòng)的慢一點(diǎn) mainLeft = mainLeft + deltaX; // 讓菜單根據(jù)手指增量移動(dòng),考慮兩側(cè)邊界問題(通過不停地layout實(shí)現(xiàn)移動(dòng)效果) // 為何不適用scrollBy,因?yàn)閟crollBy移動(dòng)的是外層的大View,現(xiàn)在需求是分別移動(dòng)這個(gè)大view內(nèi)的兩個(gè)小View // scrollBy的話,會(huì)讓菜單和主頁面同時(shí)移動(dòng),不會(huì)產(chǎn)生錯(cuò)位效果, // 你會(huì)想,那讓小view自己scrollBy,這樣也是不行的, // 因?yàn)樽屝iew,例如menu調(diào)用scrollBy的話,會(huì)讓menu自己的邊框在動(dòng), // 看上去,是menu內(nèi)部的文字在移動(dòng),但是menu并沒有在外層的大View里移動(dòng) // 說的很拗口,但是真的不能用scrollBy if (menuLeft >= menuRightBorder) { menuLeft = menuRightBorder; } else if (menuLeft <= menuLeftBorder) { menuLeft = menuLeftBorder; } menuView.layout(menuLeft, 0, menuLeft + menuWidth, menuHeight); // 讓主頁面根據(jù)手指增量移動(dòng),考慮兩側(cè)邊界問題 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.求偽瞬時(shí)速度 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) { // 離開屏幕的正是目前的有效手指,此處需要重新調(diào)整,并且需要重置VelocityTracker int reIndex = pointerIndexLeave == 0 ? 1 : 0; mPointerId = event.getPointerId(reIndex); // 調(diào)整觸摸位置,防止出現(xiàn)跳動(dòng) 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的左側(cè)狀態(tài),這影響到了,再次手指觸摸時(shí)候的動(dòng)畫,否則突變 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的左側(cè)狀態(tài),這影響到了,再次手指觸摸時(shí)候的動(dòng)畫,否則突變 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這個(gè)類來完成平滑移動(dòng)了,還好我們有動(dòng)畫 */ 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); } } }
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。