热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android自定义View的使用及其原理知识点总结

在本篇文章里小编给大家整理的是关于Android自定义View的使用及其原理知识点总结内容,需要的朋友们可以学习下。

在Android开发中,系统提供给我们的UI控件是有限的,当我们需要使用一些特殊的控件的时候,只靠系统提供的控件,可能无法达到我们想要的效果,这时,就需要我们自定义一些控件,来完成我们想要的效果了。下面,我就来讲讲自定义控件的那些事。

首先,我来讲讲Android的控件架构。Android的控件可以被分为两类,分别是ViewGroup和View。在ViewGroup中可以包含多个View,并且管理他们。控件树就是有这两个部分组成的,控件树的上层负责的是下层控件的绘制和测量以及交互。我们在Activity中使用的findViewById()方法,就是在控件树中用深度遍历的方法搜索到对应的ID的。每一颗控件树的顶部,都有个ViewParent对象,他是整棵树的核心,负责调度所有的交互事件。在Activity中,我们是使用setContentView()来加载布局的。每个Activity都是包含着一个Window对象的,在Android中通常是PhoneWindow,他将一个DecorView作为整个窗口的根View,将要显示的内容呈现在window上。DecorView又分为两个部分,一个是TitleView,一个是ContentView。ContentView是一个ID为content的Framelayout,布局文件就是设置在这里面的。而TitleView就是我们看到topbar标题栏。这就是activity加载布局文件的过程了。

接下来,我们开始讲自定义控件的使用,下面讲解使用的时候,会夹带着一些原理的分析。自定义控件可以分为三种类型,一种是拓展谷歌提供的系统控件,来达到自己想要的效果。一种是将系统提供的控件组合在一起,作为一个组合控件来使用。还有一种是重新绘制测量一个全新的控件。

一、拓展谷歌提供的系统控件

假如我们要对Textview控件进行拓展,首先我们要定义一个类继承TextView,选择性的重写它的onDraw()、onMeasure()、onTouchEvent()等方法。其中,onDraw()负责对图像的绘制,onMeasure()负责测量位置,onTouchEvent()负责设置触摸的事件。当我们想直接绘制出有背景颜色的TextView时,可以在类中定义画笔,在onDraw()进行绘制。代码如下:

Paint paint1=new Paint(); //定义画笔
paint1.setColor(Color.YELLOW);
paint1.setStyle(Paint.Style.FILL);

然后,通过以下的代码,就可以绘制出一个带矩形框的Textview,但是需要在绘制完成后在调用父类的onDraw(),因为是在系统控件上拓展,所以,还要有其原来的功能。

@Override
  protected void onDraw(Canvas canvas) {
    canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),paint1);//绘制矩形
    canvas.save();
    super.onDraw(canvas);
    canvas.restore();
  }

使用canvas对象就可以进行绘图了,对canvas的讲解,我将会在下一篇博客讲解。

然后,我们只需要在布局文件中加入自定义的控件即可,在布局文件中,自定义view的名字就是自定义控件类的包名加上类名,假设定义CustomTextview类继承TextView,例子如下:

二、将系统提供的控件组合在一起

除了拓展原有的控件以外,我们还可以将控件组合成一个新的控件使用。首先,我们先定义一个新的布局文件,并把Imageview和Textview加入,代码如下。


 

然后我们定义一个类继承LinearLayout,在类的构造方法中对控件和布局进行初始化。

public void init(Context context) {
    //指定线性布局的显示方式,垂直
    setOrientation(VERTICAL);
    //设置用户期望的布局方式
    LayoutParams mLayoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    setLayoutParams(mLayoutParams);
    setGravity(Gravity.CENTER);
    setPadding(4, 4, 4, 4);
    //设置其布局文件
    View mButtOnbtnView= LayoutInflater.from(context).inflate(layout.botton_btn_view, this, true);
    mImageView = mButtonbtnView.findViewById(id.iv);
    mTextView = mButtonbtnView.findViewById(id.tv);
  }

