view的基础知识介绍怎么写_汽车基础知识简单介绍

view的基础知识介绍怎么写_汽车基础知识简单介绍通过上面的官方介绍,我们可以看到,View 是我们平常看到的视图上所有元素的父类,按钮Button、文本TextView、图片ImageView 等。 ViewGroup 也是 View 的子类,ViewGroup 相当与 View 的容器,可以包含很多的 View. 左上角为…

转载请以链接形式标明出处: 本文出自:103style的博客

《Android开发艺术探索》 学习记录


可以带着以下问题来看本文:

  • View的坐标系和坐标,平移等动画改变的是什么属性?
  • View有哪些事件?
  • 如果获取系统可识别的最短滑动距离?
  • 如果计算滑动的速度?
  • 单击、双击、长按等事件的监听?
  • 弹性滑动的实现?

目录

  • View 与 ViewGroup
  • View 的位置参数
  • MotionEvent 和 TouchSlop
  • VelocityTracker
  • GestureDetector
  • Scroller

View与ViewGroup

View

public class View extends Object implements Drawable.Callback,KeyEvent.Callback,AccessibilityEventSource java.lang.Object    ↳ android.view.View Known direct subclasses AnalogClock , ImageView,KeyboardViewMediaRouteButtonProgressBar , Space , SurfaceView , TextViewTextureViewViewGroup,ViewStub. Known indirect subclasses AbsListViewAbsSeekBarAbsSpinner , AbsoluteLayoutAutoCompleteTextViewButtonCalendarViewCheckBoxCheckedTextViewChronometer, and 57 others..

ViewGroup

public abstract class ViewGroup extends View implements ViewParent, ViewManager java.lang.Objectandroid.view.Viewandroid.view.ViewGroup Known direct subclasses AbsoluteLayout, AdapterView<T extends Adapter>, FragmentBreadCrumbs, FrameLayout, GridLayout, LinearLayout, RelativeLayout, SlidingDrawer, Toolbar, TvView. Known indirect subclasses AbsListView, AbsSpinner, CalendarView, DatePicker, ExpandableListView, Gallery, GridView, HorizontalScrollView,ImageSwitcher, and 26 others.

通过上面的官方介绍,我们可以看到,View 是我们平常看到的视图上所有元素的父类,按钮Button、文本TextView、图片ImageView 等。 ViewGroup 也是 View 的子类,ViewGroup 相当与 View 的容器,可以包含很多的 View.


View的位置参数

View的坐标系如下图:

View坐标系

左上角为原点O(0,0),X、Y轴分别向右向下递增。 图中 View 和 ViewGroup 的位置由其四个顶点决定,以View为例,分别对应四个属性:LeftTopRightBottom. 所以 Width = Right - Left, Height = Bottom - Top.

Android 3.0 开始,View又增加了 xytranslationXtranslationY 四个参数。 xy 即为上图中的A点,分别对应A点在View坐标系中的X、Y轴上的坐标。 translationXtranslationY则为相对于父容器ViewGroup的偏移量,默认为 0。 他们的关系为: x = left + tranlastionXy = top + tranlastionY.

需要注意的是:在平移过程中,top 和 left 表示的是原始左上角的位置信息,是不变的,发生改变的是 x、y、translationX、translationY

下面我们来测试看看:

<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
    <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dp" android:padding="8dp" android:text="Hello World!" />
</LinearLayout>
//MainActivity.java
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView tv = findViewById(R.id.tv);
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, "tv.getLeft() = " + tv.getLeft());
                Log.e(TAG, "tv.getTop() = " + tv.getTop());
                Log.e(TAG, "tv.getRight() = " + tv.getRight());
                Log.e(TAG, "tv.getBottom() = " + tv.getBottom());
                Log.e(TAG, "tv.getWidth() = " + tv.getWidth());
                Log.e(TAG, "tv.getHeight() = " + tv.getHeight());
                Log.e(TAG, "tv.getX() = " + tv.getX());
                Log.e(TAG, "tv.getY() = " + tv.getY());
                Log.e(TAG, "tv.getTranslationX() = " + tv.getTranslationX());
                Log.e(TAG, "tv.getTranslationY() = " + tv.getTranslationY());
            }
        });
    }
}

点击按钮,打印日志如下:

MainActivity: tv.getLeft() = 21
MainActivity: tv.getTop() = 21
MainActivity: tv.getRight() = 263
MainActivity: tv.getBottom() = 114
MainActivity: tv.getWidth() = 242
MainActivity: tv.getHeight() = 93
MainActivity: tv.getX() = 21.0
MainActivity: tv.getY() = 21.0
MainActivity: tv.getTranslationX() = 0.0
MainActivity: tv.getTranslationY() = 0.0

