麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

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

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 | 黄色网址在线免费 | 国产精品99久久99久久久二 | 久久手机在线视频 | 一级在线观看视频 | 欧美黑人伦理 | 插插操 | 国产男女爽爽爽爽爽免费视频 | 92看片淫黄大片欧美看国产片 | 欧美日韩电影 | 免费看日产一区二区三区 | 欧美精品一区二区中文字幕 | h网站在线观看 | 日本网站一区二区三区 | 日日鲁夜夜视频热线播放 | 亚洲精品一区二区三区在线看 | 欧美国产一级片 | 欧美另类在线视频 | 国产91丝袜在线熟 | 久久久久av电影 | 日本精品免费观看 | 久久视频精品 | 久久精品国产99国产精品亚洲 | 成人福利视频在线 | 久久草在线观看视频 | wankzhd| 成人福利视频导航 | 在线观看91精品 | 狠狠婷婷综合久久久久久妖精 | 日日草天天干 | 色99久久 | 精品国产一区二 | 亚洲一区二区中文字幕在线观看 | 羞羞视频免费视频欧美 | 久久一区二区三区av | 免费看成年人网站 | www.91sp| 91精品视频免费 |