Android开发:自定义TabLayout,神奇效果竟是如此简单

news2024/11/20 20:32:29

此时有的小伙伴可能会想,自定义view太麻烦,不如让UI直接把这个弧度切出来,不是一共就三个tab项嘛,Textview,ImageView… 这样横着排起来,然后做适当的显示和隐藏不就行啦。

是的,确实是可以,但是作为一个优秀的开发人员。我们还是要优选自定义view滴。

思路分析

================================================================

(1) 由效果图,我们很容易分析出,不管tab项一共几个,无非就这三种情况。

草图如下:

(2) 无论是哪种情况,首先我们需要画出一个背景矩形,这个比较简单。

(3)下面就是曲线的画法,草图中也已经标注了,很明显我需要2个控制点,那就需要用到三阶贝塞尔曲线啦!cubicTo

(4)画出图形后,点击事件如何响应处理呢?我们在 onTouchEvent 是能获取到点击控件后x,y坐标的,判断x的坐标是在哪个tab项的范围内,我们就认为点击了那个tab项,就可以了。

(5)控件的圆角的如何实现呢?canvas的 范围裁切 就可以啦。

代码实现

================================================================

思路梳理好了,那我们就写代码吧:

情况一(关键代码):

//最左边的图形

Path pathLeft = new Path();

pathLeft.lineTo(textWidth, 0);

pathLeft.cubicTo(textWidth + arcControlX, arcControlY, textWidth + arcWidth - arcControlX, viewHeight - arcControlY, textWidth + arcWidth, viewHeight);

pathLeft.lineTo(0, viewHeight);

pathLeft.lineTo(0, 0);

paint.setColor(selectColor);

canvas.drawPath(pathLeft, paint);

步骤说明:

  1. 首先我们的起始点坐标是(0,0),到坐标(textWidth, 0) 画一条直线;

  2. 然后三阶贝塞尔曲线,2个控制点的坐标1(textWidth + arcControlX,arcControlY),坐标2(textWidth + arcWidth - arcControlX,viewHeight - arcControlY),结束的坐标(textWidth + arcWidth,viewHeight);

  3. 最后画直线到坐标点(0, viewHeight),再到最终的原点(0, 0)。

到这里一个封闭的路径,就画好啦。情况二和情况三也是同样的道理,我也不再废话了。

情况二(关键代码):

//中间的图形

Path pathCenter = new Path();

pathCenter.moveTo(tabPosition * textWidth + tabPosition * arcWidth, 0);

pathCenter.cubicTo(tabPosition * textWidth + tabPosition * arcWidth - arcControlX, arcControlY, tabPosition * textWidth + tabPosition * arcWidth - arcWidth + arcControlX, viewHeight - arcControlY, tabPosition * textWidth + tabPosition * arcWidth - arcWidth, viewHeight);

pathCenter.lineTo(tabPosition * textWidth + tabPosition * arcWidth + textWidth + arcWidth, viewHeight);

pathCenter.cubicTo(tabPosition * textWidth + tabPosition * arcWidth + textWidth + arcWidth - arcControlX, viewHeight - arcControlY, tabPosition * textWidth + tabPosition * arcWidth + textWidth + arcControlX, arcControlY, tabPosition * textWidth + tabPosition * arcWidth + textWidth, 0);

pathCenter.lineTo(tabPosition * textWidth + tabPosition * arcWidth, 0);

paint.setColor(selectColor);

canvas.drawPath(pathCenter, paint);

情况三(关键代码):

//最右边的图形

Path pathRight = new Path();

pathRight.moveTo(viewWidth, 0);

pathRight.lineTo(viewWidth - textWidth, 0);

pathRight.cubicTo(viewWidth - textWidth - arcControlX, arcControlY, viewWidth - textWidth - arcWidth + arcControlX, viewHeight - arcControlY, viewWidth - textWidth - arcWidth, viewHeight);

pathRight.lineTo(viewWidth, viewHeight);

pathRight.lineTo(viewWidth, 0);

paint.setColor(selectColor);

canvas.drawPath(pathRight, paint);

tabtext的绘制(关键代码):

for (int i = 0; i < tabTextList.size(); i++) {

String strTabText = tabTextList.get(i);

Rect rectText = new Rect();

textPaint.getTextBounds(strTabText, 0, strTabText.length(), rectText);

int strWidth = rectText.width();

int strHeight = rec​
tText.height();

if (i == 0) {

canvas.drawText(strTabText, (textWidth + arcWidth / 2) / 2 - strWidth / 2, viewHeight / 2 + strHeight / 2, textPaint);

} else if (i == tabTextList.size() - 1) {

canvas.drawText(strTabText, viewWidth - (textWidth + arcWidth / 2) / 2 - strWidth / 2, viewHeight / 2 + strHeight / 2, textPaint);

} else {

canvas.drawText(strTabText, textWidth * i + arcWidth * (i - 1) + (textWidth + 2 * arcWidth) / 2 - strWidth / 2, viewHeight / 2 + strHeight / 2, textPaint);

}

}

tab点击处理(关键代码):

@Override