我们可以看到 left、top、right、bottom 是整形的, 而 x、y、translationX、translationY 是浮点型的


MotionEvent 和 TouchSlop

MotionEvent 即为我们点击屏幕所产生的一些列事件,主要有以下几个:

  • ACTION_DOWN:手指刚接触屏幕。
  • ACTION_MOVE:手指在屏幕上滑动。
  • ACTION_UP:手指离开屏幕的一瞬间。
  • ACTION_CANCEL:消耗了DOWN事件却没有消耗UP事件,再次触发DOWN时,会先触发CANCEL事件。

一般依次点击屏幕操作,会产生一些列事件:DOWN → 0个或多个 MOVE → UP。 通过MotionEvent 我们可以知道事件发生的 x , y 坐标, 可以通过系统提供的 getX()/getY()getRawX()/getRawY()获取。 getX()/getY()是对于当前View左上角的坐标. getRawX()/getRawY()则是对于屏幕左上点的坐标.

TouchSlop 则是系统所能识别的最短的滑动距离, 这个距离可以通过 ViewConfiguration.get(getContext()).getScaledTouchSlop() 获得。 在 Genymotion上的 Google pixel 9.0系统 420dpi 的模拟器上得到的值如下:

MainActivity: getScaledTouchSlop = 21

VelocityTracker

VelocityTracker 是用来记录手指滑动过程中的速度的,包括水平方向和数值方向。 可以通过如下方式来获取当前事件的滑动速度:

tv.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                VelocityTracker velocityTracker = VelocityTracker.obtain();
                velocityTracker.addMovement(event);
                velocityTracker.computeCurrentVelocity(1000);
                float vX = velocityTracker.getXVelocity();
                float vY = velocityTracker.getYVelocity();
                Log.e(TAG, "vX = " + vX + ", vY = " + vY);
                velocityTracker.clear();
                velocityTracker.recycle();
                break;
        }
        return true;
    }
});
MainActivity: vX = 542.164, vY = 271.18683
MainActivity: vX = 2257.9578, vY = 291.47467
MainActivity: vX = 2237.9333, vY = 379.69537
MainActivity: vX = 1676.5919, vY = 697.79443
MainActivity: vX = 1672.0844, vY = 288.5999
MainActivity: vX = 645.7418, vY = 322.51065
MainActivity: vX = 810.2783, vY = 270.19778

当然最后,在不用的时候记得调用以下代码重置并回收掉 VelocityTracker:

velocityTracker.clear();
velocityTracker.recycle();

GestureDetector

GestureDetector 即手势检测,用于辅助我们捕获用户的 单击、双击、滑动、长按等行为。

使用也很简单,只需要创建一个下面来看个示例。 在构造函数中创建 通过 gestureDetector = new GestureDetector(context, this) 创建 GestureDetector, 然后实现 GestureDetector.OnGestureListenerGestureDetector.OnDoubleTapListener 接口, 然后在 onTouchEvent 中 返回 gestureDetector.onTouchEvent(event)

public class TestGestureDetector extends View implements GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener {
    private static final String TAG = "TestGestureDetector";
    GestureDetector gestureDetector;
    public TestGestureDetector(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        gestureDetector = new GestureDetector(context, this);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }
    @Override
    public boolean onDown(MotionEvent e) {
        Log.e(TAG, "onDown: action = " + e.getAction());
        return false;
    }
    @Override
    public void onShowPress(MotionEvent e) {
        Log.e(TAG, "onShowPress:");
    }
    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        Log.e(TAG, "onSingleTapUp: " + e.getAction());
        return false;
    }
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        Log.e(TAG, "onScroll: e1.action = " + e1.getAction() + ", e2.action = " + e2.getAction());
        return false;
    }
    @Override
    public void onLongPress(MotionEvent e) {
        Log.e(TAG, "onLongPress: action = " + e.getAction());
    }
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        Log.e(TAG, "onFling: e1.action = " + e1.getAction() + ", e2.action = " + e2.getAction());
        return false;
    }
    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        Log.e(TAG, "onSingleTapConfirmed: action = " + e.getAction());
        return false;
    }
    @Override
    public boolean onDoubleTap(MotionEvent e) {
        Log.e(TAG, "onDoubleTap: action = " + e.getAction());
        return false;
    }
    @Override
    public boolean onDoubleTapEvent(MotionEvent e) {
        Log.e(TAG, "onDoubleTapEvent: action = " + e.getAction());
        return false;
    }
}

然后在布局中让它占满屏幕。

tips: action = 0DOWN 事件 action = 1UP 事件 action = 2MOVE 事件

