手機版qq有一個挺不錯的效果是側拉菜單,在主界面向右滑動就會在左側出現關於自己qq的一些信息;
實現側滑效果目前知道有三種:
一是SlidingDrawer,谷歌在Android4.2之後已經不建議使用
二是DrawerLayout,谷歌提供的挺好用的控件,只需在佈局文件中加載即可
三是第三方提供的開源控件,更加的靈活;下面就來簡單介紹一下原理
一、需求
1.定義兩個佈局,菜單和主內容,然後整合在一個自定義的控件中
2.分別測量出菜單和主內容的寬和高
3.主內容全屏顯示在屏幕上,那麼左側的菜單就會看不到,達到了隱藏菜單的效果
4.處理屏幕的onTouchEvent()事件,實現左右滑動控制菜單的隱藏與顯示
二、具體實現
1.定義佈局文件菜單和主內容,然後整合在自定義的SlideMenu控件中
2.在SlideMenu中通過onMeasure測量佈局文件的寬和高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//測量菜單的寬和高,寬爲240dip,高爲父窗體高度
//0代表菜單
View menuView = getChildAt(0);
menuView.measure(menuView.getLayoutParams().width, heightMeasureSpec);
//測量內容正文的寬和高,與父窗體一致
//1代表正文內容
View contentView = getChildAt(1);
contentView.measure(widthMeasureSpec, heightMeasureSpec);
}
在系統底層measure調用了onMeasure(定義了寬高的約束)
onMeasrue調用了setMeasureDimension(真實的寬高信息)
3.通過onLayout方法繪製佈局
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//把測量出菜單和內容繪製屏幕上
View menuView = getChildAt(0);
//設置菜單在屏幕的左側顯示(即隱藏)
menuView.layout(-menuView.getMeasuredWidth(), t, 0, b);
//主內容的繪製
View contentView = getChildAt(1);
contentView.layout(l, t, r, b);
}
4.處理屏幕的onTouchEvent事件實現菜單的隱藏與顯示
@Override
public boolean onTouchEvent(MotionEvent event) {
int scrollX;
switch (event.getAction()) {
//按下
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
break;
//移動
case MotionEvent.ACTION_MOVE:
int moveX = (int) event.getX();
//偏移量
int deltax = downX - moveX;
scrollX = getScrollX() + deltax;
//根據當前位置確定屏幕的顯示
if (scrollX < -getChildAt(0).getMeasuredWidth()) {
scrollTo(-getChildAt(0).getMeasuredWidth(), 0);
} else if (scrollX > 0) {
scrollTo(0, 0);
} else {
scrollBy(deltax, 0);
}
downX = moveX;
break;
//擡起
case MotionEvent.ACTION_UP:
int center = -getChildAt(0).getMeasuredWidth() / 2;
scrollX = getScrollX();
if (scrollX < center) {
currentState = SCREEN_CONTENT;
} else {
currentState = SCREEN_MAIN;
}
switchScreen();
break;
}
return true;
}
/**
* 根據currentState的狀態來更改屏幕的顯示
*/
private void switchScreen() {
int startX = getScrollX(); //當前屏幕的X位置
int dX = 0; //增量=目的位置-當前位置
switch (currentState) {
case SCREEN_CONTENT:
// scrollTo(-getChildAt(0).getMeasuredWidth(),0);
dX = -getChildAt(0).getMeasuredWidth() - startX;
break;
case SCREEN_MAIN:
// scrollTo(0,0);
dX = 0 - startX;
break;
}
int duration = Math.abs(dX) * 10;
if (duration > 1000) {
duration = 1000;
}
scroller.startScroll(startX, 0, dX, 0, duration);
invalidate();
}
@Override
public void computeScroll() {
//當不在模擬數據時候跳出遞歸
if (scroller.computeScrollOffset()) {
//獲取當前模擬的數據
int currX = scroller.getCurrX();
//更新位置
scrollTo(currX, 0);
invalidate();
}
}
滑動的分析:
1.當點擊屏幕時記錄下當前的位置downX,在移動的過程中根據當前位置moveX和downX計算偏移量
根據偏移量的值不斷的修正菜單和主內容在屏幕的顯示位置;
2.當擡起時,如果菜單顯示出來的內容是否大於二分之一,那麼滑動顯示菜單,反之顯示主內容
3.currentState用來記錄當前屏幕顯示的是菜單還是主內容,並且是屏幕固定時偏移量計算的依據
4.scroller.startScroll(startX,0,dX,0,duration)
用來模擬X軸從startX到偏移dX經過duration時間的過程,但是這個方法只是模擬,並不會改變
屏幕的顯示,還需要通過invalidate()來修改屏幕的顯示
5.invalidate()在ViewGroup中通過調用DrawChild()->再調view.draw->computeScroll修改屏幕
顯示的內容,由於startScroll的數據是緩慢變化的,因此需要用遞歸不斷的更新當前的顯示,就
實現了鬆手後菜單緩慢移動的效果
三、回調簡介
Android回調方法用的特別多,其原理類似於設計模式中的觀察者模式,回調通過自身提供一個內部
接口,在接口定義統一的方法,那麼不管是誰調用我,只要實現我接口中定義的方法,使代碼有了很
好的封裝性
四、個人總結
寫自定義控件一定要有的步驟:
1.分析控件的實現都有什麼狀態
2.找出狀態之間轉換的臨界條件
3.實現即可