Android的View体系与自定义View系列一:(Kotlin版)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本篇内容包括介绍View体系,坐标系以及View的滑动方式。本篇内容为Android进阶之光的个人学习总结,博主利用Kotlin对书本中的教学进行复构。


一、View与ViewGroup

1.View

我们知道Android有许多原生控件,比如最常用的TextView和ImageView,这些控件都和View有关。有的控件不直接继承View,但是间接继承View。这些控件我们点进源码会更好的理解。
如图TextView继承自View,而Button继承自TextView,从而间接实现继承View。发现了吗?常用的控件都是继承View的。
在这里插入图片描述
在这里插入图片描述

2.ViewGroup

如图,我们常用的布局如LinearLayout,它继承自ViewGroup。我们将控件写在布局中,布局类似一个大箩筐或者说是容器,里面放入我们需要的控件。那么布局的父类ViewGroup也类似一个大箩筐,这个大箩筐比较特殊,这个ViewGroup大箩筐中只装两种东西。一个是VIew,一个是ViewGroup。 便于你的理解,我用面向对象的思想以生活中的事物类比一下。我们将ViewGroup比作你上学时期背的书包,书包中包含课本(View)不能做容器的里面装不了东西的,和笔盒(ViewGroup)。
View树如下。
在这里插入图片描述

二、坐标系

1.Android坐标系

以屏幕的左上角为原点,向右为X轴正方向,向下为Y轴正方向。在触控事件中,通过**getRawX方法和getRawY**方法获得的坐标系也是Android坐标系。
在这里插入图片描述

2.View坐标系

在这里插入图片描述
如图,View的宽度 width = getRight() - getLeft()
求高与其原理相同。但是Android其实为我们提供了其封装方法 getHeight()getWidth() 内部原理就是上面的方法。

假设蓝色点为我们第一次触摸屏幕的点。View与ViewGroup的的点击事件都会由onTouchEvent(event: MotionEvent)来处理。

getX()			//获取点击事件距离控件左边的距离,即视图坐标
getY()			//获取点击事件距离控件**顶边**的距离,即视图坐标
getRawX()		//获取点击事件距离整个屏幕左边的距离,即绝对坐标
getRawY()		//获取点击事件距离整个屏幕**顶边**的距离,即绝对坐标

上方注意一下,涉及到Y的,都是获得与顶边的距离。

三、View滑动

View滑动这里,主要介绍6种滑动方法。所有例子来源于Android进阶之光,没有学习过的朋友可以过去学习看看。
方法分别是:layout方法、offsetLeftAndRight()offsetTopandBottom()方法、LayoutParams、动画、scrollToscrollBy,以及Scroller

还记得我们刚才说了View的点击实践都在OnTouchEvent(event: MotionEvent)处理吗?
我们新建一个CustomView类,并在onTouchEvent()方法中获取触摸点的坐标。
Kotlin版本的代码如下:

class CustomView @JvmOverloads
constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    val mScroller = Scroller(context)
    //初始化触摸屏幕瞬间的坐标
    var lastX = 0
    var lastY = 0
    override fun onTouchEvent(event: MotionEvent) : Boolean{
        //记录随着点击移动 变化的 x y坐标
        val x = event.x.toInt()
        val y = event.y.toInt()

        when(event.action){
            MotionEvent.ACTION_DOWN  -> {
                lastX = x
                lastY = y
            }
            MotionEvent.ACTION_MOVE -> {
                //计算偏移量
                val offsetX = x - lastX
                val offsetY = y - lastY

                //1.调用layout方法重新绘制界面UI
                //layout(left + offsetX, top + offsetY, right + offsetX, bottom + offsetY)
               
                //2.调用ofsetLeftAndRight和offsetTopAndBottom
//                offsetLeftAndRight(offsetX)
//                offsetTopAndBottom(offsetY)

                //3.LayoutParams 改变布局参数方式
//                var layoutParams: LinearLayout.LayoutParams = layoutParams as LinearLayout.LayoutParams
//                layoutParams.leftMargin = left + offsetX
//                layoutParams.topMargin = top + offsetY
//                setLayoutParams(layoutParams)

                //4.动画 新建 xml类型的文件,创建好文件后 在MainActivity中引用

                //5.scroolTo 和scrollBy
//                (parent as View).scrollBy(-offsetX,-offsetY)

                //6.Scroller
            }
        }
        return true
    }
}

这里对Kotlin语言不太熟悉的伙伴,可以参考第一行代码第三版。这里相对于Java的继承,需要熟悉一下Kotlin的继承写法,以及主从构造函数相关知识。

1.layout

我们可以通过layout方法,为其传入left、top、right、bottom参数完成View的绘制。

 layout(left + offsetX, top + offsetY, right + offsetX, bottom + offsetY)

2.offsetLeftAndRight()与offsetTopandBottom()方法

这个方法传入偏移的参数。

 offsetLeftAndRight(offsetX)
 offsetTopAndBottom(offsetY)

3. LayoutParams 改变布局参数的方式

LayoutParams 主要保存了一个View的布局参数,这里我们可以首先获得View的布局参数,再通过setLayoutParams()方法改变布局。

var layoutParams: LinearLayout.LayoutParams = layoutParams as LinearLayout.LayoutParams
                layoutParams.leftMargin = left + offsetX
                layoutParams.topMargin = top + offsetY
                setLayoutParams(layoutParams)

4.通过动画的方式:新建 xml类型的文件,创建好文件后,在MainActivity中引用

MainActivity中引用

 //4.动画 新建 xml类型的文件
        binding.customview.animation = AnimationUtils.loadAnimation(this,R.anim.translate)

5.scrollTo 和scrollBy

scrollTo()方法表示具体移到那个坐标位置,而scrollBy()表示移动多少距离。
这个是瞬间完成没有过度动画,不是很好看。除非你有特殊的要求。比如打地鼠?

 (parent as View).scrollBy(-offsetX,-offsetY)

这里你可以看到,这里面传入的参数为负数,为什么传入参数偏移为负的呢?
还记得初中学习过的物理知识吗?相对运动!假设A的上方放了一个B,我现在向左移动B,以B为参照物,请问A是向哪里运动的呢? 答案是向右的!那么我们把A当做我们想要改变的View,把B当做我们的屏幕。

6.Scroller

相比上个方法,这个方法的交互效果就要好很多了。但是它的内部也是通过不断地去scrollTo实现的。感兴趣的小伙伴可以查看源码。
首先我们要在类中初始化Scroller:val mScroller = Scroller(context)
并重写他的computeScroll方法。

override fun computeScroll() {
        super.computeScroll()
        if (mScroller.computeScrollOffset()){
            (parent as View).scrollTo(mScroller.currX,mScroller.currY)
            invalidate()
        }
    }

我们写一个用来调用Scroller的startScroll的封装

fun smoothScrollTo(desX: Int, desY: Int){
        val scrollX = scrollX
        val scrollY = scrollY

        val delta = desX - scrollX
        mScroller.startScroll(scrollX,0,delta,0,2000);
        //invalidate()不断的重新绘制视图,这样就会重新调用computeScroll方法
        invalidate()
    }

然后我们可以在MainActivity中调用 binding.customview.smoothScrollTo(-400,0) 这里设置让view向右移动400像素。

总结

以上就是本文的全部内容,如果本文对你有那么一丁点的帮助,可以收藏保存哦。我会进行持续的更新。
我们下次见~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值