热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android类似公交站牌的文字竖排效果

之前群里有人提出一个类似公交站牌的文字竖排效果,自己捉摸了一下,想想估计能实现,就算不能实现,权当学习了。先看看做出来的效果图:先说说思路

之前群里有人提出一个类似公交站牌的文字竖排效果,自己捉摸了一下,想想估计能实现,就算不能实现,权当学习了。

先看看做出来的效果图:



先说说思路:

整体为绘制圆,绘制间隔线,然后是绘制数字与文字

1.绘制圆和间隔线:

绘制这两个使用了Canvas.drawPath(Path path,Paint paint),因为这样子便于计算,只需要计算Path的路径,其它的就交给canvas可以了,path支持绘制circle(path.addCircle())与绘制line(path.lineto),本来想文本也用path绘制的,不过最后发现绘制文本只有canvas支持,path不支持。剩下的就是计算绘制起始点了

    /**
* 计算点圆与线轨迹
*/
private void computeCircleAndLinePath() {
if (data != null || data.size() != 0) {

circlePoints = new Point[data.size()];
circlePaths = new Path[data.size()];
linePaths = new Path[data.size() - 1];

//处理文字显示与圆点显示
//绘制起始偏移量,如果文本高度一半大于半径,则取差值,否则取0
int offsetX = textHeight / 2 > (circleRadius + circleStokeWidth / 2) ? textHeight / 2 - (circleRadius + circleStokeWidth / 2) : 0;
//处理paddingLeft
offsetX += getPaddingLeft();
for (int i = 0; i Path circlePath = new Path();
//计算每个圆点的中心点
int mCircleCenterX = i * lineLength + circleRadius + circleStokeWidth / 2 + offsetX;
int mCircleCenterY = circleMarginTop + circleRadius;
circlePoints[i] = new Point(mCircleCenterX, mCircleCenterY);

//计算圆点路径
circlePath.addCircle(mCircleCenterX, mCircleCenterY, circleRadius, Path.Direction.CCW);
circlePaths[i] = circlePath;
if (i == data.size() - 1) {
continue;
}
//计算线段路径
Path linePath = new Path();
//计算线段起始点
int mlineStartX = mCircleCenterX + circleRadius + circleStokeWidth / 2;
int mlineEndX = (i + 1) * lineLength + offsetX;
linePath.moveTo(mlineStartX, mCircleCenterY);
linePath.lineTo(mlineEndX, mCircleCenterY);
linePaths[i] = linePath;
}
}
}
上述代码整体为计算这两个circle与line的起始点,圆的大小,线段的长度都由样式定制来决定,同时记录了每个圆的中心点,每个圆的路径,每个线的路径。

还有一个offsetX,至于这个变量是考虑了因为要定制样式,有字体大于圆的情况,这样的话就会存在文字大小与圆半径之间存在偏移,如果不考虑他,有可能我们的效果会出现第一个条目的文字只显示一半,所以我记录了这个偏移,把他加入了起始点的计算


这里需要注意,android绘制circle时结合了Paint画笔的笔触宽度strokeWidth,为圆半径+strokeWidth/2,所以我们在

Path.addCircle(float x,float y,float radius,Direction dir)时需要考虑传入的半径为设定的圆半径+strokeWidth/2


最后使用canvas绘制即可:

    /**
* 绘制圆和线段
*/
private void drawCircleAndLine(Canvas canvas) {
for (int i = 0; i //改变颜色与风格
if (i == currSelectedItem) {
circlePaint.setColor(circleSelectColor);
circlePaint.setStyle(Paint.Style.FILL_AND_STROKE);
} else {
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setColor(circleColor);
}
canvas.drawPath(circlePaths[i], circlePaint);
}
for (int i = 0; i canvas.drawPath(linePaths[i], linePaint);
}
}