接下来,它的使用方法就和拓展控件的方法一样了,直接在布局文件中,加入控件即可。

三、重写View来实现全新的控件

当系统原生的控件无法满足我们需求时,我们就可以定义一个新的控件来完成需要的功能。创建一个新的控件,需要继承View类,其难点主要在于绘制控件和实现交互。在继承View类时,我们还需要重写它的onDraw(),onMeasure()、onTouchEvent()来实现绘制、测量和触摸事件。

onDraw()绘制就是在canvas对象上调用其一系列方法进行绘图,绘制控件的形状。

onMeasure()

下面,我来讲讲onMeasure()。在绘制View之前,我们需要告诉系统我们需要画一个多大的View以及他的位置,这就是onMeasure()进行的了。首先,我们来了解一下测量的三种模式:

EXACTLY:精确值模式,在指定view具体数值的时候会用到。

AT_MOST:最大值模式,将控件设置为"wrap_content"用到,它会根据子控件或者内容变化而变化。

UNSPECIFIED:绘制控件想要多大就可以多大。

根据以上三种模式,我们就可以在测量的时候判断和使用了。首先,我们重写一个view的onMeasure()方法。再通过使用MeasureSpec类获得控件的测量模式。MeasureSpec使用的是位运算,其高2位为测量的模式,剩下的30位为测量的大小。

@Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
 
    if (widthMode == MeasureSpec.EXACTLY) {
 
    } else if (widthMode == MeasureSpec.AT_MOST) {
 
    } else if (widthMode == MeasureSpec.UNSPECIFIED) {
 
    }
 
  }

以上代码就是通过判断测量模式来给定义控件的大小,这里只是测量了控件的宽度,控件高度的测量也是类似的,就不在做详解。

前面说过,ViewGroup是用来管理控件的,当ViewGroup的大小为"wrap_content"时,它就会遍历其所有子View,来获得子View的大小,再来设置自身的大小。我们使用过的布局,像RelativeLayout,LinearLayout都是继承ViewGroup的,所以他们也是使用这种方法来获得自己的大小的。

onTouchEvent()

onTouchEvent()就是我们所说的触摸事件,由于Android手机是触屏的,所以我们自定义View在触摸屏幕的时候,也需要有一定的处理来完成交互。当重写onTouchEvent方法的时候,我们可以看到,需要传入MotionEvent的对象。我们可以通过这个类来设置触摸的事件,也可以获得触摸点的位置。我们可以通过getAction()来获取触摸事件的行动,来判断是否按下屏幕或者移动。在Android的坐标系中,我们都知道Android的屏幕在竖屏的时候,以左上角的位置为原点,向右为x轴的正方向,向下为y轴的正方向,知道了这个后,我们就可以通过调用getX()和getY()方法可以获取触摸点的坐标,来完成一些交互操作。

public boolean onTouchEvent(MotionEvent event) {
    float x;
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
      {
        x=event.getX();
      }
        break;
      case MotionEvent.ACTION_MOVE:
        break;
      case MotionEvent.ACTION_UP:
        break;
    }
    return true;
  }

以上就是自定义控件常用重写的方法,通过了重写这几个方法,我们基本就可以实现一个简易的自定义控件了。下面,我们来了解下控件的事件拦截机制的原理。

事件拦截机制分析

我们前面讲过,控件结构是树形结构,一个ViewGroup中可能有多个ViewGroup或者View,那么,触摸事件是怎么准确的分配给每个View和ViewGroup的呢。我们假设有一个ViewGroupA,在他的里面嵌套着ViewGroupB,而在ViewGroupB的里面,又嵌套着一个View。当我们重写ViewGroupA类的时候,就需要重写里面的这三个方法:

  1. dispatchTouchEvent()
  2. onInterceptTouchEvent()
  3. onTouchEvent()

