用Android来开发自适应机型屏幕大小的文本控件
需求
自适应大小的文本:
效果图:
项目开发中,开发人员根据UI人员提供的一套尺寸,布局了一些带文本的页面,
往往会少考虑一些数据极限的问题,造成机型屏幕适配问题。
例如:
文本(或数值)长度可变,如经验值、金币数量等,如果页面同一高度使用了多个Textview布局摆放,当Textview文本长度增加时,有可能造成重叠现象。
例子还有很多,相信很多开发人员也都曾遇到过。
今天我们就写一个简单的例子,解决该问题。
我们项目中的实际需求:1。文本控件单行、居中显示;2。文本控件默认有一个固定尺寸,如果文本过多,字号自动变小,文本仍然完全显示,不折行;3。文本控件尺寸变小,文本不变,字号也自动变小,文本仍然完全显示,不折行;其实2和3仔细分析一下是一个意思:文本控件的空间不够存放当前文本时,字号自动缩小。分析
一般的情况,很多开发者做法是继承TextView,控制其属性以达到效果。
我们换一种做法。
TextView文本说到底,就是一个View,而我们看到的文本,其实是通过画笔绘制在View空间(即:画布)上的。
那么我们也使用这种方式,画笔来实现吧。
可能有开发者会担心很难,其实一点也不难,只需要简单掌握几个基础知识(1。画布、2。画笔、3。文本绘制的计算)就可以了。
我们自定义出来的控件代码,不会比直接继承TextView多,而且大家还能学到更多有用的知识。
那么我们现在开始分析:
如何在一定的空间中自适应文本的大小,使其能完全显示?
原理很简单,就是把字号变小就行了,那么变成多小?如何计算呢?1。我们要实现的控件,只是一个文本显示,界面不复杂,所以我们不需要使用XML,只写一个自定义类即可;2。自定义类直接继承View;3。View中可获得的或者外界传入的信息(即:已知条件):3。1文本信息Text(外界传入)3。2字号最大值MaxSize(外界传入,即,我们设置的默认字号,因为字号只会缩小,不会增加,所以也叫字号最大值)3。3文本使用字号最大值应显示的宽度PreWidth(可通过Paint、Text、MaxSize获得)3。4空间的宽度canvasWidth(即画布宽度,可在View的onSizeChanged方法中获得);那么,当空间不足以显示文本时,应通过已知条件计算canvasWidth应对应的字号大小X,如何计算呢?经过分析,大字体显示的宽度与小字体显示的宽度比值应是一致的,所以,公式如下:MaxSizePreWidthxcanvasWidth;xMaxSizecanvasWidthPreWidth;x就是我们需要重新设置的字号。得到字号,我们就可以为画笔设置文字大小了,也就可以通过画笔画出合适大小的文字了。4。绘制文本,文本的坐标如何处理呢?此处我们以居中显示为例:我们可以通过文本宽高与画布宽高可计算出文字应被绘制的位置(x,y)例如:容器(即画布)宽10,高10,文字宽4,高4,我们把容器和文字分别想象成两个矩形,如何使文字矩形居中显示在容器矩形里,需要我们算出文字矩形左上角的位置(x,y),即文字绘制的位置,其实也很简单:x(容器宽文字宽)21(04)33y(容器高文字高)23如果我们直接使用这个(x,y)直接绘制文本,得到的结果是文字位置向上偏移了,这不是我们想要的结果。其实,文本绘制是根据基线(baseline)来绘制的,我们还需要了解文本绘制时的结构计算。(文本如何绘制,下面会有介绍)
好了,基础知识点分析完了,我们先熟悉一下这些基础吧!绘图基础
本章介绍的基础知识仅介绍当前需要用到的,不会特别细致,如果有需要,请自行查询资料。
1。Canvas画布、Paint画笔
我们看到的控件上的内容,其实是绘制在画布上的,而绘制是使用的画笔工具。
画布有很多方法,如:画文本、圆、矩形、线、路径、图片等;Canvas(画布)提供的画文本的方法:publicvoiddrawText(char〔〕text,intindex,intcount,floatx,floaty,Paintpaint){thrownewRuntimeException(Stub!);}publicvoiddrawText(Stringtext,floatx,floaty,Paintpaint){thrownewRuntimeException(Stub!);}publicvoiddrawText(Stringtext,intstart,intend,floatx,floaty,Paintpaint){thrownewRuntimeException(Stub!);}publicvoiddrawText(CharSequencetext,intstart,intend,floatx,floaty,Paintpaint){thrownewRuntimeException(Stub!);}
画笔可以设置我们想要的效果,如颜色、字体、字号、线粗细等;Paint(画笔)设置文本属性的一些主要方法:设置文本大小publicvoidsetTextSize(floattextSize){thrownewRuntimeException(Stub!);}设置颜色publicvoidsetColor(intcolor){thrownewRuntimeException(Stub!);}设置文本间距publicvoidsetLetterSpacing(floatletterSpacing){thrownewRuntimeException(Stub!);}设置文本对齐方式publicvoidsetTextAlign(Paint。Alignalign){thrownewRuntimeException(Stub!);}设置文本文字类型:如:宋体、黑体、平方等字体publicTypefacesetTypeface(Typefacetypeface){thrownewRuntimeException(Stub!);}计算文本宽度(根据设置的字号大小,要绘制的文本内容,计算并返回该文本应占用的宽度)publicfloatmeasureText(Stringtext){thrownewRuntimeException(Stub!);}。。。还有很多设置文本的方法,可以自行查找!!!
2。文本绘制
想把文字精准的绘制到屏幕上,
需要了解字体测量类:FontMetrics,以及该类的top,ascent,descent,bottom,leading五个成员变量;
如果我们想在画布的最左上角(即(0,0)位置)开始绘制文本,绘制完成后,我们看到的是如上图所示的Baseline以下的部分,而上面的部分我们是看不到的。
原因很简单,文本的绘制,是基于Baseline的,如果我们直接设置绘制的位置是(0,0),即Baseline的位置是(0,0),如上图所示,Baseline以上的文本肯定被绘制在控件上面去了,即y为负数,所以我们看不到Baseline以上的内容。
那么,我们应该如何做才能将文字绘制在我们想要的位置呢,其实很简单,我们只需要让Baseline的位置向下偏移就可以了,但是偏移多少呢?
再看上图,我们只需要拿到文本的上面的几个属性(ascent等),就可以算出来了。代码实现
自定义控件类:AutoTextViewpackageiwangzhe。customautosizetextview;importandroid。content。Context;importandroid。graphics。Canvas;importandroid。graphics。Paint;importandroid。util。AttributeSet;importandroid。view。View;importandroid。widget。TextView;类:AutoTextView作者:qxc日期:201843。publicclassAutoTextViewextendsView{privateContextcontext;上下文privateintcanvasWidth;画布宽度privateintcanvasHeight;画布高度privatePaintpaint;画笔privateintmaxTextSize50;外界传入的默认字号(最大字号),单位SP,如果不传入,我们默认50sp,大家可自行修改privateStringtext;外界传入的文本内容publicAutoTextView(Contextcontext){super(context);this。contextcontext;initPaint();初始化画笔属性}publicAutoTextView(Contextcontext,AttributeSetattrs){super(context,attrs);this。contextcontext;initPaint();初始化画笔属性}初始化画笔属性privatevoidinitPaint(){设置防锯齿paintnewPaint(Paint。ANTIALIASFLAG);设置画笔宽度paint。setStrokeWidth(1);}设置文本(供外界调用)paramtext文本publicAutoTextViewsetText(Stringtext){this。texttext;returnthis;}设置支持的最大文本字号(供外界调用)paramsize文本字号publicAutoTextViewsetMaxTextSize(intsize){this。maxTextSizesize;returnthis;}OverrideprotectedvoidonSizeChanged(intw,inth,intoldw,intoldh){super。onSizeChanged(w,h,oldw,oldh);每一次外观变化,都会调用该方法this。canvasWidthgetWidth();获得画布宽度this。canvasHeightgetHeight();获得画布高度}OverrideprotectedvoidonDraw(Canvascanvas){每次重绘,绘制传递进来的文本信息drawText(canvas);}绘制文本privatevoiddrawText(Canvascanvas){根据画布宽度,获得合适的字号(即:刚好能显示满当前宽度的字号,与maxsize相比,只能小不能大)floattextSizegetRightSize();为画笔设置上合适的字号paint。setTextSize(sp2px(textSize));计算Baseline绘制的起点X轴坐标,计算方式:画布宽度的一半文字宽度的一半intx(int)(canvasWidth2paint。measureText(text)2);intx0;计算Baseline绘制的Y坐标,计算方式:画布高度的一半文字总高度的一半inty(int)((canvasHeight2)((paint。descent()paint。ascent())2));绘制文本canvas。drawText(text,x,y,paint);}获得合适的字号privatefloatgetRightSize(){paint。setTextSize(sp2px(maxTextSize));paint。setTextAlign(Paint。Align。LEFT);根据最大值,计算出当前文本占用的宽度floatpreWidthpaint。measureText(text);如果文本占用的宽度比画布宽度小,说明不用伸缩,直接返回当前字号if(preWidthcanvasWidth){returnmaxTextSize;}已知当前文本字号、文本占用宽度、画布宽度,计算出合适的字号,并返回returnmaxTextSizecanvasWidthpreWidth;}实际绘制时,需要使用像素进行绘制,此处提供sp转px的方法privateintsp2px(floatspValue){finalfloatscalecontext。getResources()。getDisplayMetrics()。density;return(int)(spValuescale0。5f);}}
调用方类与布局文件:MainActivity
使用SeekBar模拟文本控件宽度变化(即:容器大小变化)lt;?xmlversion1。0encodingutf8?RelativeLayoutxmlns:androidhttp:schemas。android。comapkresandroidxmlns:toolshttp:schemas。android。comtoolsandroid:ididactivitymainandroid:layoutwidthmatchparentandroid:layoutheightmatchparenttools:contextiwangzhe。customautosizetextview。MainActivityiwangzhe。customautosizetextview。AutoTextViewandroid:layoutwidthmatchparentandroid:backgroundeeeeeeandroid:layoutheight50dpandroid:layoutmarginTop20dpandroid:ididatvSeekBarandroid:layoutwidthmatchparentandroid:layoutheightwrapcontentandroid:layoutbelowidatvandroid:layoutmarginTop50dpandroid:progress100android:ididsbRelativeLayoutpackageiwangzhe。customautosizetextview;importandroid。content。Context;importandroid。content。res。Configuration;importandroid。support。v7。app。AppCompatActivity;importandroid。os。Bundle;importandroid。widget。RelativeLayout;importandroid。widget。SeekBar;publicclassMainActivityextendsAppCompatActivity{SeekBarsb;AutoTextViewatv;OverrideprotectedvoidonCreate(BundlesavedInstanceState){super。onCreate(savedInstanceState);setContentView(R。layout。activitymain);initView();initEvent();}voidinitView(){sb(SeekBar)findViewById(R。id。sb);sb。setProgress(100);atv(AutoTextView)findViewById(R。id。atv);设置测试数据atv。setText(一二三四五六七八九十)。setMaxTextSize(50);}voidinitEvent(){使用SeekBar模拟文本控件宽度变化(即:容器大小变化)sb。setOnSeekBarChangeListener(newSeekBar。OnSeekBarChangeListener(){OverridepublicvoidonProgressChanged(SeekBarseekBar,inti,booleanb){RelativeLayout。LayoutParamslinearParams(RelativeLayout。LayoutParams)atv。getLayoutParams();linearParams。heightdip2px(MainActivity。this,50);linearParams。widthisb。getWidth()100;atv。setLayoutParams(linearParams);}OverridepublicvoidonStartTrackingTouch(SeekBarseekBar){}OverridepublicvoidonStopTrackingTouch(SeekBarseekBar){}});}publicstaticintdip2px(Contextcontext,floatdpValue){finalfloatscalecontext。getResources()。getDisplayMetrics()。density;return(int)(dpValuescale0。5f);}}
总结:
此控件使用起来,很简单,但是技术点很实用,以后做动态报表的自定义控件时,都会用到。
如果大家想要增加设置文本字体、加粗、下划线等样式,请参考该示例,自行扩展即可。
本文章,主要是为了让大家了解使用画布画笔自定义控件的过程,如果想在自己的项目中使用,请根据需要自行调整优化。
正式留队!北控顶薪内线不走了,手握600万年薪,马布里有苦难北控男篮这个休赛期还没有透露新的引援计划,可以确定的是主教练马布里不会离开,球队还引进了CBA名宿张劲松。目前,除了队内确定侯逸凡已经离队,外援方面,兰兹博格和托多罗维奇都不会……
航天人10年太空之约见证载人航天跨越之旅来源:人民日报从航天任务连战连捷到重大航天工程深入推进,从空间科学探索到航天科技成果服务经济社会发展,10年来,一次次振奋人心的火箭腾飞,一项项令人惊叹的航天成就,勾勒出……
国乒女将03惨败,早田希娜晋级!张本智和一轮游,猛将跪地庆祝7月14日,WTT球星挑战赛布达佩斯站展开女单116决赛较量。在师姐陈梦退赛后,刘炜珊没有顶住压力,输给日本主力早田希娜,止步32强。王艺迪在先丢一局后上演逆转,战胜徐孝元,晋……
跑跑卡丁车手游传说道具车狂野鲨狂野鲨是2022年10月5日S20赛季通过幸运翻翻乐获得的一辆传说道具车。是一辆辅助上位车。使用磁铁和黄金磁铁时高概率获得的胎印导弹可以保证狂野鲨有稳定的输出和上位能力,胎印导……
价值6。8亿的天下第一宴,到底都有些什么菜?一起来看看在中国新疆哈密的一座博物馆之内,竟然赫然陈列着一桌超级丰盛的菜,然而这桌菜的标价竟然达到了6。8亿,让所有观看的游客感到瞠目结舌。这一大桌菜光是桌子直接就达到16。8米,……
收评A股三大指数震荡收跌,两市成交额创一年多新低金融界9月19日消息周一A股三大指数纷纷低开,随后在早盘展开震荡整理,午后一小时A股持续走弱,创业板指跌超1,随后市场止跌有所反弹。截至收盘,沪指跌0。35,报3115。……
儿子婚礼父亲感人致辞儿子成亲,做父亲的无论怎样也得上台说几句!那么婚礼父亲致辞要说些什么呢?下面由品学网小编给大家带来的儿子婚礼父亲感人致辞,希望各位客官喜欢!儿子婚礼父亲感人致辞1大家好……
2018区民政局工作计划范文xx年,区民政局将在区委、区政府的正确领导下,在市民政局的具体指导下,继续围绕保发展、保民生、保稳定的工作大局,以贯彻落实党的xx届四中全会会议精神为抓手,以打造诚信、贴心、高……
2021年最新个人廉洁自律自查报告(通用5篇)时间过得太快,让人不知所措,工作已经告一段落了,回顾这段时间的工作,取得了成绩,也存在着问题,好好地做个总结并写一份自查报告吧。那么大家知道正规的自查报告怎么写吗?以下是小编为……
中层干部培训班学习心得尊敬的各位领导:下午好!我很荣幸代表第三小组对本次培训班学习体会进行发言。这次培训内容丰富,有学习贯彻云南省第九次党代会精神的专题讲座;有深入学习贯彻省第九次……
幼儿园财务管理规定范文3篇幼儿园财务应真实、准确和完整地反映财务收支情况,按月编制财务报表,按规定期限报送登记管理机关、业务主管单位和财政部门,并接受监督和检查。幼儿园的财务管理都有哪些制度?下面是相关……
热门放假通知7篇在学习、工作生活中,越来越多人会去使用通知,通知是运用广泛的知照性公文。如何写一份恰当的通知呢?以下是小编收集整理的放假通知7篇,欢迎阅读,希望大家能够喜欢。放假通知篇1……