自定义View-渐变TextView(重点:绘制文本)

源码链接

夸克网盘分享

效果展示


分析

  • 动态效果,使用Animator实现
  • 自定义View
    • 继承TextView
    • 使用TextView的测量,不重写
    • 使用TextView的布局,不重写
    • 绘制-重写绘制
      • 使用两种颜色绘制文本
      • 颜色占比不同,百分比从0~1

实现

自定义属性

从需求可知,需要定义两种颜色。

  • 在attrs.xml中使用declare-styleable标签声明属性。

  • 自定义View.java中使用TypedArray获取xml属性值(在构造函数中获取)

        注意:获取值后,需要使用typedArray.recycle()回收。

  • xml中使用自定义View及其自定义属性


创建画笔

绘制,就是在画布上用画笔绘制内容。

要绘制两种颜色的文字,需要两支画笔,画笔有不同的颜色和大小,使用颜色的大小创建画笔。


绘制文字

画布Canvas类中提供了绘制文字的方法。

text:要绘制的文本。--继承自TextView,通过getText().toString()获取。

x:绘制文本水平方向的开始位置。 -- 可以从0开始,

y:文字的基线baseline。

paint:画笔(颜色、大小)-- 已创建

  • 什么是基线?

来源于四线三格。

基线就是第三条线。

  • Paint.FontMetrics类说明

Paint.FontMetrics类说明,通过paint.getFontMetrics()获取。

红色框是mPaint.getTextBounds(textStr,0,textStr.length(),rect1),获取文字的Rect。就是图中的Rect r。

蓝色的框是文字真实大小,去除padding的。

通过paint.getFontMetrics()获取的是图中FontMetricInt的值,都是以Baseline为起点获取的值,向上是负值(top),向下是正值(bottom)。

  • 基线的计算方法?

获取基线的方式有多种,此处使用FontMetrics的top、bottom以及getHeight()获取基线。

先获取下图dy的高度,然后使用高度中线(即图中红线的位置 高度/2)+dy获取基线的坐标。

获取dy的方法:float dy = (fontMetrics.bottom-fontMetrics.top)/2-fontMetrics.bottom

基线位置1:baseline = getHeight()/2+dy 注意:此方法文本永远在TextView中居中,即使paddingTop和paddingBottom值不一致的时候。

基线位置2:float baseline = (fontMetrics.bottom-fontMetrics.top)/2+dy+getPaddingTop(); 此方法考虑了paddingTop和paddingBottom值不一致的时候


裁剪画布-如何绘制部分文字

那么如何将文字分为两部分绘制,且使用不同的画笔。

裁剪画布,在onDraw(Canvas canvas)中是的画布大小是View所能使用的整个区域,要分左右两部分绘制,那就通过裁剪画布的方式实现,将画布裁剪为左右两部分,分别使用不同的画笔绘制。

  • 裁剪画布前先创建画布副本 canvas.save(); 这样画布可以恢复。
  • 创建画布副本后,绘制的内容,在画布清空后,已绘制的内容仍然生效。
  • 画布可以分层
  • 超出画布大小的部分,即使绘制,也不显示。
  • 清空画布 canvas.restore();

//两种颜色各绘制一半
int point = (int) (getWidth()*0.5f);
drawText(canvas, mChangePaint, 0, point);
drawText(canvas, mOriginalPaint, point, getWidth());/*** 绘制文本* 创建画布副本* 裁剪画布* 绘制文本 只能绘制画布大小,超出的部分不绘制,画布清空后已绘制的内容仍存在* 清空画布 恢复画布大小/清空画布内容*/
private void drawText(Canvas canvas, Paint paint,int start,int end) {//创建画布副本canvas.save();//将画布裁剪为Rect大小Rect rect = new Rect(start,0,end,getHeight());canvas.clipRect(rect);String text = getText().toString();//判空if(TextUtils.isEmpty(text)) return;//绘制文本 计算baselinefloat dy = 0;Paint.FontMetrics fontMetrics = paint.getFontMetrics();dy = (fontMetrics.bottom-fontMetrics.top)/2-fontMetrics.bottom;float baseline = getHeight()/2+dy;canvas.drawText(text,0,baseline,paint);//将画布清空(内容和大小)canvas.restore();
}

