亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

View體系之View的位置與事件

2019-11-09 16:25:48
字體:
來源:轉載
供稿:網友

View體系之View的位置與事件

本文原創,轉載請注明出處。 歡迎關注我的 簡書 ,關注我的專題 Android Class 我會長期堅持為大家收錄簡書上高質量的 Android 相關博文。

寫在前面: 最近完成了開發任務,接下來工作上做一些優化和修修補補的工作就可以了,所以難得有一些完整的時間來鞏固知識。我們知道基本上 RecyclerView 每個人都有接觸過,但是看過源碼或者理解原理的并不多,以前我們用 ListView,包括后來又出現了的 CoordinatorLayout 來完成復雜炫酷的聯動效果,ConstraintLayout 來給子 View 之間添加約束。完成這些高大上功能的都是自定義 View,所以真正掌握理解自定義 View,幾乎成了 Android 開發者的必備技能。所以我也通過看書和官方文檔,來學習鞏固這里的知識,整理成系列的文章,方便記憶和交流。

View 的概念

View 是什么?我理解 View 有兩層含義。首先 View 是 Android 所有視圖中頂層的基類,是一個抽象的概念。其次 View 也可以特指某一個不再可以有子 View 的 View。

如果第一次理解起來可能不太容易,不過我畫了一張圖,應該好理解多了。

View 樹狀結構

首先頂層的 View 是一個抽象概念,無論 ViewGroup(視圖組,比如 RelativeLayout)還是一個具體的 View(Button TextView),他們都繼承自 View。而 ViewGroup 本身可以包含很多個 ViewGroup 和 View。

View 的位置

當一個 View 擺在屏幕上時,你能想到它最基本有哪些屬性?對了,就是它自身的大小和位置。

View 本身提供了一些 get set 方法,讓我們獲得它的成員變量,其中就包括位置的信息。

來寫一個 Demo 更好的理解。

布局文件如下,非常簡單:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="melo.leja.com.viewdemo.MainActivity"> <melo.leja.com.viewdemo.DemoRelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="16dp" android:layout_marginTop="16dp"> <Button android:id="@+id/bt_view_demo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginTop="16dp" android:text="Hello World!" /> </melo.leja.com.viewdemo.DemoRelativeLayout></RelativeLayout>

MainActivity:

@Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { initView(); } } PRivate void initView() { int top = btViewDemo.getTop(); int left = btViewDemo.getLeft(); int bottom = btViewDemo.getBottom(); int right = btViewDemo.getRight(); //在 View 平移過程中,這幾個參數并不改變(原始值) Log.e(TAG, "top:" + top + ",left:" + left + ",bottom:" + bottom + ",right:" + right); float x = btViewDemo.getX(); float y = btViewDemo.getY(); //平移過程中此參數發生變化 Log.e(TAG, "x:" + x + ",y:" + y); float translationX = btViewDemo.getTranslationX(); float translationY = btViewDemo.getTranslationY(); Log.e(TAG, "translationX:" + translationX + ",translationY:" + translationY); //在 MotionEvent 之中 getRawX - getLeft = getX int scaledTouchSlop = ViewConfiguration.get(getapplicationContext()).getScaledTouchSlop(); Log.e(TAG, "scaledTouchSlop:" + scaledTouchSlop); }

首先有個小坑,這些 View 的屬性onWindowFocusChanged方法中才能獲取到,在oncreate onStart onResume 獲取不到。具體原因未來再解釋。

首先獲取位置信息的方法分為幾組:

getTop getLeft() getBottom() getRight()

它們分別可以獲取 View 原始的 上邊距縱坐標、左邊距橫坐標、下邊距縱坐標、右邊距橫坐標。

getX() getY() 它們獲取的是 View 左上角的坐標。

getTranslationX() getTranslationY() 它們獲取的是 View 左上角的偏移量,默認為 0。

用圖理解:

這里寫圖片描述

首先 Android 的坐標系是以右下為正方向的。假設 View 向右下角移動了一點。 top bottom left right 這幾個屬性是 View 的原始屬性,并不會因為平移和發生改變,在 x 方向上平移了 translationX 和 y 方向平移了 translationY 大小之后,這個時候 x,y 各自的數值如圖所示。

也就是說: getLeft() + getTranslationX() = getX()

來看看打印的 log 吧:

log

因為 Button 父 View 為一個自定義的 RelativeLayout,當我改變這個自定義 RelativeLayout 的 margin 值時,打印出來的任何值都沒有發生改變。所以我想說明的是,上述成員變量的意義都是:

相對于父布局的大小,并不是一個絕對值。

另外相信你也肯定能知道,這個 View 自身的

width = getRight - getLeft height = getBottom - getTop

MotionEvent

關于 MotionEvent,先來看看官方文檔的解釋:

Object used to report movement (mouse, pen, finger, trackball) events. Motion events may hold either absolute or relative movements and other data, depending on the type of device.

其意思就是 MotionEvent 是用來報告運動事件的載體,在 Android 設備中,一般 MotionEvent 攜帶有事件類型、事件的坐標、事件的時間等等。

我們在未來會討論的事件分發中,傳遞的就是 MotionEvent。

我們給 Button set 一個 onTouchListener,來看看 MotionEvent 里面都有什么。

btViewDemo.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { float x = motionEvent.getX(); float y = motionEvent.getY(); float rawX = motionEvent.getRawX(); float rawY = motionEvent.getRawY(); int action = MotionEventCompat.getActionMasked(motionEvent); switch (action) { case MotionEvent.ACTION_DOWN: Log.e(TAG, "ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(TAG, "ACTION_UP"); break; } Log.e(TAG, "event.getX:" + x + ",getRawX:" + rawX + ",event.getY:" + y + ",getRawY:" + rawY); return false;

這里依然有兩組參數,getX/getRawX,這兩個又代表什么意思呢?

getX,getRawX

在 MotionEvent 攜帶的事件信息中,getX/getY 指的是相對于當前 View 左上角而言的,getRawX/getRawY 是相對于屏幕邊距的。raw 這個單詞是 生的 未加工的意思,這樣一來,就好理解多了。

再來說 MotionEvent 的中事件的類型,Demo 中我只列舉出來了三個,當我手指按下 –> 滑動 –> 抬起時,MotionEvent 中的事件會一次變為 一個 ACTION_DOWN 多個 ACTION_MOVE 和一個 ACTION_UP。

當然關于事件類型還有很多,大家可以去查閱官方文檔,這里不多做解釋,未來需要再補充吧!

TouchSlop

TouchSlop 這個值是設備認為滑動的最小距離,如果滑動的距離小于 TouchSlop 的值,則被認為沒有滑動。

這個值是于設備有關的,不同的設備 TouchSlop 的值也許是不一樣的。

這個值的獲取方法:

int scaledTouchSlop = ViewConfiguration.get(getApplicationContext()).getScaledTouchSlop(); Log.e(TAG, "scaledTouchSlop:" + scaledTouchSlop);

VelocityTracker

VelocityTracker 這個類是用來追蹤手指的速度的,我們可以在重寫 View 的 onTouchEvent 方法來使用它:

@Override public boolean onTouchEvent(MotionEvent event) { VelocityTracker velocityTracker = VelocityTracker.obtain(); velocityTracker.addMovement(event); boolean consume = mGestureDetector.onTouchEvent(event); int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: velocityTracker.computeCurrentVelocity(1 * 1000); float xVelocity = velocityTracker.getXVelocity(); float yVelocity = velocityTracker.getYVelocity(); Log.e(TAG, "xVelocity:" + xVelocity + ",yVelocity:" + yVelocity); break; case MotionEvent.ACTION_UP: velocityTracker.clear(); velocityTracker.recycle(); break; case MotionEvent.ACTION_CANCEL: break; } return true; }

通過 addMovement(event) 方法將 MotionEvent 傳入,類的內部會進行一些運算,這里我們不用關心。然后 computeCurrentVelocity 傳入一個時間間隔,單位為毫秒。這個的意思就是在這個時間間隔內,移動的像素值,就是得到的速度。注意速度是有正負的,這與我們高中學的物理概念相同。

注意在不使用的時候調用 velocityTracker.clear(); velocityTracker.recycle(); 進行資源回收。

GestureDetector

手勢檢測,作用就是檢測用戶的單擊、雙擊、長按、滑動、快速滑動等等。使用方法和剛才的速度檢測類是一樣的,都需要將 MotionEvent 傳入。

public DemoRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mGestureDetector = new GestureDetector(this); mGestureDetector.setIsLongpressEnabled(false); }public class DemoRelativeLayout extends RelativeLayout implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener

當然自定義 View 還要實現這兩個接口來實現回調。

在 onTouchEvent 中調用 boolean consume = mGestureDetector.onTouchEvent(event);(代碼在上方)

我把 MotionEvent 對象傳給了 GestureDetector,由 GestureDetector 內部去做運算分析,我究竟做了哪些手勢。說到這里你也就明白了,無論是速度還是手勢識別,其實就是拿著 MotionEvent 中的事件信息去進行分析,相當于一個輔助工具類就對了。來看看這些回調方法吧。

@Override public boolean onDown(MotionEvent motionEvent) { // 按下 Log.e(TAG, "onDown"); return false; } @Override public void onShowPress(MotionEvent motionEvent) { // 按住不松手 Log.e(TAG, "onShowPress"); } @Override public boolean onSingleTapUp(MotionEvent motionEvent) { // 單擊 Log.e(TAG, "onSingleTapUp"); return false; } @Override public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) { // 滑動 Log.e(TAG, "onScroll"); return false; } @Override public void onLongPress(MotionEvent motionEvent) { // 長按 Log.e(TAG, "onLongPress"); } @Override public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) { // 快速滑動 Log.e(TAG, "onFling"); return false; } @Override public boolean onSingleTapConfirmed(MotionEvent motionEvent) { // 嚴格的單擊 Log.e(TAG, "onSingleTapConfirmed"); return false; } @Override public boolean onDoubleTap(MotionEvent motionEvent) { // 雙擊 Log.e(TAG, "onDoubleTap"); return false; } @Override public boolean onDoubleTapEvent(MotionEvent motionEvent) { // 雙擊 Log.e(TAG, "onDoubleTapEvent"); return false; }

還是挺多的,做了一下簡單地解釋,如果需要深入了解,那就查閱下官方文檔吧!

本文到這里的內容已經完成了,下文中會研究講解 View 的滑動,以及最后還有重頭戲,也就是 View 的事件分發,敬請期待吧~


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧亚精品在线观看| 久久成人国产精品| 国产va免费精品高清在线| 日本精品久久中文字幕佐佐木| 成年无码av片在线| 91在线观看免费观看| 欧美色图在线视频| 色与欲影视天天看综合网| 亚洲人成绝费网站色www| 亚洲高清免费观看高清完整版| 亚洲欧美中文字幕在线一区| 在线国产精品视频| 全色精品综合影院| 日本在线精品视频| 国产69久久精品成人看| 亚洲毛茸茸少妇高潮呻吟| 日韩精品中文字幕在线播放| 色777狠狠综合秋免鲁丝| 久久久精品国产网站| 日韩在线观看免费网站| 日韩黄在线观看| 欧美精品激情在线观看| 日韩成人在线电影网| 成人黄色免费在线观看| 亚洲影院色无极综合| 日韩欧美成人网| 国产精品91视频| 亚洲欧美一区二区精品久久久| 国产91精品高潮白浆喷水| 国产精品久久久久久一区二区| 尤物九九久久国产精品的分类| 亚洲美女av电影| 国产欧美日韩精品丝袜高跟鞋| 最近2019年中文视频免费在线观看| 国产精品无av码在线观看| 91热精品视频| 午夜欧美不卡精品aaaaa| 91wwwcom在线观看| 欧美日韩国产精品专区| 欧美大全免费观看电视剧大泉洋| 亚洲在线免费看| 一本色道久久88亚洲综合88| 亚洲精品美女久久久| 日本久久久久亚洲中字幕| 久久国产精品久久精品| 成人欧美一区二区三区在线湿哒哒| 久久躁日日躁aaaaxxxx| 国产精品国产三级国产aⅴ9色| 亚洲国产精品系列| 97精品国产aⅴ7777| 亚洲精品www久久久| 一区二区三区天堂av| 国产精品午夜一区二区欲梦| 91久久精品国产91久久| 欧美视频专区一二在线观看| 日韩视频中文字幕| 92国产精品久久久久首页| 精品国产乱码久久久久久婷婷| 欧美一级在线亚洲天堂| 中文字幕日韩综合av| 欧美精品videos另类日本| 国产suv精品一区二区三区88区| 久久精品最新地址| 欧美专区国产专区| 国产精品夜间视频香蕉| 国产成人精品综合久久久| 美女999久久久精品视频| 欧美巨乳在线观看| 国产精品1234| 欧美巨大黑人极品精男| 国产一区二区三区欧美| 国产成人在线一区二区| 久久精品99久久久久久久久| 日韩电影第一页| 国产精品久久久久久久久久久久久| 国产69精品久久久久9999| 国产精品成人在线| 欧美国产日韩一区二区| 亚洲综合最新在线| 97精品一区二区三区| 欧美大片在线看| 岛国av在线不卡| 欧美日韩高清在线观看| 在线观看欧美日韩| 亚洲aa在线观看| 欧美精品在线播放| 国产精品电影网站| 91免费看片网站| 色吧影院999| 欧美高清视频在线观看| 4388成人网| 97久久精品视频| 亚洲欧美精品中文字幕在线| 亚洲美女性视频| 亚洲综合中文字幕在线观看| 91精品国产91久久久| 国产噜噜噜噜久久久久久久久| 永久免费毛片在线播放不卡| 国产成人久久久精品一区| 中文字幕欧美日韩va免费视频| 91手机视频在线观看| 日韩欧美aⅴ综合网站发布| 久久国产精品电影| 亚洲新声在线观看| 日本欧美精品在线| 久久精视频免费在线久久完整在线看| 中文字幕av一区二区| 日本一区二区在线免费播放| 欧美视频在线观看免费网址| 黑丝美女久久久| 亚洲成人黄色在线| 欧美日韩黄色大片| 久久99亚洲精品| 欧美性猛交xxxxx水多| 久久精品视频网站| 日韩电影免费观看在线| 大荫蒂欧美视频另类xxxx| 欧美在线视频观看免费网站| 欧美精品免费播放| 欧美一级大片在线免费观看| 亚州精品天堂中文字幕| 91tv亚洲精品香蕉国产一区7ujn| 国产精品夜间视频香蕉| 久久精品国产清自在天天线| 北条麻妃在线一区二区| 亚洲精品视频播放| 这里只有精品久久| 狠狠色香婷婷久久亚洲精品| 欧美激情精品久久久久久黑人| 亚洲欧洲午夜一线一品| 68精品国产免费久久久久久婷婷| 亚洲欧美三级在线| 一道本无吗dⅴd在线播放一区| 国产精品99久久99久久久二8| 在线视频日韩精品| 黑人巨大精品欧美一区二区三区| 成人免费网站在线| 精品中文字幕在线| 久久精品视频播放| 国产中文日韩欧美| 中文国产成人精品| 国产综合香蕉五月婷在线| 国语自产在线不卡| 亚洲第一视频网| 97视频在线观看视频免费视频| 久久久国产成人精品| 国产视频欧美视频| 亚洲日本成人女熟在线观看| 国产色视频一区| 日本亚洲精品在线观看| 69国产精品成人在线播放| 成人a在线观看| 韩剧1988在线观看免费完整版| 亚洲自拍偷拍视频| 亚洲一区二区久久久久久| 国产精品日韩在线播放| 亚洲精品影视在线观看| 一区二区欧美日韩视频| 久久久久国产精品免费网站| 久久精品国产91精品亚洲| xxxx欧美18另类的高清| 亚洲日本aⅴ片在线观看香蕉| 亚洲欧美色图片| 国产午夜精品免费一区二区三区|