运行程序,我们执行一次单击,一次长按单击,然后双击一次,发下打印日志如下:

//第一次单击
TestGestureDetector: onDown: action = 0
TestGestureDetector: onShowPress:
TestGestureDetector: onLongPress: action = 0
//第一次长按单击
TestGestureDetector: onDown: action = 0
TestGestureDetector: onShowPress:
TestGestureDetector: onLongPress: action = 0
//第一次双击
TestGestureDetector: onDown: action = 0
TestGestureDetector: onShowPress:
TestGestureDetector: onDown: action = 0
TestGestureDetector: onShowPress:
TestGestureDetector: onLongPress: action = 0

通过上面的日志信息我们可以知道 : 一次 单击长按单击 操作会触发 onDownonShowPressonLongPress三个回调。 双击 操作则会依次触发 onDownonShowPressonDownonShowPressonLongPress 五次回调。

显示单击出现 onLongPress 是不合理的,我们可以通过 gestureDetector.setIsLongpressEnabled(false) 禁用掉,而且我们也没有监听到 单机和双击等其他回调,这是为什么呢?

这是因为我们 没有消耗掉 DOWN 事件,这涉及到事件分发相关的知识了,这里先不说,后面会写文章单独讲解。那怎么消耗掉 DOWN 事件呢?很简单,只要在 onDown 中返回 true。 修改上述代码如下,只贴出修改的部分,

public class TestGestureDetector extends View implements GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener {
    ...
    public TestGestureDetector(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        gestureDetector = new GestureDetector(context, this);
        gestureDetector.setIsLongpressEnabled(false);
    }
    @Override
    public boolean onDown(MotionEvent e) {
        Log.e(TAG, "onDown: action = " + e.getAction());
        return true;
    }
    ...
}

运行程序,在执行一次单击,一次长按单击和一次双击,日志如下:

//第一次单击
TestGestureDetector: onDown: action = 0
TestGestureDetector: onSingleTapUp: 1
TestGestureDetector: onSingleTapConfirmed: action = 0
//第一次长按单击
TestGestureDetector: onDown: action = 0
TestGestureDetector: onShowPress:
TestGestureDetector: onSingleTapUp: 1
TestGestureDetector: onSingleTapConfirmed: action = 1
//第一次双击
TestGestureDetector: onDown: action = 0
TestGestureDetector: onSingleTapUp: 1
TestGestureDetector: onDoubleTap: action = 0
TestGestureDetector: onDoubleTapEvent: action = 0
TestGestureDetector: onDown: action = 0
TestGestureDetector: onDoubleTapEvent: action = 1

我们可以看到现在一次单击则会触发onDownonSingleTapUponSingleTapConfirmed 这三个回调。 一次长按单击则会触发onDownonShowPressonSingleTapUponSingleTapConfirmed 这四个回调。 一次双击则会一次触发onDownonSingleTapUponDoubleTaponDoubleTapEventonDownonDoubleTapEvent 这六个回调。

而我们在屏幕上快速滑动时,则会触发 onDownonShowPressonScrollonScrollonFling这五个回调,onShowPress 取决于你在按下和开始滑动之前的时间间隔,短的话就不会有, 是否有 onFling 取决于滑动的距离和速度

TestGestureDetector: onDown: action = 0
TestGestureDetector: onShowPress:
TestGestureDetector: onScroll: e1.action = 0, e2.action = 2
TestGestureDetector: onScroll: e1.action = 0, e2.action = 2
TestGestureDetector: onFling: e1.action = 0, e2.action = 1

下面我们来统一介绍下这些回调具体的含义把:

方法名 描述 所属接口
onDown 触摸View的瞬间,由一个 DOWN 触发 OnGestureListener
onShowPress 触摸View未松开或者滑动时触发 OnGestureListener
onSingleTapUp 触摸后松开,在onDown的基础上加了个 UP 事件,
属于单击行为
OnGestureListener
onScroll 按下并拖动,由一个 DOWN 和 多个 MOVE 组成,
属于拖动行为
OnGestureListener
onLongPress 长按事件 OnGestureListener
onFling 快速滑动后松开,需要滑动一定的距离 OnGestureListener
onSingleTapConfirmed 严格的单击行为,onSingleTapUp之后只能是onSingleTapConfirmed 或 onDoubleTap 中 的一个 OnDoubleTapListener
onDoubleTap 双击行为,和 onSingleTapConfirmed 不共存 OnDoubleTapListener
onDoubleTapEvent 表示双击行为的发生,
一次双击行为会触发多次onDoubleTapEvent
OnDoubleTapListener

Scroller