2.绘制数字和文本:

  因为Path不能绘制文本,只能用canvas来绘制,canvas提供了大概三种类型的方法:

 canvas.drawText();

 canvas.drawPostText();(已废弃)

 canvas.drawTextonPath() 该方法不好控制文本的方向以及文本的样式

 所以采用drawText()方法

 

    /**
* 绘制数字和文本
*/
private void drawNumberAndItemText(Canvas canvas) {
for (int i = 0; i //数字绘制起始点
int mNumberStartX = circlePoints[i].x;
int mNumberStartY = circlePoints[i].y + circleRadius + numberMarginTop;
//测量数字宽度
float textWidth = numberPaint.measureText(String.valueOf(i + 1), 0, String.valueOf(i + 1).length());
//改变颜色
if (i == currSelectedItem) {
numberPaint.setColor(numberSelectColor);
} else {
numberPaint.setColor(numberColor);
}
canvas.drawText(String.valueOf(i + 1), mNumberStartX - textWidth / 2, mNumberStartY, numberPaint);
drawItemText(canvas);
}
}
因为为竖直排列,所以要判断每个字的宽度,高度,同时还的考虑每个字之间的间隔,所以确定每个字的范围很有必要

    /**
* 绘制文本
*/
private void drawItemText(Canvas canvas) {
//记录每个条目区域
textRegiOns= new Region[data.size()];
for (int i = 0; i Region region = new Region();
int mItemStartX = circlePoints[i].x;
int mItemStartY = circlePoints[i].y + circleRadius + numberMarginTop + textMarginTop;
String text = data.get(i);
char[] chars = text.toCharArray();
//每个条目区域的计算
Rect textArea = new Rect();
textArea.left = mItemStartX - textHeight / 2;
textArea.right = textArea.left + textHeight;
//减去一个文字高度,因为绘制文字是在baseline上方绘制,基线位置为drawText(text,x,y,paint)的y位置
textArea.top = mItemStartY - textHeight;
int lastTextHeight = 0;
//改变颜色
if (i == currSelectedItem) {
textPaint.setColor(textSelectColor);
} else {
textPaint.setColor(textColor);
}
for (int j = 0; j canvas.drawText(String.valueOf(text.charAt(j)), mItemStartX - textHeight / 2, mItemStartY + lastTextHeight, textPaint);
lastTextHeight += textHeight + textSpace;
}
//此处减去最后一个文字间隔
textArea.bottom = textArea.top + lastTextHeight - textSpace;
region.set(textArea);
textRegions[i] = region;

}
}

文本的宽度,高度可以采用如下方法获得:

    /**
* 计算文本信息,包含每个条目的宽度,高度
*/
private Rect getTextInfo(String text, Paint paint) {
Rect mrect = new Rect();
//获得文本的最小矩形大小,也是测量文本高度,宽度的一种方法
paint.getTextBounds(text, 0, text.length(), mrect);
return mrect;
}

文本宽度也可以使用Paint.measureText(String text,int start,int end)获得

这里需要注意:

 android绘制文字的时候,使用drawText(String text,float x,float y,Paint paint)时,大家注意y坐标的位置实际上为绘制文字的baseline位置,在基线位置之上开始绘制



最后因为在布局文件使用了scrollView,torizontalScrollView,所以在View.onMeasure()计算了整个view占据的最小空间大小,如果不这样计算,在绘制view的时候会无法显示,因为scrollview传递给子布局的大小为0

    @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//此处为与HorizontalScrollView搭配使用,达到滑动目的,别的滑动方式请修改此处代码或者删除
