其實一直沒想好自定義控件這塊怎么寫,因為涉及面太廣,而且我自定義控件能力其實很一般帽驯。先寫著同眯,再優(yōu)化吧。
網(wǎng)上也有很多大藕万剑總結(jié)的都挺好的,我也是看著他們的博客學(xué)習自己總結(jié)下,當做日記
基礎(chǔ)入門
android坐標系是左上角為原點呻澜,往右邊X增加,往下Y增加惨险。自定義View可以分為繼承自View和ViewGroup羹幸。
event.getX():表示的是觸摸的點距離自身左邊界的距離
event.getY():表示的是觸摸的點距離自身上邊界的距離
event.getRawX:表示的是觸摸點距離屏幕左邊界的距離
event.getRawY:表示的是觸摸點距離屏幕上邊界的距離
View.getWidth():表示的是當前控件的寬度,即getRight()-getLeft()
View.getHeight():表示的是當前控件的高度辫愉,即getBottom()-getTop()
View.getTop():子View的頂部到父View頂部的距離
View.getRight():子View的右邊界到父View的左邊界的距離
View.getBottom():子View的底部到父View的頂部的距離
View.getLeft():子View的左邊界到父View的左邊界的距離
View.getTranslationX()計算的是該View在X軸的偏移量栅受。初始值為0,向左偏移值為負恭朗,向右偏移值為正屏镊。
View.getTranslationY()計算的是該View在Y軸的偏移量。初始值為0痰腮,向上偏移為負而芥,向下偏移為證。
MotionEvent
MotionEvent是大家很熟悉
https://blog.csdn.net/huaxun66/article/details/52352469
動作常量:
MotionEvent.ACTION_DOWN:當屏幕檢測到第一個觸點按下之后就會觸發(fā)到這個事件膀值。
MotionEvent.ACTION_MOVE:當觸點在屏幕上移動時觸發(fā)棍丐,觸點在屏幕上停留也是會觸發(fā)的,主要是由于它的靈敏度很高沧踏,而我們的手指又不可能完全靜止(即使我們感覺不到移動歌逢,但其實我們的手指也在不停地抖動)。
MotionEvent.ACTION_POINTER_DOWN:當屏幕上已經(jīng)有觸點處于按下的狀態(tài)的時候悦冀,再有新的觸點被按下時觸發(fā)趋翻。
MotionEvent.ACTION_POINTER_UP:當屏幕上有多個點被按住,松開其中一個點時觸發(fā)(即非最后一個點被放開時)觸發(fā)。
MotionEvent.ACTION_UP:當觸點松開時被觸發(fā)踏烙。
MotionEvent.ACTION_OUTSIDE: 表示用戶觸碰超出了正常的UI邊界.
MotionEvent.ACTION_SCROLL:android3.1引入师骗,非觸摸滾動,主要是由鼠標讨惩、滾輪辟癌、軌跡球觸發(fā)。
MotionEvent.ACTION_CANCEL:不是由用戶直接觸發(fā)荐捻,由系統(tǒng)在需要的時候觸發(fā)黍少,例如當父view通過使函數(shù)onInterceptTouchEvent()返回true,從子view拿回處理事件的控制權(quán)時,就會給子view發(fā)一個ACTION_CANCEL事件处面,子view就再也不會收到后續(xù)事件了厂置。
方法:
getAction():返回動作類型
getX()/getY():獲得事件發(fā)生時,觸摸的中間區(qū)域的X/Y坐標,由這兩個函數(shù)獲得的X/Y值是相對坐標魂角,相對于消費這個事件的視圖的左上角的坐標昵济。
getRawX()/getRawY():由這兩個函數(shù)獲得的X/Y值是絕對坐標,是相對于屏幕的野揪。
getSize():指壓范圍
getPressure(): 壓力值访忿,0-1之間,看情況斯稳,很可能始終返回1海铆,具體的級別由驅(qū)動和物理硬件決定的
getEdgeFlags():當事件類型是ActionDown時可以通過此方法獲得邊緣標記(EDGE_LEFT,EDGE_TOP,EDGE_RIGHT,EDGE_BOTTOM),但是看設(shè)備情況,很可能始終返回0
getDownTime() :按下開始時間
getEventTime() : 事件結(jié)束時間
getActionMasked():多點觸控獲取經(jīng)過掩碼后的動作類型
getActionIndex():多點觸控獲取經(jīng)過掩碼和平移后的索引
getPointerCount():獲取觸控點的數(shù)量挣惰,比如2則可能是兩個手指同時按壓屏幕
getPointerId(nID):對于每個觸控的點的細節(jié)卧斟,我們可以通過一個循環(huán)執(zhí)行g(shù)etPointerId方法獲取索引
getX(nID):獲取第nID個觸控點的x位置
getY(nID):獲取第nID個觸控點的y位置
getPressure(nID):獲取第nID個觸控點的壓力
TouchSlop
ouchSlop是一個常量,保存的是系統(tǒng)所能識別出的最小滑動距離憎茂。這個值在不同的設(shè)備上有可能是不同的唆涝,我們通常使用它來進行一些滑動過濾,比如說判斷當前滑動的距離如果少于這個值唇辨,那么就可以判斷不是滑動廊酣,不進行滑動后的處理,增強用戶體驗赏枚。
ViewConfiguration.get(this).getScaledTouchSlop();
VelocityTracker
VelocityTracker亡驰,速度追蹤,通過這個類我們可以獲取手指在滑動時的速度饿幅,其中包括水平和垂直方向的速度凡辱。
獲取第一步,在View的onTouch()方法中添加追蹤:
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
第二步栗恩,獲取當前滑動速度:
velocityTracker.computeCurrentVelocity(1000);
float xVelocity = velocityTracker.getXVelocity();
float yVelocity = velocityTracker.getYVelocity();
需要注意的是透乾,在獲取之前,必須先要調(diào)用computeCurrentVelocity(int units)方法,其中units參數(shù)代表的是毫秒時間段乳乌。比如說上方寫的1000就代表1秒內(nèi)手指滑動的像素數(shù)捧韵,如果1秒內(nèi)滑動了100像素數(shù),那么值就為100汉操,當然這個值也有可能是負數(shù)再来,如果是在水平方向從右往左滑動,值則會負數(shù)磷瘤,垂直時芒篷,從下往上滑動,也為負數(shù)采缚≌肼總結(jié)下來可以用一個公式來表示:
速度 = (終點位置 - 起點位置) / 時間段
當我們不需要使用VelocityTracker的時候,可以通過如下方法重置并回收內(nèi)存:
velocityTracker.clear();
velocityTracker.recycle();
GestureDetector手勢識別器
gestureDetector = new GestureDetector(MainActivity.this, new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent motionEvent) {
return false;
}
@Override
public void onShowPress(MotionEvent motionEvent) {
}
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
return false;
}
@Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
return false;
}
@Override
public void onLongPress(MotionEvent motionEvent) {
}
@Override
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
return false;
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean b = gestureDetector.onTouchEvent(event);
return b;
}
OnDown(MotionEvent e):用戶按下屏幕就會觸發(fā)扳抽;
onShowPress(MotionEvent e):如果是按下的時間超過瞬間糊识,而且在按下的時候沒有松開或者是拖動的,那么onShowPress就會執(zhí)行摔蓝,具體這個瞬間是多久,我也不清楚呃……
onLongPress(MotionEvent e):長按觸摸屏愉耙,超過一定時長贮尉,就會觸發(fā)這個事件
觸發(fā)順序:
onDown->onShowPress->onLongPress
onSingleTapUp(MotionEvent e):從名子也可以看出,一次單獨的輕擊抬起操作,也就是輕擊一下屏幕,立刻抬起來朴沿,才會有這個觸發(fā)猜谚,當然,如果除了Down以外還有其它操作,那就不再算是Single操作了,所以也就不會觸發(fā)這個事件
觸發(fā)順序:
點擊一下非常快的(不滑動)Touchup:
onDown->onSingleTapUp->onSingleTapConfirmed
點擊一下稍微慢點的(不滑動)Touchup:
onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed
onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) :滑屏赌渣,用戶按下觸摸屏魏铅、快速移動后松開,由1個MotionEvent ACTION_DOWN, 多個ACTION_MOVE, 1個ACTION_UP觸發(fā)
參數(shù)解釋:
e1:第1個ACTION_DOWN MotionEvent
e2:最后一個ACTION_MOVE MotionEvent
velocityX:X軸上的移動速度坚芜,像素/秒
velocityY:Y軸上的移動速度览芳,像素/秒
onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY):在屏幕上拖動事件。無論是用手拖動view鸿竖,或者是以拋的動作滾動沧竟,都會多次觸發(fā),這個方法 在ACTION_MOVE動作發(fā)生時就會觸發(fā)
滑屏:手指觸動屏幕后,稍微滑動后立即松開
onDown-----》onScroll----》onScroll----》onScroll----》………----->onFling
拖動
onDown------》onScroll----》onScroll------》onFiling
distanceX的值等于e1的X值減去e2的X值,計算結(jié)果帶正負號缚忧。
distanceY的值等于e1的Y值減去e2的Y值,計算結(jié)果帶正負號悟泵。
View移動
scrollBy()和scrollTo()
scrollBy()其實內(nèi)部調(diào)用的就是scrollTo();
scrollTo()是直接挪動到x,y闪水。
scrollBy()是在我當前位子posx,posy的基礎(chǔ)上挪動x,y也就是挪到posx+x,posy+y的位子
這種挪動知識內(nèi)容位置挪動了糕非,其實view還是在那里。
//設(shè)置寬度和大小
<TextView
android:id="@+id/text"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#FF6A6A"
android:gravity="center"
android:text="挪動測試" />
//每次挪動50
text = (TextView) findViewById(R.id.text);
text.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
text.scrollBy(50,50);
}
});
通過這個測試我們發(fā)現(xiàn),雖然字體沒了朽肥,但是textview其實還是在原地的禁筏,可以點擊的,主要我們背景色在那里顯示的很明白啊鞠呈,哈哈融师。
Scroller
Scroller大家應(yīng)該也很常見,比如下面鏈接的完全解析啥的
https://blog.csdn.net/zhangkaiyazky/article/details/79529813
Scroller個人認為最大的用處就在于可以實現(xiàn)很多特效蚁吝,比如平滑滑動旱爆,加速等
常用方法
mScroller.getCurrX() //獲取mScroller當前水平滾動的位置
mScroller.getCurrY() //獲取mScroller當前豎直滾動的位置
mScroller.getFinalX() //獲取mScroller最終停止的水平位置
mScroller.getFinalY() //獲取mScroller最終停止的豎直位置
mScroller.setFinalX(int newX) //設(shè)置mScroller最終停留的水平位置,沒有動畫效果窘茁,直接跳到目標位置
mScroller.setFinalY(int newY) //設(shè)置mScroller最終停留的豎直位置怀伦,沒有動畫效果,直接跳到目標位置
//滾動山林,startX, startY為開始滾動的位置房待,dx,dy為滾動的偏移量, duration為完成滾動的時間
mScroller.startScroll(int startX, int startY, int dx, int dy) //使用默認完成時間250ms
mScroller.startScroll(int startX, int startY, int dx, int dy, int duration)
mScroller.computeScrollOffset() //返回值為boolean,true說明滾動尚未完成驼抹,false說明滾動已經(jīng)完成桑孩。這是一個很重要的方法,通常放在View.computeScroll()中框冀,用來判斷是否滾動是否結(jié)束流椒。
動畫移動
//補間動畫--位移,只是看著位移了明也,其實還在原地
Button mButton = (Button) findViewById(R.id.Button);
// 步驟1:創(chuàng)建 需要設(shè)置動畫的 視圖View
Animation translateAnimation = new TranslateAnimation(0宣虾,500,0温数,500);
// 步驟2:創(chuàng)建平移動畫的對象:平移動畫對應(yīng)的Animation子類為TranslateAnimation
// 參數(shù)分別是:
// 1. fromXDelta :視圖在水平方向x 移動的起始值
// 2. toXDelta :視圖在水平方向x 移動的結(jié)束值
// 3. fromYDelta :視圖在豎直方向y 移動的起始值
// 4. toYDelta:視圖在豎直方向y 移動的結(jié)束值
translateAnimation.setDuration(3000);
// 固定屬性的設(shè)置都是在其屬性前加“set”绣硝,如setDuration()
mButton.startAnimation(translateAnimation);
// 步驟3:播放動畫
//屬性動畫--位移,這個是真的位移了
TextView tvTest = (TextView) findViewById(R.id.tv_test);
float curTranslationY = tvTest.getTranslationY();
ObjectAnimator animator
= ObjectAnimator.ofFloat(tvTest, "translationY",
curTranslationY, curTranslationY + 100f);
animator.setDuration(2000);
animator.start();
LayoutParams
這個大家用的可能比較多一點
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
lp.leftMargin += 200;
view.setLayoutParams(lp);