志在指尖
用双手敲打未来

Android自定义控件:自适应大小的文本控件

需求
自顺应大小的文本:
效果图: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,
如何计算呢?
经过火析,大字体显现的宽度与小字体显现的宽度比值应是分歧的,所以,公式如下:
MaxSize/PreWidth=x/canvasWidth;
x=MaxSize*canvasWidth/PreWidth;
x就是我们需求重新设置的字号。
得到字号,我们就能够为画笔设置文字大小了,也就能够经过画笔画出适宜大小的文字了。
4.绘制文本,文本的坐标如何处置呢?
此处我们以居中显现为例:
我们能够经过文本宽高与画布宽高可计算出文字应被绘制的位置(x,y)
例如:
容器(即画布)宽10,高10,文字宽4,高4,我们把容器和文字分别想象成两个矩形,
如何使文字矩形居中显现在容器矩形里,需求我们算出文字矩形左上角的位置(x,y),即文字绘制的位置,
其实也很简单:
x=(容器宽-文字宽)/2=1(0-4)/3=3
y=(容器高-文字高)/2=3
假如我们直接运用这个(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!”);
}

还有很多设置文本的办法,能够自行查找!!!
详细,可参考这篇文章:
https://www.jianshu.com/p/f69873371763
https://blog.csdn.net/ttjay000/article/details/73876973
2.文本绘制
想把文字精准的绘制到屏幕上,
需求理解字体丈量类:FontMetrics,以及该类的top,ascent,descent,bottom,leading五个成员变量;
绘制的起始点基于Baseline.png
假如我们想在画布的最左上角(即(0,0)位置)开端绘制文本,绘制完成后,我们看到的是如上图所示的Baseline以下的局部,而上面的局部我们是看不到的。
缘由很简单,文本的绘制,是基于Baseline的,假如我们直接设置绘制的位置是(0,0),即Baseline的位置是(0,0),如上图所示,Baseline以上的文本肯定被绘制在控件上面去了,即y为负数,所以我们看不到Baseline以上的内容。
那么,我们应该如何做才干将文字绘制在我们想要的位置呢,其实很简单,我们只需求让Baseline的位置向下偏移就能够了,但是偏移几呢?
再看上图,我们只需求拿到文本的上面的几个属性(ascent等),就能够算出来了。
详细可参考这篇文章:
https://www.cnblogs.com/tianzhijiexian/p/4297664.html
代码完成
自定义控件类:AutoTextView
packageiwangzhe.customautosizetextview;
importandroid.content.Context;
importandroid.graphics.Canvas;
importandroid.graphics.Paint;
importandroid.util.AttributeSet;
importandroid.view.View;
importandroid.widget.TextView;
/**
*类:AutoTextView
*作者:qxc
*日期:2018/4/3.
*/
publicclassAutoTextViewextendsView{
privateContextcontext;//上下文
privateintcanvasWidth;//画布宽度
privateintcanvasHeight;//画布高度
privatePaintpaint;//画笔
privateintmaxTextSize=50;//外界传入的默许字号(最大字号),单位SP,假如不传入,我们默许50sp,大家可自行修正
privateStringtext=””;//外界传入的文本内容
publicAutoTextView(Contextcontext){
super(context);
this.context=context;
initPaint();//初始化画笔属性
}
publicAutoTextView(Contextcontext,AttributeSetattrs){
super(context,attrs);
this.context=context;
initPaint();//初始化画笔属性
}
//初始化画笔属性
privatevoidinitPaint(){
//设置防锯齿
paint=newPaint(Paint.ANTI_ALIAS_FLAG);
//设置画笔宽度
paint.setStrokeWidth(1);
}
/**
*设置文本(供外界调用)
*@paramtext文本
*/
publicAutoTextViewsetText(Stringtext){
this.text=text;
returnthis;
}
/**
*设置支持的最大文本字号(供外界调用)
*@paramsize文本字号
*/
publicAutoTextViewsetMaxTextSize(intsize){
this.maxTextSize=size;
returnthis;
}
@Override
protectedvoidonSizeChanged(intw,inth,intoldw,intoldh){
super.onSizeChanged(w,h,oldw,oldh);
//每一次外观变化,都会调用该办法
this.canvasWidth=getWidth();//取得画布宽度
this.canvasHeight=getHeight();//取得画布高度
}
@Override
protectedvoidonDraw(Canvascanvas){
//每次重绘,绘制传送进来的文本信息
drawText(canvas);
}
//绘制文本
privatevoiddrawText(Canvascanvas){
//依据画布宽度,取得适宜的字号(即:刚好能显现满当前宽度的字号,与maxsize相比,只能小不能大)
floattextSize=getRightSize();
//为画笔设置上适宜的字号
paint.setTextSize(sp2px(textSize));
//计算Baseline绘制的起点X轴坐标,计算方式:画布宽度的一半-文字宽度的一半
intx=(int)(canvasWidth/2-paint.measureText(text)/2);
//intx=0;
//计算Baseline绘制的Y坐标,计算方式:画布高度的一半-文字总高度的一半
inty=(int)((canvasHeight/2)-((paint.descent()+paint.ascent())/2));
//绘制文本
canvas.drawText(text,x,y,paint);
}
//取得适宜的字号
privatefloatgetRightSize(){
paint.setTextSize(sp2px(maxTextSize));
paint.setTextAlign(Paint.Align.LEFT);
//依据最大值,计算出当前文本占用的宽度
floatpreWidth=paint.measureText(text);
//假如文本占用的宽度比画布宽度小,阐明不用伸缩,直接返回当前字号
if(preWidth<canvasWidth){
returnmaxTextSize;
}
//已知当前文本字号、文本占用宽度、画布宽度,计算出适宜的字号,并返回
returnmaxTextSize*canvasWidth/preWidth;
}
//实践绘制时,需求运用像素停止绘制,此处提供sp转px的办法
privateintsp2px(floatspValue){
finalfloatscale=context.getResources().getDisplayMetrics().density;
return(int)(spValue*scale+0.5f);
}
}
调用方类与规划文件:MainActivity
运用SeekBar模仿文本控件宽度变化(即:容器大小变化)
<?xmlversion=”1.0″encoding=”utf-8″?>
<RelativeLayoutxmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:id=”@+id/activity_main”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
tools:context=”iwangzhe.customautosizetextview.MainActivity”>
<iwangzhe.customautosizetextview.AutoTextView
android:layout_width=”match_parent”
android:background=”#eeeeee”
android:layout_height=”50dp”
android:layout_marginTop=”20dp”
android:id=”@+id/atv”/>
<SeekBar
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_below=”@+id/atv”
android:layout_marginTop=”50dp”
android:progress=”100″
android:id=”@+id/sb”/>
</RelativeLayout>
packageiwangzhe.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;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
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(){
@Override
publicvoidonProgressChanged(SeekBarseekBar,inti,booleanb){
RelativeLayout.LayoutParamslinearParams=(RelativeLayout.LayoutParams)atv.getLayoutParams();
linearParams.height=dip2px(MainActivity.this,50);
linearParams.width=i*sb.getWidth()/100;
atv.setLayoutParams(linearParams);
}
@Override
publicvoidonStartTrackingTouch(SeekBarseekBar){
}
@Override
publicvoidonStopTrackingTouch(SeekBarseekBar){
}
});
}
publicstaticintdip2px(Contextcontext,floatdpValue){
finalfloatscale=context.getResources().getDisplayMetrics().density;
return(int)(dpValue*scale+0.5f);
}
}
总结:
此控件运用起来,很简单,但是技术点很适用,以后做动态报表的自定义控件时,都会用到。
假如大家想要增加设置文本字体、加粗、下划线等款式,请参考该示例,自行扩展即可。
本文章,主要是为了让大家理解运用画布画笔自定义控件的过程,假如想在本人的项目中运用,请依据需求自行调整优化。

未经允许不得转载:IT技术网站 » Android自定义控件:自适应大小的文本控件
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载