Android自定義View總結(一)基礎知識與例項
阿新 • • 發佈:2019-01-08
自定義View是最能體現一個Android開發者水平的技能之一了。
接下來的一些列部落格將總結一下Android的自定義相關View知識,包括View的結構,事件體系,工作原理,自定義View的繪製等。
參考資料部分來自於書上以及各種部落格。
新建了一個qq群 482543750,歡迎一起學習Android的小夥伴加入。
提供各種Android學習資料,面試資料,Android簡歷模板。
一、概述 Android中,View不屬於四大元件,但它甚至比Receiver和Provider都要重要。 Android提供了許多基礎的控制元件,但遠遠不能滿足我們的需要,很多時候我們根據需求進行新控制元件的定義,這就需要我們對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走到了圖示位置:
public class DragView extends View{ int lastX; int lastY; public DragView(Context context) { super(context); } public DragView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); } @Override public boolean onTouchEvent(MotionEvent event) { int x = (int) event.getX(); int y = (int) event.getY(); Log.e("觸發onTouchEvent",x+"::::::"+y); switch (event.getAction()){ case MotionEvent.ACTION_DOWN:{ lastX = x; lastY = y; } break; case MotionEvent.ACTION_MOVE:{ int offsetX = x - lastX; int offsetY = 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; } return true; } }
佈局:
<RelativeLayout
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">
<com.lian.scrolltest.DragView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#000000"
/>
</RelativeLayout>
MainActivity直接顯示佈局即可 很好地實現瞭如上效果 3、MotionEvent和TouchSlop (1)MotionEvent 我們在自定義View的時候,常常需要在onTouchEvent()中定義觸控行為 典型的觸控事件型別包括: ACTION_DOWN:手指剛剛接觸螢幕 ACTION_MOVE:手指在螢幕上移動 ACTION_UP:手指從螢幕離開 通過MotionEvent物件,我們可以得到點選事件的一系列位置引數 getX(),getY():觸控事件發生的位置相對於View的座標 getRawX(),getRawY()返回返回相對於螢幕左上角的x 和 y 座標 (2)TouchSlop 表示系統能辨識出的認為是滑動的最小距離。 若兩次滑動小於此常量,判定為不屬於滑動操作 這個常量的大小和手機有關。 通過如下方式獲得:
ViewConfiguration.get(getContext()).getScaledTouchSlop();
我們處理滑動事件時,可以用這個引數進行過濾。 4、VelocityTracker、GestureDetector、Scroller (1)VelocityTracker 用於追蹤滑動過程中的速度,包括水平和豎直方向的速度。 使用方法: 在View的onTouchEvent()中將Event託管給VelocityTracker, 採用相應API獲取引數
//獲取物件
VelocityTracker velocityTracker = VelocityTracker.obtain();
//託管event
velocityTracker.addMovement(event);
//設定時間間隔,結果會表示為每1000毫秒經過多少畫素,若設定為100,結果表示為沒100毫秒經過多少畫素
velocityTracker.computeCurrentVelocity(1000);
//獲取X和Y方向上的速度
int xVelicity = (int) velocityTracker.getXVelocity();
int yVelicity = (int) velocityTracker.getYVelocity();
比如1秒內X方向滑動了100畫素,那麼引數設定為1000時,結果就為100,表示1000毫秒劃過100畫素
引數設定為100時,結果就為10(表示每100毫秒劃過10畫素)
不需要使用時,對其進行回收
velocityTracker.clear();
velocityTracker.recycle();
(2)GestureDetector GestureDetector中將封裝了一系列觸控行為,包括單擊、滑動、長按,雙擊等。 使用: 在自定義View中實現onGestureDetector介面,在其中重寫onSingleTapTop()-單擊事件、onFiling()-快速滑動、omLongPress()-長按、onDoubleTap()-雙擊 等方法,定義自己的事件處理邏輯 還有,在onTouchEvent()中:
//獲取物件
GestureDetector gestureDetector = new GestureDetector(this);
//解決長按屏幕後無法拖動的問題
gestureDetector.setIsLongpressEnabled(false);
//託管event
boolean consume = gestureDetector.onTouchEvent(event);
return consume;
(3)Scroller 用於實現View的彈性滑動。 為了讓View實現滑動,我們常常使用scrollTo和ScrollBy,但其過程是瞬間完成的,沒有過度效果,使用者體驗並不好。 使用Scroller和View的computeScroll配合,可以實現有過渡效果的滑動 三、View的滑動 滑動是自定義View使用最多的效果之一, 有三種實現方式: a、View的scrollTo/ScrollBy方法 b、使用動畫為View施加平移效果 c、改變View的LayoutParams是的View重新佈局實現滑動 1、使用scrollTo/ScrollBy 原始碼:
/**
* Set the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the x position to scroll to
* @param y the y position to scroll to
*/
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();
}
}
}
/**
* Move the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the amount of pixels to scroll by horizontally
* @param y the amount of pixels to scroll by vertically
*/
public void scrollBy(int x, int y) {
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畫素
通過如下程式碼:
Button button = (Button) findViewById(R.id.btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dragView.animate().translationX(200).setDuration(1000).start();
}
});
3.改變佈局引數 即改變LayoutParams, 比如我們讓以上自定義View向右平移100畫素 只要將此View的marginLeft引數值增加100px 同樣以上為例,將自定義View的寬度增加100px,向右平移100px
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) dragView.getLayoutParams();
layoutParams.width+=100;
layoutParams.leftMargin+=100;
dragView.requestLayout();
//或者dragView.setLayoutParams(layoutParams);
}
});
點選按鈕發現View向右滑動而且變胖了,但是瞬間滑動過去的,沒有動畫效果