Android自定义view流程

news2024/10/7 4:24:27

Android自定义view流程,主要目的是总结实现过程中的思路以及一些需要注意的地方。 首先,我们先来看一张效果图:
在这里插入图片描述

实现逻辑

  • 重新指定View宽高
  • 绘制外圆圆弧背景及进度
  • 绘制中圆圆弧背景及进度
  • 绘制内圆圆弧背景及进度

知识点

onMeasure

  • 用于测量View的大小。创建时View无需测量,当将这个View放入一个容器(父控件)时候才需要测量,而测量方法由父控件调用。当控件的父控件要放置该控件的时候,父控件会调用子控件的onMeasure方法确定子控件需要的空间大小,然后传入widthMeasureSpec和heightMeasureSpec来告诉子控件可获得的空间大小,子控件通过这两个参数就可以测量自身的宽高了。

setMeasuredDimension

  • 用于重新设置View宽高

Canvas#drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

  • 绘制以oval为边界的圆弧

onDraw

  • 用来确定View长什么样。onDraw绘制过程如下:
    1. Draw the background(绘制背景)
    2. If necessary, save the canvas’ layers to prepare for fading(如果需要,为保存这层为边缘的滑动效果作准备)
    3. Draw view’s content(绘制内容)
    4. Draw children(绘制子View)
    5. If necessary, draw the fading edges and restore layers(如果需要,绘制边缘效果并且保存图层)
    6. Draw decorations (scrollbars for instance)(绘制边框,比如scrollbars,TextView)

主要代码

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // 根据父控件传递的widthMeasureSpec和heightMeasureSpec调用MeasureSpec.getSize测量自身宽高
    int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
    int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
    int finalWidth = measureWidth;
    int finalHeight = measureHeight;
    // 根据自身宽高重新计算新的宽高,使新的宽高比为2:1
    if (measureWidth >= measureHeight * 2) {
        finalWidth = measureHeight * 2;
    } else {
        finalHeight = measureWidth / 2;
    }
    // 设置View新的宽高
    setMeasuredDimension(finalWidth, finalHeight);
}
/**
 * 绘制圆弧
 * @param canvas
 * @param progress 进度
 * @param color 进度颜色
 * @param radius 圆弧半径
 */
private void drawArc(Canvas canvas, float progress, int color, float radius){
    // 圆心
    mXCenter = getWidth() / 2;
    mYCenter = getHeight() ;

    mPaint.setColor(mBackgroundArcColor);
    // 构造边界矩形
    RectF oval = new RectF();
    oval.left = (mXCenter - radius);
    oval.top = (mYCenter - radius);
    oval.right = mXCenter + radius;
    oval.bottom = radius * 2 + (mYCenter - radius);
    //绘制圆弧背景
    canvas.drawArc(oval, -180, 180, false, mPaint);

    //绘制圆弧进度
    float showDegree = progress / 100 * 180;
    mPaint.setColor(color);
    canvas.drawArc(oval, -180, showDegree, false, mPaint);
}
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    // 初始半径
    float originalRadius = getWidth() * .5f;
    // 画笔半宽
    float halfArcStokeWidth = mArcStrokeWidth * .5f;

    // 外圆环半径=初始半径-画笔半宽
    float outSideArcRadius = originalRadius - halfArcStokeWidth;
    drawArc(canvas, mOutsideProgress, mOutsideArcColor, outSideArcRadius);

    // 中圆环半径=外圆的半径-圆环偏移值-画笔半宽
    float middleArcRadius = outSideArcRadius - mArcOffset - halfArcStokeWidth;
    drawArc(canvas, mMiddleProgress, mMiddleArcColor, middleArcRadius);

    // 内圆环半径=中圆的半径-圆环偏移值-画笔半宽
    float insideArcRadius = middleArcRadius - mArcOffset - halfArcStokeWidth;
    drawArc(canvas, mInsideProgress, mInsideArcColor, insideArcRadius);
}

全部代码

ThreeArcView.java

public class ThreeArcView extends View {

    //圆弧画笔
    private Paint mPaint;
    //背景圆环颜色
    private int mBackgroundArcColor;
    //外圆环颜色
    private int mOutsideArcColor;
    //中圆环颜色
    private int mMiddleArcColor;
    //内圆环颜色
    private int mInsideArcColor;
    //外圆展示弧度
    private float mOutsideProgress;
    //中圆展示弧度
    private float mMiddleProgress;
    //内圆展示弧度
    private float mInsideProgress;
    //圆弧宽度
    private float mArcStrokeWidth;
    //圆偏移值
    private float mArcOffset;

