Android 自定義View實(shí)現(xiàn)抽屜效果
Android 自定義View實(shí)現(xiàn)抽屜效果
說(shuō)明
- 這個(gè)自定義View,沒(méi)有處理好多點(diǎn)觸摸問(wèn)題
- View跟著手指移動(dòng),沒(méi)有采用傳統(tǒng)的scrollBy方法,而是通過(guò)不停地重新布局子View的方式,來(lái)使得子View產(chǎn)生滾動(dòng)效果menuView.layout(menuLeft, 0, menuLeft + menuWidth, menuHeight);
- 相應(yīng)的,由于沒(méi)有使用scrollBy方法,就沒(méi)有產(chǎn)生getScrollX值,所以不能通過(guò)Scroller的startScroll方法來(lái)完成手指離開(kāi)后的平滑滾動(dòng)效果,而是使用了Animation動(dòng)畫(huà)的applyTransformation方法來(lái)完成插值,從而實(shí)現(xiàn)動(dòng)畫(huà)效果
主要算法是:動(dòng)畫(huà)當(dāng)前值=起始值+(目標(biāo)值-起始值)*interpolatedTime
其中interpolatedTime是一個(gè)0.0f~1.0f的數(shù)字,系統(tǒng)自己插值計(jì)算好了(默認(rèn)是線性變化的),當(dāng)然你可以自己寫(xiě)插值器
/**
* 由于上面不能使用scrollBy,那么這里就不能使用Scroller這個(gè)類來(lái)完成平滑移動(dòng)了,還好我們有動(dòng)畫(huà)
*/
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()來(lái)獲得索引
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è)邊界問(wèn)題(通過(guò)不停地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ì)讓菜單和主頁(yè)面同時(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并沒(méi)有在外層的大View里移動(dòng)
// 說(shuō)的很拗口,但是真的不能用scrollBy
if (menuLeft >= menuRightBorder) {
menuLeft = menuRightBorder;
} else if (menuLeft <= menuLeftBorder) {
menuLeft = menuLeftBorder;
}
menuView.layout(menuLeft, 0, menuLeft + menuWidth, menuHeight);
// 讓主頁(yè)面根據(jù)手指增量移動(dòng),考慮兩側(cè)邊界問(wèn)題
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的菜單寬度就打開(kāi)菜單,否則打開(kāi)主頁(yè)面
if (mainLeft > menuWidth / 2.5) {
Log.e("qqq", "顯示菜單");
smoothToMenu();
} else {
Log.e("qqq", "顯示主頁(yè)面");
smoothToMain();
}
}
// 4.ACTION_UP釋放VelocityTracker,交給其他控件使用
releaseVelocityTracker();
break;
case MotionEvent.ACTION_CANCEL:
// 4.ACTION_UP釋放VelocityTracker,交給其他控件使用
releaseVelocityTracker();
case MotionEvent.ACTION_POINTER_UP:
// 獲取離開(kāi)屏幕的手指的索引
int pointerIndexLeave = event.getActionIndex();
int pointerIdLeave = event.getPointerId(pointerIndexLeave);
if (mPointerId == pointerIdLeave) {
// 離開(kāi)屏幕的正是目前的有效手指,此處需要重新調(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)畫(huà),否則突變
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)畫(huà),否則突變
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è)類來(lái)完成平滑移動(dòng)了,還好我們有動(dòng)畫(huà)
*/
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);
}
}
}
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
- Android開(kāi)發(fā)之DrawerLayout實(shí)現(xiàn)抽屜效果
- Android編程實(shí)現(xiàn)抽屜效果的方法詳解
- Android自定義控件仿QQ抽屜效果
- Android DrawerLayout實(shí)現(xiàn)抽屜效果實(shí)例代碼
- Android 抽屜效果的導(dǎo)航菜單實(shí)現(xiàn)代碼實(shí)例
- Android實(shí)現(xiàn)自定義滑動(dòng)式抽屜菜單效果
- Android App中DrawerLayout抽屜效果的菜單編寫(xiě)實(shí)例
- Android SlidingDrawer 抽屜效果的實(shí)現(xiàn)
- Android的Activity跳轉(zhuǎn)動(dòng)畫(huà)各種效果整理
- Android Tween動(dòng)畫(huà)之RotateAnimation實(shí)現(xiàn)圖片不停旋轉(zhuǎn)效果實(shí)例介紹
- Android實(shí)現(xiàn)圖片輪播效果的兩種方法
- Android編程實(shí)現(xiàn)抽屜效果的方法示例
相關(guān)文章
android將搜索引擎設(shè)置為中國(guó)雅虎無(wú)法搜索問(wèn)題解決方法
android 進(jìn)入搜索,將搜索引擎設(shè)置為中國(guó)雅虎,無(wú)法搜索到相關(guān)網(wǎng)絡(luò)結(jié)果,該問(wèn)題是由于yahoo的搜索接口改變導(dǎo)致,具體解決方法如下,感興趣的朋友可以參考下哈2013-06-06
Android開(kāi)發(fā)壁紙的驗(yàn)證設(shè)置和確認(rèn)功能實(shí)現(xiàn)demo
android?wallpaper包括鎖屏壁紙和桌面壁紙,壁紙又區(qū)分靜態(tài)和動(dòng)態(tài)兩種。本文詳細(xì)介紹靜態(tài)壁紙?jiān)O(shè)置和確認(rèn),有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-04-04
android長(zhǎng)截屏原理及實(shí)現(xiàn)代碼
本篇文章主要介紹了android長(zhǎng)截屏原理及實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08
Android利用方向傳感器獲得手機(jī)的相對(duì)角度實(shí)例說(shuō)明
下面以實(shí)例向大家介紹喜愛(ài)Android利用方向傳感器獲得手機(jī)的相對(duì)角度,不了解的朋友可以參考下2013-06-06
android實(shí)現(xiàn)程序自動(dòng)升級(jí)到安裝示例分享(下載android程序安裝包)
這篇文章主要介紹了android實(shí)現(xiàn)下載android程序安裝包自動(dòng)升級(jí)的示例,大家參考使用吧2014-01-01
Android實(shí)現(xiàn)屏幕手寫(xiě)簽名
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)屏幕手寫(xiě)簽名,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07
Android實(shí)現(xiàn)手勢(shì)密碼功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)手勢(shì)密碼功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android原生TabLayout使用的超全解析(看這篇就夠了)
現(xiàn)在很多app都有頂部可左右切換的導(dǎo)航欄,并且還帶動(dòng)畫(huà)效果,要實(shí)現(xiàn)這種導(dǎo)航欄,可以使用Android原生的Tablayout也可以借助第三方框架實(shí)現(xiàn),這篇文章主要給大家介紹了關(guān)于Android原生TabLayout使用的相關(guān)資料,需要的朋友可以參考下2022-09-09