public boolean onTouchEvent(MotionEvent event) {

boolean isHandleClick = false;//是否处理点击事件

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

float x = event.getX();

float y = event.getY();

System.out.println(“YPKTabLayoutView.onTouchEvent x=” + x + " y=" + y);

for (int i = 0; i < tabNumber; i++) {

if (x <= ((i + 1) * textWidth + i * arcWidth + arcWidth / 2)) {//点击的第一个按钮

tabPosition = i;

if (onTabClickListener != null) {

onTabClickListener.tabSelectedListener(tabPosition);

}

invalidate();

isHandleClick = true;

break;

}

}

return isHandleClick;

case MotionEvent.ACTION_MOVE:

break;

case MotionEvent.ACTION_UP:

break;

}

return super.onTouchEvent(event);

}

步骤说明:

我们在 onTouchEvent方法中, 首先获取到点击控件后x,y坐标,然后for循环判断x的坐标是在哪个tab项的范围内,最后在哪个范围内,我们就认为点击了那个tab项,回调对应的 tabPosition 就可以了。

远程依赖使用

==================================================================

一:添加依赖

Add it in your root build.gradle at the end of repositories:

allprojects {

repositories {

maven { url ‘https://jitpack.io’ }

}

}

Add the dependency

dependencies {

implementation ‘com.github.dacaoyuan:YPKTabDemo:1.0.2’

}

二:在xml布局中添加

<com.ypk.library.view.YPKTabLayoutView

android:id=“@+id/mYPKTabLayoutView”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:layout_margin=“10dp”

app:view_bg_corners=“0”

app:arcControlX=“30” />

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取
ent"

android:layout_margin=“10dp”

app:view_bg_corners=“0”

app:arcControlX=“30” />

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-GdaIXqiO-1719084181772)]一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

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

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

相关文章

人工智能在数字病理切片虚拟染色以及染色标准化领域的研究进展|顶刊速递·24-06-23

小罗碎碎念 本期推文主题&#xff1a;人工智能在数字病理切片虚拟染色以及染色标准化领域的研究进展 这一期的推文是我发自内心觉得为数不多&#xff0c;特别宝贵的一篇推文&#xff0c;原因很简单——可参考的文献相对较少&方向非常具有研究意义&现在不卷。 数字病理…

nginx服务器连接数告警

序言 只要系统之间有交互&#xff0c;那么就会有连接数&#xff0c;连接数的告警阈值一般设置个几万&#xff0c;当连接数开始告警之后&#xff0c;怎么来排查呢&#xff1f; 连接数过多&#xff0c;会消耗cpu&#xff0c;内存&#xff0c;文件句柄等资源&#xff0c;其实也还好…

《算法笔记》总结No.2——模拟

一.简单模拟 考察代码能力&#xff0c;不涉及算法&#xff1a;一类题目怎么说你就怎么做的类型~ 巴音布鲁克比赛&#xff0c;以车队为基准评选冠军。业务要求是&#xff1a;第一行输入正整数N&#xff0c;代表车手的总个数&#xff1b;接下来的N行每行键入两个数字&#xff1a…

【TOOL】ceres学习笔记(二) —— 自定义函数练习

文章目录 一、曲线方程1. 问题描述2. 实现方案 一、曲线方程 1. 问题描述 现有数学模型为 f ( x ) A e x B s i n ( x ) C x D f(x)Ae^xBsin(x)Cx^D f(x)AexBsin(x)CxD &#xff0c;但不知道 A A A 、 B B B 、 C C C 、 D D D 各参数系数&#xff0c;实验数据中含有噪声…

Windows程序设计课程作业-3(文件并发下载)

目录 目录 1.作业内容 2.作业要求 3.主要思路 1&#xff09;窗体和组件初始化 2&#xff09;下载管理器实例化 3&#xff09;按钮点击事件处理 4&#xff09;窗体加载事件处理 5&#xff09;下载消息处理 4.主要难点 1&#xff09;多线程管理&#xff1a; 2&#xff09…

智能优化算法改进策略之局部搜索算子(六)--进化梯度搜索

1、原理介绍 进化梯度搜索(Evolutionary Gradient Search, EGS)[1]是兼顾进化计算与梯度搜索的一种混合算法&#xff0c;具有较强的局部搜索能力。在每次迭代过程中&#xff0c;EGS方法首先用受进化启发的形式估计梯度方向&#xff0c;然后以最陡下降的方式执行实际的迭代步骤&…

【JavaSE ⑧】P219 ~ 225 Date类‘’DateFormat类转化Date和字符串;Calendar类获得日历中某值,修改日历,日历转日期

目录 日期时间类1 Date类概述常用方法 2DateFormat类构造方法格式规则常用方法parse方法format方法 3 Calendar类概念获取方式常用方法get/set方法add方法getTime方法 ● 练习1.判断Date不同参数构造的输出2. 用日期时间相关的API&#xff0c;计算一个人已经出生了多少天。3. 获…