    // 圆心x坐标
    private int mXCenter;
    // 圆心y坐标
    private int mYCenter;

    public ThreeArcView(Context context) {
        this(context, null);
    }

    public ThreeArcView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ThreeArcView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttrs(context, attrs);
        initVariable();
    }

    private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray typeArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ThreeArcView, 0, 0);
        mArcStrokeWidth = typeArray.getDimension(R.styleable.ThreeArcView_ts_strokeWidth, dp2px(context, 20));
        // 圆环背景颜色
        mBackgroundArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_bgArcColor, 0xFFFFFFFF);
        // 圆环颜色
        mOutsideArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_outsideBgColor, 0xFFFFFFFF);
        mMiddleArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_middleBgColor, 0xFFFFFFFF);
        mInsideArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_insideBgColor, 0xFFFFFFFF);
        // 圆进度
        mOutsideProgress = typeArray.getFloat(R.styleable.ThreeArcView_ts_outsideProgress, 0f);
        mMiddleProgress = typeArray.getFloat(R.styleable.ThreeArcView_ts_middleProgress, 0f);
        mInsideProgress = typeArray.getFloat(R.styleable.ThreeArcView_ts_insideProgress, 0f);
        // 圆环偏移值
        mArcOffset = typeArray.getDimension(R.styleable.ThreeArcView_ts_radiusOffset, dp2px(context, 20));
        typeArray.recycle();

        // 偏移值不能小于画笔宽度的一半,否则会发生覆盖
        if (mArcOffset < mArcStrokeWidth / 2){
            mArcOffset = mArcStrokeWidth / 2;
        }
    }

    private void initVariable() {
        //背景圆弧画笔设置
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mArcStrokeWidth);
        mPaint.setStrokeCap(Paint.Cap.ROUND);//开启显示边缘为圆形
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 分别获取期望的宽度和高度,并取其中较小的尺寸作为该控件的宽和高
        int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
        //裁剪出一个 (宽:高) = (2:1) 的矩形
        int finalWidth = measureWidth;
        int finalHeight = measureHeight;
        if (measureWidth >= measureHeight * 2) {
            finalWidth = measureHeight * 2;
        } else {
            finalHeight = measureWidth / 2;
        }
        setMeasuredDimension(finalWidth, finalHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 初始半径
        float originalRadius = getWidth() * .5f;
        // 画笔半宽
        float halfArcStokeWidth = mArcStrokeWidth * .5f;

        // 外圆环半径=初始半径-画笔半宽
        float outSideArcRadius = originalRadius - halfArcStokeWidth;
        drawArc(canvas, mOutsideProgress, mOutsideArcColor, outSideArcRadius);

        // 中圆环半径=外圆的半径-圆环偏移值-画笔半宽
        float middleArcRadius = outSideArcRadius - mArcOffset - halfArcStokeWidth;
        drawArc(canvas, mMiddleProgress, mMiddleArcColor, middleArcRadius);

        // 内圆环半径=中圆的半径-圆环偏移值-画笔半宽
        float insideArcRadius = middleArcRadius - mArcOffset - halfArcStokeWidth;
        drawArc(canvas, mInsideProgress, mInsideArcColor, insideArcRadius);
    }

    /**
     * 绘制圆弧
     * @param canvas
     * @param progress 进度
     * @param color 进度颜色
     * @param radius 圆弧半径
     */
    private void drawArc(Canvas canvas, float progress, int color, float radius){
        // 圆心
        mXCenter = getWidth() / 2;
        mYCenter = getHeight() ;

        mPaint.setColor(mBackgroundArcColor);
        // 构造边界矩形
        RectF oval = new RectF();
        oval.left = (mXCenter - radius);
        oval.top = (mYCenter - radius);
        oval.right = mXCenter + radius;
        oval.bottom = radius * 2 + (mYCenter - radius);
        //绘制圆弧背景
        canvas.drawArc(oval, -180, 180, false, mPaint);

        //绘制圆弧进度
        float showDegree = progress / 100 * 180;
        mPaint.setColor(color);
        canvas.drawArc(oval, -180, showDegree, false, mPaint);
    }

    private void setOutSideProgress(float progress){
        this.mOutsideProgress = progress;
        postInvalidate();
    }

    private void setMiddleProgress(float progress){
        this.mMiddleProgress = progress;
        postInvalidate();
    }

    private void setInsideProgress(float progress){
        this.mInsideProgress = progress;
        postInvalidate();
    }

    public void setProgress(float outSideProgress, float middleProgress, float insideProgress) {
        mOutsideProgress = outSideProgress;
        mMiddleProgress = middleProgress;
        mInsideProgress = insideProgress;
        postInvalidate();
    }

    public int dp2px(Context context, float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }

    public int sp2px(Context context, float spValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }

    public int px2sp(Context context, float pxValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }
}