设置颜色占比

需将两种颜色绘制文字的占比,能自定义,范围是0~1,动态设置占比。、

注意:在设置后,需要刷新重绘,否则不生效。


设置颜色变化方向

为了实现从左到右变化,和从右到左变化,需要设置方向。


动画实现动效

使用动画,实现颜色占比0~1的变化。


源码

package com.learn.customui.custom;import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.animation.LinearInterpolator;
import android.widget.TextView;import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;import com.learn.customui.R;public class ColorChangeTextView extends AppCompatTextView {//原始颜色画笔private Paint mOriginalPaint;//结束颜色画笔private Paint mChangePaint;//颜色占比private float mCurrentProgress = 0f;//方向private Direction mDirection = Direction.LEFT_TO_RIGHT;public enum Direction{LEFT_TO_RIGHT,RIGHT_TO_LEFT}public ColorChangeTextView(Context context) {this(context,null);}public ColorChangeTextView(Context context, @Nullable AttributeSet attrs) {this(context, attrs,0);}public ColorChangeTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context,attrs);}//初始化View 获取属性值private void init(Context context,AttributeSet attrs) {TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ColorChangeTextView);int originalColor = typedArray.getColor(R.styleable.ColorChangeTextView_originalColor, Color.BLACK);int changeColor = typedArray.getColor(R.styleable.ColorChangeTextView_changeColor,Color.BLACK);mOriginalPaint = getPaint(originalColor);mChangePaint = getPaint(changeColor);//一定要回收?为什么?typedArray.recycle();}//创建Paint 颜色、大小private Paint getPaint(int color){Paint paint = new Paint();paint.setColor(color);paint.setTextSize(getTextSize());return paint;}@Overrideprotected void onDraw(Canvas canvas) {int point = (int) (getWidth()*mCurrentProgress);if(mDirection == Direction.LEFT_TO_RIGHT) {drawText(canvas, mChangePaint, 0, point);drawText(canvas, mOriginalPaint, point, getWidth());}else if(mDirection == Direction.RIGHT_TO_LEFT){drawText(canvas, mOriginalPaint, 0, getWidth()-point);drawText(canvas, mChangePaint, getWidth()-point, getWidth());}}/*** 绘制文本* 创建画布副本* 裁剪画布* 绘制文本 只能绘制画布大小,超出的部分不绘制,画布清空后已绘制的内容仍存在* 清空画布 恢复画布大小/清空画布内容*/private void drawText(Canvas canvas, Paint paint,int start,int end) {//创建画布副本canvas.save();//将画布裁剪为Rect大小Rect rect = new Rect(start,0,end,getHeight());canvas.clipRect(rect);String text = getText().toString();//判空if(TextUtils.isEmpty(text)) return;//绘制文本 计算baselinefloat dy = 0;Paint.FontMetrics fontMetrics = paint.getFontMetrics();dy = (fontMetrics.bottom-fontMetrics.top)/2-fontMetrics.bottom;float baseline = getHeight()/2+dy;canvas.drawText(text,0,baseline,paint);//将画布清空(内容和大小)canvas.restore();}//设置颜色改变点public void setCurrentProgress(float mCurrentProgress) {this.mCurrentProgress = mCurrentProgress;//刷新 重绘 否则动画不生效invalidate();}//设置颜色改变方向public void setDirection(Direction mDirection) {this.mDirection = mDirection;}//设置动画 实现动态改变文字颜色的过程public void setAnimator(ColorChangeTextView.Direction direction){setDirection(direction);ValueAnimator valueAnimator = ValueAnimator.ofFloat(0,1);//值变化范围valueAnimator.setDuration(2000);//时长2svalueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float currentProgress = (float)animation.getAnimatedValue();setCurrentProgress(currentProgress);}});valueAnimator.start();}
}
package com.learn.customui;import androidx.appcompat.app.AppCompatActivity;import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.TextView;import com.learn.customui.custom.ColorChangeTextView;public class MainActivity extends AppCompatActivity {ColorChangeTextView tv;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv = (ColorChangeTextView) findViewById(R.id.tv);}public void leftToRight(View view) {tv.setAnimator(ColorChangeTextView.Direction.LEFT_TO_RIGHT);}public void rightToLeft(View view) {tv.setAnimator(ColorChangeTextView.Direction.RIGHT_TO_LEFT);}}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><com.learn.customui.custom.ColorChangeTextViewandroid:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:background="@color/red"android:paddingTop="10dp"android:paddingBottom="5dp"android:text="中华人民共和国"app:originalColor="@color/black"app:changeColor="@color/red" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="左到右"android:onClick="leftToRight"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="右到左"android:onClick="rightToLeft"/></LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="ColorChangeTextView"><attr name="originalColor" format="color"/><attr name="changeColor" format="color"/></declare-styleable></resources>

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

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