【Java】已解决java.lang.NoSuchMethodException异常

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决java.lang.NoSuchMethodException异常 在Java编程中&#xff0c;java.lang.NoSuchMethodException是一个常见的运行时异常&#xff0c;它通常表示尝试通过反射调用一个不存在…

Ai调教写作技巧,不会还有人在到处找指令吧!一篇文章带你学会生成爆款文章写作指令

大家好&#xff0c;我是网创有方的站长&#xff0c;今天教大家一个重磅级的ai调价指令。 相信很多朋友们都去到处找过写作指令&#xff0c;包括我之前也是&#xff0c;但是随着运用ai写作的次数越来越多&#xff0c;我也是渐渐地熟悉了ai的调教。那么本文的目的是什么呢&#…

使用obdumper对oceanbase进行备份,指定2881端口

1.安装obdumper &#xff08;1&#xff09;下载软件 OceanBase分布式数据库-海量数据 笔笔算数https://www.oceanbase.com/softwarecenter &#xff08;2&#xff09;安装软件 参考&#xff1a;https://www.oceanbase.com/docs/common-oceanbase-dumper-loader-100000000062…

C语言标准库

目录 引言 一、C标准库概述 常用标准库函数 字符串处理 数学运算 动态内存分配 标准库的扩展与限制 扩展功能 使用限制 使用自定义库与第三方库 创建自定义库 使用第三方库 表格总结 标准库头文件及功能 常用标准库函数 总结 引言 C标准库是C编程语言的重要组成…

Android studio登录Google账号超时的解决方法

确保自己已经打开了代理&#xff08;科学上网&#xff09;在设置-外观与行为-系统设置-HTTP代理 中打开“自动检测代理设置”&#xff1a; 再次重新尝试登录Google账号&#xff0c;登陆成功&#xff01; 学术会议征稿 想要了解国内主办的覆盖学科最全最广的学术会议&#xff0c…

redis持久化操作【随记】

持久化 Redis它是将数据保存到内存当中,内存里的数据最大特点: 断电易失.保存在内存的数据就没有了.如果如果这些数据还有用,业务使用啥的,不能就让它这么没有了. redis当中提供持久化机制, 说白了,将内存的数据 —-> 写入到磁盘. –> 持久化. 1 rdb方式 redis database,…

帕金森患者饮食指南:科学调养,呵护健康

&#x1f33c;在医学的广阔领域中&#xff0c;帕金森病作为一种慢性神经系统疾病&#xff0c;除了需要专业的医疗治疗外&#xff0c;日常饮食的调养也显得尤为重要。 今天&#xff0c;就为大家带来一份专为帕金森患者打造的饮食建议&#xff0c;希望能为大家的健康调养提供一些…

泛微E9与金蝶云星空ERP的无缝集成案例详解(包括接口与字段)

业务系统现状 背景介绍 泛微E9和金蝶云星空ERP是两款广泛应用与企业管理的信息系统&#xff0c;分别在移动办公自动化和企业资源计划管理领域占据重要地位。然而企业在使用这些系统时往往面临着信息孤岛和系统孤立的问题&#xff0c;导致数据无法在不系统之间高效流转共享。 当…

微服务——服务治理

目录 1 什么是服务治理&#xff1f;2 为什么需要服务治理&#xff1f;3 服务治理的关键点3.1 服务注册与发现3.2 负载均衡3.3 容错与熔断3.4 服务监控与告警3.5 服务配置管理 4 示例说明5 总结 1 什么是服务治理&#xff1f; 简单来说&#xff0c;服务治理就是对微服务架构中的…

Java:113-Spring Data JPA详解

Spring Data JPA详解 Spring Data Jpa 是应用于Dao层的⼀个框架&#xff0c;简化数据库开发的&#xff0c;作用和Mybatis框架⼀样&#xff0c;但是在使用方式和底层机制是有所不同的&#xff0c;最明显的⼀个特点&#xff0c;Spring Data Jpa 开发Dao的时候&#xff0c;很多场景…

气膜建筑:持久耐用的建筑选择—轻空间

随着科技的发展&#xff0c;气膜建筑以其快速施工、节能环保和灵活多用的特点&#xff0c;正在各个领域获得越来越多的应用。然而&#xff0c;许多人对气膜建筑的耐用程度仍存有疑虑。本文将从气膜建筑的材料、结构设计和维护等方面&#xff0c;深入探讨气膜建筑的耐用性&#…

【Android WebView】WebView基础

一、简介 WebView是一个基于webkit引擎、展现web页面的控件。Android的Webview在低版本和高版本采用了不同的webkit版本内核&#xff0c;4.4后直接使用了Chrome。 二、重要类 以WebView类为基础&#xff0c;WebSettings、WebViewClient、WebChromeClient为辅助共同完成安卓段加…

阿里云,大周末彻底要爆!

大家好&#xff0c;我是肉哥&#xff0c;熟悉我的人都知道&#xff0c;每年6.18我都会带领大家薅阿里云的羊毛&#xff0c;今年也不例外&#xff0c;作为程序员搞台ECS&#xff0c;做个项目满满的成就感&#xff01;阿里内部小伙伴透漏&#xff0c;今年的618活动优惠力度更是拉…