styes.xml

<declare-styleable name="ThreeArcView">
    <!-- 画笔宽度 -->
    <attr name="ts_strokeWidth" format="dimension" />
    <!-- 圆弧背景色 -->
    <attr name="ts_bgArcColor" format="color" />
    <!-- 外圆进度颜色 -->
    <attr name="ts_outsideBgColor" format="color" />
    <!-- 中圆进度颜色 -->
    <attr name="ts_middleBgColor" format="color" />
    <!-- 内圆进度颜色 -->
    <attr name="ts_insideBgColor" format="color" />
    <!-- 外圆进度 -->
    <attr name="ts_outsideProgress" format="float" />
    <!-- 中圆进度 -->
    <attr name="ts_middleProgress" format="float" />
    <!-- 内圆进度 -->
    <attr name="ts_insideProgress" format="float" />
    <!-- 圆偏移值 -->
    <attr name="ts_radiusOffset" format="dimension" />
</declare-styleable>

OK,本文到此结束~

最后这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶资料》,帮助大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。

由于文章内容比较多,篇幅有限,资料已经被整理成了PDF文档,有需要《Android八大模块进阶资料》完整文档的可以加微信 即可免费领取!

PS:(文末还有使用ChatGPT机器人小福利哦!!大家不要错过)

《Android八大模块进阶笔记》

在这里插入图片描述

相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

一、源码解析合集

在这里插入图片描述

二、开源框架合集

在这里插入图片描述

同时这里还搭建了一个基于chatGPT的微信群聊机器人,24小时为大家解答疑难技术问题

图片

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/952055.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

读SQL学习指南(第3版)笔记08_视图和索引

1. 精心设计的应用程序通常会在保持实现细节私有的同时公开公有接口&#xff0c;以便未来在不影响最终用户的情况下修改设计 2. 视图 2.1. 不同于数据表&#xff0c;视图并不涉及数据存储&#xff0c;不用担心视图会填满你的磁盘空间 2.2. 一种数据查询机制 2.3. 从用户的视…

基于swing的中国象棋java小游戏jsp源代码Mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、主要功能 可以实现双人下棋&#xff0c;可以悔棋&#xff0c;可…

R语言其他相关函数(各函数解析含实例,可供查询)

目录 一.函数相关 1.函数定义&#xff1a;function 2.调用文件:source 3. Call 4.Recall 5.browser 6.debug和undebug 7.trace 8.traceback 9.options 10.missing 11.nargs 12.stop 13.指定退出时执行的表达式 14.expression和eval 15.system.time 16. invisi…

Springboot+mybatis-plus+dynamic-datasource 切换数据源失败问题总结

Springbootmybatis-plusdynamic-datasourceDruid 多数据源 切换数据源失败总结 文章目录 Springbootmybatis-plusdynamic-datasourceDruid 多数据源 切换数据源失败总结0.前言1. dynamic-datasource 切换数据源失败场景总结1. spring-batch整合情况下切换数据源异常解决办法&am…

Matlab图像处理-图像缩放

基本概念 图像缩放是指将给定的图像在x轴方向按比例缩放a倍&#xff0c;在y轴方向按比例缩放b倍&#xff0c;从而获得一幅新的图像。 如果ab&#xff0c;即在x轴方向和y轴方向缩放的比率相同&#xff0c;则称这样的比例缩放为图像的全比例缩放。 如果a≠b&#xff0c;图像比…

Spring: HiddenHttpMethodFilter的用法及原理

作用&#xff1a;将html表单提交的post请求转换为put请求或delete请求发给接口。 html不支持put和delete. 一、web.xml中配置过滤器 <filter><filter-name>HiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenH…

认识SQL sever

目录 一、数据库的概念 1.1数据库的基本概念 1.2对数据库的了解 二、数据库的分类 2.1关系型数据库&#xff08;RDBMS&#xff09;&#xff1a; 2.2非关系型数据库&#xff08;NoSQL&#xff09;&#xff1a; 2.3混合数据库&#xff1a; 2.4数据仓库&#xff1a; 2.5嵌…

二叉树中的堆

