1、View是什麼
View是Android所有控件的基類,簡單到TextView、Button,復雜到RelativeLayout,LinearLayout,其共同基類都是View。
所以,View可以理解為控件的抽象,也是一個控件。
除此之外,還有ViewGroup,字面意義上,它表示控件組,內部可以包含許多個控件。
ViewGroup也繼承自View,這意味著,一個View的可以是單個控件,也可以是多個控件組成的一組控件,這就形成了View樹。
下面這個圖很好地體現了View的繼承關系
2、View的相關參數
View的位置決定於它的四個頂點,對應View的四個屬性:
Top:左上角縱坐標,通過getTop ()獲得
Left:左上角橫坐標,通過getLeft()獲得
Right: 右下角橫坐標,通過getRight ()獲得
Bottom: 右下角縱坐標,通過getBottom ()獲得
這些坐標都是相對於View的父容器所說的,是一種相對坐標。
下面這張圖表示的是View中涉及位置參數的各個方法對應的具體含義。
最外層是手機屏幕,中間是一個ViewGroup嵌套一個View。
涉及到的其他方法請繼續往下看。
此外,參數x,y表示View左上角的橫縱坐標,
translationX和translationY表示View的左上角相對於父容器的偏移量。
他們都有相應的Get/Set方法
這幾個參數也是相對於父容器的坐標
可以知道,這幾個參數換算關系如下
x = left +translationX
y = top +translationY
利用這些參數,我們來自定義一個能隨手指滑動而改變位置的View
實現如下效果:
初始位置:
手指滑動後,自定義View走到了圖示位置:
代碼如下:
自定義View
[java]view plaincopy
- publicclassDragViewextendsView{
-
- intlastX;
- intlastY;
-
- publicDragView(Contextcontext){
- super(context);
- }
-
- publicDragView(Contextcontext,AttributeSetattrs){
- super(context,attrs);
- }
-
- @Override
- protectedvoidonDraw(Canvascanvas){
- super.onDraw(canvas);
- }
-
- @Override
- publicbooleanonTouchEvent(MotionEventevent){
-
- intx=(int)event.getX();
- inty=(int)event.getY();
- Log.e("觸發onTouchEvent",x+"::::::"+y);
-
- switch(event.getAction()){
- caseMotionEvent.ACTION_DOWN:{
- lastX=x;
- lastY=y;
- }
- break;
- caseMotionEvent.ACTION_MOVE:{
- intoffsetX=x-lastX;
- intoffsetY=y-lastY;
- Log.e("觸發ACTION_MOVE",offsetX+"::::::"+offsetY);
- layout(getLeft()+offsetX,getTop()+offsetY,getRight()+offsetX,getBottom()+offsetY);
- Log.d("DragView",getLeft()+"______"+getTop()+"-------"+getBottom()+"-------"+getRight());
- }
- break;
- }
-
- returntrue;
- }
- }
布局:
[html]view plaincopy
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context="com.lian.scrolltest.MainActivity">
-
- android:layout_width="100dp"
- android:layout_height="100dp"
- android:background="#000000"
-
- />
-
MainActivity直接顯示布局即可
很好地實現了如上效果
3、MotionEvent和TouchSlop
(1)MotionEvent
我們在自定義View的時候,常常需要在onTouchEvent()中定義觸摸行為
典型的觸摸事件類型包括:
ACTION_DOWN:手指剛剛接觸屏幕
ACTION_MOVE:手指在屏幕上移動
ACTION_UP:手指從屏幕離開
通過MotionEvent對象,我們可以得到點擊事件的一系列位置參數
getX(),getY():觸摸事件發生的位置相對於View的坐標
getRawX(),getRawY()返回返回相對於屏幕左上角的x 和 y 坐標
(2)TouchSlop
表示系統能辨識出的認為是滑動的最小距離。
若兩次滑動小於此常量,判定為不屬於滑動操作
這個常量的大小和手機有關。
通過如下方式獲得:
[java]view plaincopy
- ViewConfiguration.get(getContext()).getScaledTouchSlop();
我們處理滑動事件時,可以用這個參數進行過濾。
4、VelocityTracker、GestureDetector、Scroller
(1)VelocityTracker
用於追蹤滑動過程中的速度,包括水平和豎直方向的速度。
使用方法:
在View的onTouchEvent()中將Event托管給VelocityTracker,
采用相應API獲取參數
[java]view plaincopy
- //獲取對象
- VelocityTrackervelocityTracker=VelocityTracker.obtain();
- //托管event
- velocityTracker.addMovement(event);
- //設置時間間隔,結果會表示為每1000毫秒經過多少像素,若設置為100,結果表示為沒100毫秒經過多少像素
- velocityTracker.computeCurrentVelocity(1000);
- //獲取X和Y方向上的速度
- intxVelicity=(int)velocityTracker.getXVelocity();
- intyVelicity=(int)velocityTracker.getYVelocity();
比如1秒內X方向滑動了100像素,那麼參數設置為1000時,結果就為100,表示1000毫秒劃過100像素
參數設置為100時,結果就為10(表示每100毫秒劃過10像素)
不需要使用時,對其進行回收
[java]view plaincopy
- velocityTracker.clear();
- velocityTracker.recycle();
(2)GestureDetector
GestureDetector中將封裝了一系列觸摸行為,包括單擊、滑動、長按,雙擊等。
使用:
在自定義View中實現onGestureDetector接口,在其中重寫onSingleTapTop()-單擊事件、onFiling()-快速滑動、omLongPress()-長按、onDoubleTap()-雙擊
等方法,定義自己的事件處理邏輯
還有,在onTouchEvent()中:
[java]view plaincopy
- //獲取對象
- GestureDetectorgestureDetector=newGestureDetector(this);
- //解決長按屏幕後無法拖動的問題
- gestureDetector.setIsLongpressEnabled(false);
- //托管event
- booleanconsume=gestureDetector.onTouchEvent(event);
- returnconsume;
(3)Scroller
用於實現View的彈性滑動。
為了讓View實現滑動,我們常常使用scrollTo和ScrollBy,但其過程是瞬間完成的,沒有過度效果,用戶體驗並不好。
使用Scroller和View的computeScroll配合,可以實現有過渡效果的滑動
三、View的滑動
滑動是自定義View使用最多的效果之一,
有三種實現方式:
a、View的scrollTo/ScrollBy方法
b、使用動畫為View施加平移效果
c、改變View的LayoutParams是的View重新布局實現滑動
1、使用scrollTo/ScrollBy
源碼:
[java]view plaincopy
- /**
- *Setthescrolledpositionofyourview.Thiswillcauseacallto
- *{@link#onScrollChanged(int,int,int,int)}andtheviewwillbe
- *invalidated.
- *@paramxthexpositiontoscrollto
- *@paramytheypositiontoscrollto
- */
- publicvoidscrollTo(intx,inty){
- if(mScrollX!=x||mScrollY!=y){
- intoldX=mScrollX;
- intoldY=mScrollY;
- mScrollX=x;
- mScrollY=y;
- invalidateParentCaches();
- onScrollChanged(mScrollX,mScrollY,oldX,oldY);
- if(!awakenScrollBars()){
- postInvalidateOnAnimation();
- }
- }
- }
-
- /**
- *Movethescrolledpositionofyourview.Thiswillcauseacallto
- *{@link#onScrollChanged(int,int,int,int)}andtheviewwillbe
- *invalidated.
- *@paramxtheamountofpixelstoscrollbyhorizontally
- *@paramytheamountofpixelstoscrollbyvertically
- */
- publicvoidscrollBy(intx,inty){
- scrollTo(mScrollX+x,mScrollY+y);
- }
scrollBy實際上也調用scrollTo方法,實現基於當前位置的滑動,scrollTo實現基於所傳遞參數的絕對滑動
mScrollX和mScrollY可以動過get方法得到。
mScrollX的值總是等於View的左邊緣到View的內容左邊緣水平方向的距離,
mScrollY的值總是等於View的上邊緣和View的內容上邊緣豎直方向的距離。
需要注意的是,scrollBy和scrollTo
只能改變View的內容的位置而不能改變View在布局中的位置
在View的內容位置改變是,mScrollX和mScrollY值可正可負
2、使用動畫
通過動畫可以讓一個View進行平移,主要是操作View的translationX和translationY屬性,可以用View動畫,也可以用屬性動畫。
以屬性動畫為例,
我們在上面例子的基礎上添加一個按鈕
點擊按鈕,View在1秒鐘的時間內向右平移200像素
通過如下代碼:
[java]view plaincopy
- Buttonbutton=(Button)findViewById(R.id.btn);
- button.setOnClickListener(newView.OnClickListener(){
- @Override
- publicvoidonClick(Viewv){
- dragView.animate().translationX(200).setDuration(1000).start();
- }
- });
3.改變布局參數
即改變LayoutParams,
比如我們讓以上自定義View向右平移100像素
只要將此View的marginLeft參數值增加100px
同樣以上為例,將自定義View的寬度增加100px,向右平移100px
[java]view plaincopy
- button.setOnClickListener(newView.OnClickListener(){
- @Override
- publicvoidonClick(Viewv){
- RelativeLayout.LayoutParamslayoutParams=(RelativeLayout.LayoutParams)dragView.getLayoutParams();
- layoutParams.width+=100;
- layoutParams.leftMargin+=100;
- dragView.requestLayout();
- //或者dragView.setLayoutParams(layoutParams);
- }
- });
點擊按鈕發現View向右滑動而且變胖了,但是瞬間滑動過去的,沒有動畫效果