相关文章

论文发表作图必备:训练结果对比,多结果绘在一个图片【Precision】【Recall】【mAP0.5】【mAP0.5-0.95】【loss】

前言:Hello大家好,我是小哥谈。YOLO(You Only Look Once)算法是一种目标检测算法,它可以在图像中实时地检测和定位目标物体。YOLO算法通过将图像划分为多个网格,并在每个网格中检测目标物体,从而实现快速的目标检测。本文所介绍的作图教程适用于所有YOLO系列版本算法,接…

Go泛型详解

引子 如果我们要写一个函数分别比较2个整数和浮点数的大小&#xff0c;我们就要写2个函数。如下&#xff1a; func Min(x, y float64) float64 {if x < y {return x}return y }func MinInt(x, y int) int {if x < y {return x}return y }2个函数&#xff0c;除了数据类…

Idea在线搜索Maven依赖-好用工具分享

maven_search 等价于网页搜索maven依赖&#xff0c;非常方便快捷 下载安装后&#xff0c;使用&#xff1a; 点击上方Tools Maven Search 或者快捷键 Ctrl Shift M 最后选择依赖&#xff0c;复制即可

Vue 3 与 TypeScript:最佳实践详解

大家好,我是CodeQi! 很多人问我为什么要用TypeScript? 因为 Vue3 喜欢它! 开个玩笑... 在我们开始探索 Vue 3 和 TypeScript 最佳实践之前,让我们先打个比方。 如果你曾经尝试过在没有 GPS 的情况下开车到一个陌生的地方,你可能会知道那种迷失方向的感觉。 而 Typ…

昇思学习打卡-17-热门LLM及其他AI应用/基于MobileNetv2的垃圾分类

文章目录 网络介绍读取数据集训练训练策略模型保存损失函数优化器模型训练 网络介绍 MobileNetv2专注于移动端、嵌入式或IoT设备的轻量级CNN网络。MobileNet网络使用深度可分离卷积&#xff08;Depthwise Separable Convolution&#xff09;的思想在准确率小幅度降低的前提下&…

分享一款嵌入式开源LED指示灯控制代码框架cotLed

一、工程简介 cotLed是一款轻量级的LED控制软件框架&#xff0c;可以十分方便地控制及自定义LED的各种状态&#xff0c;移植方便&#xff0c;无需修改&#xff0c;只需要在初始化时实现单片机硬件GPIO初始化&#xff0c;同时为框架接口提供GPIO写函数即可。 框架代码工程地址&a…

Apache Dubbo与Nacos整合过程

Dubbo服务发现 Dubbo 提供的是一种 Client-Based 的服务发现机制&#xff0c;依赖第三方注册中心组件来协调服务发现过程&#xff0c;支持常用的注册中心如 Nacos、Consul、Zookeeper 等。 以下是 Dubbo 服务发现机制的基本工作原理图&#xff1a; 服务发现包含提供者、消费者…

LabVIEW中使用 DAQmx Connect Terminals作用意义

该图展示了如何在LabVIEW中使用 DAQmx Connect Terminals.vi 将一个信号从一个源端口连接到一个目标端口。这种处理有以下几个主要目的和作用&#xff1a; 同步操作&#xff1a; 在多任务、多通道或多设备系统中&#xff0c;可能需要不同的组件在同一时刻执行某些操作。通过将触…

redis相关知识记录

