View基礎知識
View的位置參數:
top、left、right、bottom,分別對應View的左上角和右下角相對於父容器的橫縱座標值。從Android 3.0開始,view增加了x、y、translationX、translationY四個參數,這幾個參數也是相對於父容器的座標。x和y是左上角的座標,而translationX和translationY是view左上角相對於父容器的偏移量,默認值都是0。
這幾個參數的換算公式:
View在平移過程中,top和left表示原始左上角的位置信息,其值並不會發生改變
MotionEvent和TouchSlop
MotionEvent是指手指接觸屏幕後所產生的一系列事件,主要有ACTION_UP、ACTION_DOWN、ACTION_MOVE等。
正常情況下,一次手指觸屏會觸發一系列點擊事件,主要有下面兩種典型情況:
- 點擊屏幕後離開,事件序列是ACTION_DOWN->ACTION_UP;
- 點擊屏幕後滑動一會再離開,事件序列是ACTION_DOWN->ACTION_MOVE->ACTION_MOVE-> … ->ACTION_UP;
TouchSlope是系統所能識別出的可以被認爲是滑動的最小距離,獲取方式是
ViewConfiguration.get(getContext()).getScaledTouchSlope()。
VelocityTracker
VelocityTracker用於追蹤手指在滑動過程中的速度,包括水平和垂直方向上的速度。速度可能爲負值,例如當手指從屏幕右邊往左邊滑動的時候。此外,速度是單位時間內移動的像素數,單位時間不一定是1秒鐘,可以使用方法computeCurrentVelocity(xxx)指定單位時間是多少,單位是ms。例如通過computeCurrentVelocity(1000)來獲取速度,手指在1s中滑動了100個像素,那麼速度是100,即100(像素/1000ms)。如果computeCurrentVelocity(100)來獲取速度,在100ms內手指只是滑動了10個像素,那麼速度是10,即10(像素/100ms)。
使用步驟:
1.獲取VelocityTracker實例:
VelocityTracker mVelocityTracker = VelocityTracker.obtain();
2.將MotionEvent事件納入到VelocityTracker中:
mVelocityTracker.addMovement(event);
3.計算瞬時速度:
mVelocityTracker.computeCurrentVelocity(units,maxVelocity);
4.獲取xy方向上的速度:
float vX = mVelocityTracker.getXVelocity(pointId);
float vY = mVelocityTracker.getYVelocity(pointId);
5.回收VelocityTracker:
mVelocityTracker.clear();
mVelocityTracker.recycle();
6.使用示例:
/**
* 獲取速度追蹤器
* @return
*/
private VelocityTracker getVelocityTracker()
{
if(mVelocityTracker == null)
{
mVelocityTracker = VelocityTracker.obtain();
}
return mVelocityTracker;
}
/**
* 回收速度追蹤器
*/
private void recycleVelocityTracker()
{
if(mVelocityTracker != null)
{
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
//1.獲取速度追蹤器
getVelocityTracker();
//2.將當前事件納入到追蹤器中
mVelocityTracker.addMovement(event);
int pointId = -1;
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.i(TAG,"ACTION_DOWN");
pointId = event.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG,"ACTION_MOVE");
//3.計算當前速度
mVelocityTracker.computeCurrentVelocity(1000,mMaxFlingVelocity);
//獲取x y方向上的速度
float vX = mVelocityTracker.getXVelocity(pointId);
float vY = mVelocityTracker.getYVelocity(pointId);
Log.i(TAG,"vX = "+vX+",vY = "+vY);
break;
case MotionEvent.ACTION_UP:
Log.i(TAG,"ACTION_UP");
//4.回收速度追蹤器
recycleVelocityTracker();
break;
default:
break;
}
return super.onTouchEvent(event);
}
GestureDetector
手勢檢測,用於輔助檢測用戶的單擊、滑動、長按、雙擊等行爲。
- 首先需要實現GestureListener接口
mGestureDetector = new GestureDetector(this, new GestureScrollListener());
- 在TouchEvent方法中接管手勢
mGestureDetector.onTouchEvent(ev)
View的滑動
常見的實現view的滑動的方式有三種:
- 1.通過view本身提供的scrollTo和scrollBy方法:操作簡單,適合對view內容的滑動;
- 2.通過動畫給view施加平移效果來實現滑動:操作簡單,適用於沒有交互的view和實現複雜的動畫效果;
- 3.通過改變view的LayoutParams使得view重新佈局從而實現滑動:操作稍微複雜,適用於有交互的view
scrollTo和scrollBy
- scrollTo(int x, int y) 是將View中內容滑動到相應的位置
- scrollBy(int x, int y)其實是對scrollTo的包裝,移動的是相對的位置
scrollTo和scrollBy源代碼:
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
使用動畫
- 使用動畫來移動view主要是操作view的translationX和translationY屬性,既可以使用傳統的view動畫,也可以使用屬性動畫。
- 使用動畫還存在一個交互問題:在android3.0以前的系統上,view動畫和屬性動畫,新位置均無法觸發點擊事件,同時,老位置仍然可以觸發單擊事件。從3.0開始,屬性動畫的單擊事件觸發位置爲移動後的位置,view動畫仍然在原位置。
- 動畫兼容庫nineoldandroids中的ViewHelper類提供了很多的get/set方法來爲屬性動畫服務,例如setTranslationX和setTranslationY方法,這些方法是沒有版本要求的。
ViewHelper.getTranslationX(view);
ViewHelper.setTranslationY(view,100);
ViewHelper.setTranslationX(view,100);
改變佈局參數
通過params來設置margin改變位置
Scroller
- Scroller:彈性滑動對象
- v4中ScrollerCompat類兼容庫
- 需要computeScroll配合更新位置
執行滑動:
mScroller.startScroll( startX, startY,dx, dy, duration);
控制滑動這個流程:
@Override
public void computeScroll() {
super.computeScroll();
// 判斷Scroller是否執行完畢
if (mScroller.computeScrollOffset()) {
((View) getParent()).scrollTo( mScroller.getCurrX(), mScroller.getCurrY());
// 通過重繪來不斷調用computeScroll
invalidate();//很重要
}
}
computeScroll:主要功能是計算拖動的位移量、更新背景、設置要顯示的屏幕(setCurrentScreen(mCurrentScreen);)。
重寫computeScroll()的原因:
調用startScroll()是不會有滾動效果的,只有在computeScroll()獲取滾動情況,做出滾動的響應
computeScroll在父控件執行drawChild時,會調用這個方法