堆的概念和结构 大堆&#xff1a; 树中的任何一个父亲都大于等于孩子 小堆&#xff1a; 树中的任何一个父亲都小于等于孩子 堆在逻辑上是二叉树来存储的&#xff0c;就是在我们的想象中他是按二叉树来存储的&#xff0c;但是在实际上&#xff0c;它是以数组的形式来存储的&…

软考A计划-网络工程师-复习背熟-路由器与交换配置和网络安全

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

移动端如何适配不同的屏幕尺寸

在移动端开发中&#xff0c;适配不同的屏幕尺寸是一个重要的考虑因素。以下是一些常用的方法来实现移动端的屏幕适配&#xff1a; 使用响应式布局&#xff1a;使用CSS媒体查询和弹性布局来根据屏幕尺寸调整页面布局和元素大小。通过设置百分比、em或rem单位来实现元素的相对大小…

ICCV 2023 | 港中文MMLab: 多帧光流估计模型VideoFlow,首次实现亚像素级别误差

本文提出了一个多帧光流估计模型 VideoFlow&#xff0c;旨在充分挖掘视频中的时序信息和运动规律&#xff0c;避免当前主流方法只以两帧图片作为输入而面临的信息瓶颈&#xff0c;显著提升了光流估计的性能。 在公开的 Sintel Bechmark 上&#xff0c;VideoFlow 在 Clean 和 Fi…

过期订单关闭

由于Redis具有过期监听的功能&#xff0c;于是就有人拿它来实现过期订单关闭&#xff0c;但是这个方案并不完美。今天来聊聊11种实现订单定时关闭的方案&#xff0c;总有一种适合你&#xff01; 在电商、支付等系统中&#xff0c;一般都是先创建订单&#xff08;支付单&#x…

七牛云OSS存储

前言: 七牛云的存储项目的附件,需要开发一套七牛云的工具类,可以使用该工具类进行七牛云服务器进行文件的上传与下载操作; 七牛云的文档学习: 相关的依赖项的配置: <dependency><groupId>com.amazonaws</groupId><artifactId>aws-java-sdk-s3…

如何改善客户服务?

提供优质的客户服务是经营成功企业的一个重要方面。满意的客户更有可能成为回头客&#xff0c;并向他人推荐你的产品或服务。在当今竞争激烈的市场中&#xff0c;良好的客户服务可能是一家繁荣的企业与一家艰难生存的企业的区别。今天&#xff0c;我们就改善客户服务6个行之有效…

面试官眼中的理想候选人:如何成为他们的首选

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

测试理论与方法----测试流程第五个步骤:测试总结报告

10、通用测试技术 1、测试总结报告概述 定义&#xff1a;把测试过程和结果整理成文档&#xff0c;对发现的问题和缺陷进行分析&#xff0c;为纠正软件的质量提供依据&#xff0c;同时为后续的验收和交付打下基础。测试报告是测试阶段最后的产出&#xff0c;一份详细的测试报告…

【已解决】为什么RAR压缩包里的文件无法删除?

小伙伴们是否遇到过这样的问题&#xff1f;打开RAR压缩包&#xff0c;想要删除里面的文件却发现无法删除&#xff0c;提示“不支持当前操作”。 其实&#xff0c;这是文件在压缩的时候设置了“锁定压缩文件”&#xff0c;设置后&#xff0c;除了无法删除压缩包里的文件&#xf…

控制ATOS/HYDAC/VICKERS比例插装阀线圈放大器

驱动各种国产或进口品牌开环控制比例插装式阀的线圈&#xff0c;如SUN/ATOS/HYDAC/VICKERS/REXROTH/HYDRAFORCE等品牌比例插装式方向阀、流量阀、压力阀、叠加阀等。 比例放大器参数&#xff1a; 电源24VDC标准&#xff1b; 输入指令0-10V、4-20 mA、0-5V可选&#xff1b; …

线上排查定位OOM问题的记录

整体思路&#xff1a;把机器上的快照文件下载到本地电脑&#xff0c;用eclipse analyzer打开它&#xff0c;找到是哪一行导致oom。 注&#xff1a;日志里看不到报错信息是因为 还没有来得及记录到日志文件&#xff0c;就down了。 把该文件下载到本地电脑&#xff0c;可以借助x…

八、代理模式

一、什么是代理模式 代理模式的定义&#xff1a;由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时&#xff0c;访问对象不适合或者不能直接引用目标对象&#xff0c;代理对象作为访问对象和目标对象之间的中介。 代理模式的主要角色如下: 抽象主题&#xff08…