int mViewWidth = getPaddingLeft() + getPaddingRight() + computeMinViewWidth();
int mViewHeight = getPaddingBottom() + getPaddingTop() + computeMinViewHeight();
setMeasuredDimension(mViewWidth, mViewHeight);
}
    /**     * 计算View最小宽度     */    private int computeMinViewWidth() {        int viewWidth = 0;        if (data != null || data.size() != 0) {            //计算文本高度,起始不管第一条是否为空,高度都会与其它文本保持一致            Rect textRect = getTextInfo(data.get(0), textPaint);            textHeight = textRect.height();            viewWidth = (data.size() - 1) * lineLength + 2 * circleRadius + circleStokeWidth;            if (textHeight > (2 * circleRadius + circleStokeWidth)) {                int offsetX = textHeight / 2 - (circleRadius + circleStokeWidth / 2);                viewWidth = viewWidth + 2 * offsetX;            }        }        return viewWidth;    }    /**     * 计算View最小高度     */    private int computeMinViewHeight() {        int viewHeight = 0;        if (data != null || data.size() != 0) {            String maxText = "";            for (int i = 0; i  maxText.length() ? data.get(i) : maxText;            }            //获得总文本间距            int textSpaceWidth = textSpace * (maxText.length() - 1);            Rect textRect = getTextInfo(maxText, textPaint);            //最大文本高度            int textMaxHeight = textRect.width() + textSpaceWidth;            //计算ViewHeight            viewHeight = textMaxHeight + textMarginTop + numberMarginTop + (2 * circleRadius + circleStokeWidth) + circleMarginTop;        }        return viewHeight;    }

大概就是这样子,基本上都是一点点绘制的,暂时没有想到别的办法,如有好的思路,请提供

下面附上源码下载地址:


http://download.csdn.net/download/kongzuoding/9403338


大家可以下载看看效果,如有错误,或者更好的建议,请指出


本博客用于技术分享,不得用于商业用途,支持原创,转载请做申明




推荐阅读
  • 本文介绍了如何使用 Node.js 和 Express(4.x 及以上版本)构建高效的文件上传功能。通过引入 `multer` 中间件,可以轻松实现文件上传。首先,需要通过 `npm install multer` 安装该中间件。接着,在 Express 应用中配置 `multer`,以处理多部分表单数据。本文详细讲解了 `multer` 的基本用法和高级配置,帮助开发者快速搭建稳定可靠的文件上传服务。 ... [详细]
  • 您的数据库配置是否安全?DBSAT工具助您一臂之力!
    本文探讨了Oracle提供的免费工具DBSAT,该工具能够有效协助用户检测和优化数据库配置的安全性。通过全面的分析和报告,DBSAT帮助用户识别潜在的安全漏洞,并提供针对性的改进建议,确保数据库系统的稳定性和安全性。 ... [详细]
  • 【问题】在Android开发中,当为EditText添加TextWatcher并实现onTextChanged方法时,会遇到一个问题:即使只对EditText进行一次修改(例如使用删除键删除一个字符),该方法也会被频繁触发。这不仅影响性能,还可能导致逻辑错误。本文将探讨这一问题的原因,并提供有效的解决方案,包括使用Handler或计时器来限制方法的调用频率,以及通过自定义TextWatcher来优化事件处理,从而提高应用的稳定性和用户体验。 ... [详细]
  • 开发技巧:在Interface Builder中实现UIButton文本居中对齐的方法与步骤
    开发技巧:在Interface Builder中实现UIButton文本居中对齐的方法与步骤 ... [详细]
  • 在尝试对 QQmlPropertyMap 类进行测试驱动开发时,发现其派生类中无法正常调用槽函数或 Q_INVOKABLE 方法。这可能是由于 QQmlPropertyMap 的内部实现机制导致的,需要进一步研究以找到解决方案。 ... [详细]
  • 在《Cocos2d-x学习笔记:基础概念解析与内存管理机制深入探讨》中,详细介绍了Cocos2d-x的基础概念,并深入分析了其内存管理机制。特别是针对Boost库引入的智能指针管理方法进行了详细的讲解,例如在处理鱼的运动过程中,可以通过编写自定义函数来动态计算角度变化,利用CallFunc回调机制实现高效的游戏逻辑控制。此外,文章还探讨了如何通过智能指针优化资源管理和避免内存泄漏,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
  • 如何将TS文件转换为M3U8直播流:HLS与M3U8格式详解
    在视频传输领域,MP4虽然常见,但在直播场景中直接使用MP4格式存在诸多问题。例如,MP4文件的头部信息(如ftyp、moov)较大,导致初始加载时间较长,影响用户体验。相比之下,HLS(HTTP Live Streaming)协议及其M3U8格式更具优势。HLS通过将视频切分成多个小片段,并生成一个M3U8播放列表文件,实现低延迟和高稳定性。本文详细介绍了如何将TS文件转换为M3U8直播流,包括技术原理和具体操作步骤,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 本文介绍了一种自定义的Android圆形进度条视图,支持在进度条上显示数字,并在圆心位置展示文字内容。通过自定义绘图和组件组合的方式实现,详细展示了自定义View的开发流程和关键技术点。示例代码和效果展示将在文章末尾提供。 ... [详细]
  • 本文介绍了如何利用Struts1框架构建一个简易的四则运算计算器。通过采用DispatchAction来处理不同类型的计算请求,并使用动态Form来优化开发流程,确保代码的简洁性和可维护性。同时,系统提供了用户友好的错误提示,以增强用户体验。 ... [详细]
  • PTArchiver工作原理详解与应用分析
    PTArchiver工作原理及其应用分析本文详细解析了PTArchiver的工作机制,探讨了其在数据归档和管理中的应用。PTArchiver通过高效的压缩算法和灵活的存储策略,实现了对大规模数据的高效管理和长期保存。文章还介绍了其在企业级数据备份、历史数据迁移等场景中的实际应用案例,为用户提供了实用的操作建议和技术支持。 ... [详细]
  • 在HTML布局中,即使将 `top: 0%` 和 `left: 0%` 设置为元素的定位属性,浏览器中仍然会出现空白填充。这个问题通常与默认的浏览器样式、盒模型或父元素的定位方式有关。为了消除这些空白,可以考虑重置浏览器的默认样式,确保父元素的定位方式正确,并检查是否有其他CSS规则影响了元素的位置。 ... [详细]
  • 本文详细解析了 Yii2 框架中视图和布局的各种函数,并综述了它们在实际开发中的应用场景。通过深入探讨每个函数的功能和用法,为开发者提供了全面的参考,帮助他们在项目中更高效地利用这些工具。 ... [详细]
  • 本文详细解析了 Android 系统启动过程中的核心文件 `init.c`,探讨了其在系统初始化阶段的关键作用。通过对 `init.c` 的源代码进行深入分析,揭示了其如何管理进程、解析配置文件以及执行系统启动脚本。此外,文章还介绍了 `init` 进程的生命周期及其与内核的交互方式,为开发者提供了深入了解 Android 启动机制的宝贵资料。 ... [详细]
  • 在《ChartData类详解》一文中,我们将深入探讨 MPAndroidChart 中的 ChartData 类。本文将详细介绍如何设置图表颜色(Setting Colors)以及如何格式化数据值(Formatting Data Values),通过 ValueFormatter 的使用来提升图表的可读性和美观度。此外,我们还将介绍一些高级配置选项,帮助开发者更好地定制和优化图表展示效果。 ... [详细]
  • 深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案
    深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案 ... [详细]
author-avatar
mobiledu2502876293
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有