Scroller 用于实现View的弹性滑动,当我们使用View的 scrollToscrollBy 方法进行滑动时,滑动时瞬间完成的,没有过渡效果使得用户体验不好,这个时候就可以使用 Scroler 来解决这一用户体验差的问题。 Scroller本身无法让View弹性滑动,需要配合View的 computeScroll 方法。

那如果使用Scroller呢? 它的典型代码是固定的,如下所示。 至于为什么能够实现,我们下篇文章介绍 View的滑动 的时候再具体分析。

public class TestScroller extends TextView {
    private static final String TAG = "TestScroller";
    Scroller mScroller;
    public TestScroller(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mScroller = new Scroller(context);
    }
    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }
    public void smoothScrollTo(int destX, int destY) {
        int scrollX = getScrollX();
        int scrollY = getScrollY();
        int deltaX = destX - scrollX;
        int deltaY = destY - scrollY;
        mScroller.startScroll(scrollX, scrollY, deltaX, deltaY, 1000);
        invalidate();
    }
}
//activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <com.lxk.viewdemo.TestScroller
        android:id="@+id/tv"
        android:layout_width="320dp"
        android:layout_height="320dp"
        android:layout_margin="8dp"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:padding="8dp"
        android:text="Hello World!" />
</LinearLayout>
//MainActivity.java
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TestScroller scroller = findViewById(R.id.tv);
        scroller.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                scroller.smoothScrollTo(200, 200);
            }
        });
}

运行看看,可以看到点击之后,内容在 1s 内往左上方各平移了 200px

Scroll


如果觉得不错的话,请帮忙点个赞呗。

以上


扫描下面的二维码,关注我的公众号 Android1024, 点关注,不迷路。

Android1024

`

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13205.html

(0)

相关推荐

  • 我去,你写的 switch 语句也太老土了吧

    我去,你写的 switch 语句也太老土了吧昨天早上通过远程的方式 review 了两名新来同事的代码,大部分代码都写得很漂亮,严谨的同时注释也很到位,这令我非常满意。但当我看到他们当中有一个人写的 switch 语句时,还是忍不住破口大骂:“我擦,小王,你丫写的 switch 语句也太老土了吧!” 来看看小王写的代码吧…

    2023-07-24
    129
  • kafka突然挂掉_kafka重试机制

    kafka突然挂掉_kafka重试机制LINUX上的部署服务时,如果没有注意文件路径、磁盘大小,简单地按照部署文档,应用崩的时候不要学葫芦娃叫爷爷,丢人!

    2023-04-22
    147
  • 云原生低代码扛起提效重担,高成本建生态行业仍处拓荒期

    云原生低代码扛起提效重担,高成本建生态行业仍处拓荒期11月13日2022年腾讯云开发技术峰会上,腾讯公司高级执行副总裁、云与智慧产业事业群CEO汤道生表示,云计算已发展十多年,如果说十年前的云计算

    2022-12-14
    221
  • mysql 主从复制[通俗易懂]

    mysql 主从复制[通俗易懂]1, 准备二台机器或者服务器 ,保持mysq 版本一样或者版本相差不大; 主机:114.215.198.39 从机:116.62.234.228 2 新建一个数据库 我的数据库是hlqzxm; 进入…

    2023-03-27
    167
  • MySQL是如何实现事物隔离?[亲测有效]

    MySQL是如何实现事物隔离?[亲测有效]前言 众所周知,MySQL的在RR隔离级别下查询数据,是可以保证数据不受其它事物影响,而在RC隔离级别下只要其它事物commit后,数据都会读到commit之后的数据,那么事物隔离的原理是什么?是通过

    2023-04-16
    152
  • Python中激活虚拟环境的方法

    Python中激活虚拟环境的方法Python中虚拟环境是一个重要的概念,它可以让我们在同一台电脑上同时使用不同版本的Python以及不同的Python库,同时避免了库之间的冲突。在Python中激活虚拟环境是一个必备的技能,本文将从多个方面对Python中激活虚拟环境的方法进行详细阐述。

    2024-06-22
    40
  • mysql学习笔记之explain分析

    mysql学习笔记之explain分析mysql explain功能中展示各种信息的解释: id:优化器选定的执行计划中查询的序列号。 select_type:所用的查询类型,主要由以下这集中查询类型。 . DEPENDENT UNIO…

    2023-03-11
    148
  • hbase客户端工具_cdh安全

    hbase客户端工具_cdh安全集群版本:CDH6.2.0集群Hadoop版本:Hadoop 3.0.0-cdh6.2.0集群HBase版本:2.1.0-cdh6.2.0 操作系统:macOS 10.15.5 IntelliJ ID

    2023-03-11
    141

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注