android 自定義View從基礎(chǔ)到移動


其實一直沒想好自定義控件這塊怎么寫,因為涉及面太廣,而且我自定義控件能力其實很一般帽驯。先寫著同眯,再優(yōu)化吧。
網(wǎng)上也有很多大藕万剑總結(jié)的都挺好的,我也是看著他們的博客學(xué)習自己總結(jié)下,當做日記

基礎(chǔ)入門

android坐標系是左上角為原點呻澜,往右邊X增加,往下Y增加惨险。自定義View可以分為繼承自View和ViewGroup羹幸。


android坐標系


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);
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市撑刺,隨后出現(xiàn)的幾起案子鹉胖,更是在濱河造成了極大的恐慌,老刑警劉巖够傍,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件次员,死亡現(xiàn)場離奇詭異,居然都是意外死亡王带,警方通過查閱死者的電腦和手機淑蔚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來愕撰,“玉大人刹衫,你說我怎么就攤上這事醋寝。” “怎么了带迟?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵音羞,是天一觀的道長。 經(jīng)常有香客問我仓犬,道長嗅绰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任搀继,我火速辦了婚禮窘面,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘叽躯。我一直安慰自己财边,他們只是感情好,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布点骑。 她就那樣靜靜地躺著酣难,像睡著了一般。 火紅的嫁衣襯著肌膚如雪黑滴。 梳的紋絲不亂的頭發(fā)上憨募,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音袁辈,去河邊找鬼菜谣。 笑死,一個胖子當著我的面吹牛吵瞻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播甘磨,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼橡羞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了济舆?” 一聲冷哼從身側(cè)響起卿泽,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滋觉,沒想到半個月后签夭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡椎侠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年第租,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片我纪。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡慎宾,死狀恐怖丐吓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情趟据,我是刑警寧澤券犁,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站汹碱,受9級特大地震影響粘衬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜咳促,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一稚新、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧等缀,春花似錦枷莉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至噪裕,卻和暖如春蹲盘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背膳音。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工召衔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人祭陷。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓苍凛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親兵志。 傳聞我的和親對象是個殘疾皇子醇蝴,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容