redis基本数据类型 Redis⽀持五种主要数据结构&#xff1a;字符串&#xff08;Strings&#xff09;、列表&#xff08;Lists&#xff09;、哈希表&#xff08;Hashes&#xff09;、集合&#xff08;Sets&#xff09;和有序集合&#xff08;Sorted Sets&#xff09;。这些数据结…

winform4

json using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; //导入json第三方库 使用nuget搜索 …

断电的固态硬盘数据能放多久?

近日收到一个网友的提问&#xff0c;在这里粗浅表达一下见解&#xff1a; “网传固态硬盘断电后数据只能放一年&#xff0c;一年之后就会损坏。但是我有一个固态硬盘已经放了五六年了&#xff08;上次通电还是在2018年左右&#xff0c;我读初中的时候&#xff09;&#xff0c;…

《长相思》第二季回归:好剧质量,永恒的王牌

在万千剧迷的翘首以盼中&#xff0c;《长相思》第二季终于携着前作的辉煌与期待&#xff0c;缓缓拉开了序幕。这部自播出以来便以其精湛的剧情、出色的演员阵容以及独到的宣传策略&#xff0c;赢得了广泛好评与持续关注。如今&#xff0c;第二季的回归&#xff0c;无疑再次证明…

Linux 初识

目录 ​编辑 1.Linux发展史 1.1UNIX发展历史 1.2Linux发展历史 2.Linux的开源属性 2.1 开源软件的定义 2.2 Linux的开源许可证 2.3 开源社区与协作 3.Linux的企业应用现状 3.1 服务器 3.1.1 Web服务器 3.1.2 数据库服务器 3.1.3 文件服务器 3.1.4 电子邮件服务器 …

某客户管理系统Oracle RAC节点异常重启问题详细分析记录

一、故障概述 某日10:58分左右客户管理系统数据库节点1所有实例异常重启&#xff0c;重启后业务恢复正常。经过分析发现&#xff0c;此次实例异常重启的是数据库节点1。 二、故障原因分析 1、数据库日志分析 从节点1的数据库日志来看&#xff0c;10:58:49的时候数据库进程开始…

新火种AI|微软和苹果放弃OpenAI董事会观察员席位

作者&#xff1a;一号 编辑&#xff1a;美美 微软苹果双双不做OpenAI“观察员”&#xff0c;OpenAI能更自由吗&#xff1f; 7月10消息&#xff0c;微软当地时间周一宣布将放弃在OpenAI董事会的观察员席位&#xff0c;他们称&#xff0c;OpenAI在过去八个月中取得了“重大进展…

国内的几款强大的智能—AI语言模型

AI 绘图 链接&#xff1a;点我进入 1、国内百度研发的&#xff0c;文心一言&#xff1a; https://yiyan.baidu.com/welcome 大家如果像我的界面一样有【开始体验】就是可以使用的&#xff0c;否则就是说明在等待中&#xff01; 优点&#xff1a;会画画&#xff0c;暂无次数限…

回归树模型

目录 一、回归树模型vs决策树模型&#xff1a;二、回归树模型的叶结点&#xff1a;三、如何决定每个非叶结点上的特征类型&#xff1a; 本文只介绍回归树模型与决策树模型的区别。如需了解完整的理论&#xff0c;请看链接&#xff1a;决策树模型笔记 一、回归树模型vs决策树模…

Linux设备驱动的并发控制

一、概述 Linux设备驱动中必须解决的一个问题就是多个进程对共享资源(如全局变量、静态变量、硬件资源等)的并发访问&#xff0c;会导致竟态&#xff0c;如可能会出现以下情况&#xff1a;导致执行单元C独处的数据不符合预期 导致竟态发生有如下几种情况&#xff1a; 对称多处…

int类型变量表示范围的计算原理

文章目录 1. 了解2. 为什么通常情况下int类型整数的取值范围是-2147483648 ~ 21474836473. int类型究竟占几个字节4. 推荐 1. 了解 通常情况下int类型变量占4个字节&#xff0c;1个字节有8位&#xff0c;每位都有0和1两种状态&#xff0c;所以int类型变量一共可以表示 2^32 种状…

【全面介绍Oracle】

🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步! 目录 🎥前言🎥基本概念和安装🎥SQL语言🎥PL/SQL编程🎥数据库…