而在重写View的时候,需要重写两个方法:

  1. dispatchTouchEvent()
  2. onTouchEvent()

可以根据名字看出,ViewGroup中比View多了onInterceptTouchEvent()方法,这个方法就是事件拦截的核心。在每一个方法中Log一下,再点击View的时候,就会发现方法调用的顺序:

首先,调用了ViewGroupA类的dispatchTouchEvent()和onInterceptTouchEvent()。

再调用了ViewGroupB类的dispatchTouchEvent()和onInterceptTouchEvent()。

再到View的dispatchTouchEvent()方法。

这个调用的顺序就是事件传递的顺序,而事件处理的顺序则是:

  • View的onTouchEvent()。
  • ViewGroupB的onTouchEvent()。
  • ViewGroupA的onTouchEvent()。

由此,可以看出,事件的分发是由上层的ViewGroup发布的,再逐层下发。而事件的处理,则是由下层的View处理后,再逐层上传。前面也说过,onInterceptTouchEvent()是事件拦截的核心,那么,只要设置它的返回值为true,就可以拦截事件,使其不再下发,而onTouchEvent()返回false,事件处理后就不会再上传。事件的分发和拦截的流程就大致讲解完成了。


推荐阅读
  • 深入解析Android自定义View面试题
    本文探讨了Android Launcher开发中自定义View的重要性,并通过一道经典的面试题,帮助开发者更好地理解自定义View的实现细节。文章不仅涵盖了基础知识,还提供了实际操作建议。 ... [详细]
  • 国内BI工具迎战国际巨头Tableau,稳步崛起
    尽管商业智能(BI)工具在中国的普及程度尚不及国际市场,但近年来,随着本土企业的持续创新和市场推广,国内主流BI工具正逐渐崭露头角。面对国际品牌如Tableau的强大竞争,国内BI工具通过不断优化产品和技术,赢得了越来越多用户的认可。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文将详细介绍如何使用剪映应用中的镜像功能,帮助用户轻松实现视频的镜像效果。通过简单的步骤,您可以快速掌握这一实用技巧。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • 如何在WPS Office for Mac中调整Word文档的文字排列方向
    本文将详细介绍如何使用最新版WPS Office for Mac调整Word文档中的文字排列方向。通过这些步骤,用户可以轻松更改文本的水平或垂直排列方式,以满足不同的排版需求。 ... [详细]
  • 本文总结了在使用Ionic 5进行Android平台APK打包时遇到的问题,特别是针对QRScanner插件的改造。通过详细分析和提供具体的解决方法,帮助开发者顺利打包并优化应用性能。 ... [详细]
  • 理解存储器的层次结构有助于程序员优化程序性能,通过合理安排数据在不同层级的存储位置,提升CPU的数据访问速度。本文详细探讨了静态随机访问存储器(SRAM)和动态随机访问存储器(DRAM)的工作原理及其应用场景,并介绍了存储器模块中的数据存取过程及局部性原理。 ... [详细]
  • 360SRC安全应急响应:从漏洞提交到修复的全过程
    本文详细介绍了360SRC平台处理一起关键安全事件的过程,涵盖从漏洞提交、验证、排查到最终修复的各个环节。通过这一案例,展示了360在安全应急响应方面的专业能力和严谨态度。 ... [详细]
  • 几何画板展示电场线与等势面的交互关系
    几何画板是一款功能强大的物理教学软件,具备丰富的绘图和度量工具。它不仅能够模拟物理实验过程,还能通过定量分析揭示物理现象背后的规律,尤其适用于难以在实际实验中展示的内容。本文将介绍如何使用几何画板演示电场线与等势面之间的关系。 ... [详细]
  • 本文介绍如何通过Windows批处理脚本定期检查并重启Java应用程序,确保其持续稳定运行。脚本每30分钟检查一次,并在需要时重启Java程序。同时,它会将任务结果发送到Redis。 ... [详细]
author-avatar
WINGZERO哥哥
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有