国产日韩欧美一区二区三区三州_亚洲少妇熟女av_久久久久亚洲av国产精品_波多野结衣网站一区二区_亚洲欧美色片在线91_国产亚洲精品精品国产优播av_日本一区二区三区波多野结衣 _久久国产av不卡

?

一種擴(kuò)展的Android文本顯示控件實(shí)現(xiàn)

2015-03-02 12:03:19朱明東
軟件導(dǎo)刊 2015年1期

摘要:Android自帶的文本顯示控件TextView往往難以滿足排版要求。以兩端對齊排版要求為例,實(shí)現(xiàn)能夠兩端對齊的文本顯示控件ExTextView,為擴(kuò)展TextView功能提供了方法和思路。

關(guān)鍵詞:Android;文本顯示;控件TextView;ExTextView

DOIDOI:10.11907/rjdk.143658

中圖分類號:TP301

文獻(xiàn)標(biāo)識碼:A 文章編號文章

編號:16727800(2015)001003303

基金項(xiàng)目基金項(xiàng)目:

作者簡介作者簡介:朱明東(1975-),男,碩士,國防信息學(xué)院一系講師,研究方向?yàn)閿?shù)據(jù)處理、數(shù)據(jù)分析、數(shù)據(jù)管理。

0 引言

TextView控件在Android開發(fā)中應(yīng)用比較廣泛,只要有文本顯示要求時,通常都會用到它。但是TexwView控件并不十分完美,它在顯示文本時,特別是有中西文混合文本時,往往顯得參差不齊、不夠工整,影響了排版效果,不能滿足“兩端對齊”這一中文顯示的基本要求。Android實(shí)現(xiàn)文本兩端對齊顯示的基本方法有3種:①將文本轉(zhuǎn)換為html格式,用WebView控件顯示;②棄用TexwView控件,重新實(shí)現(xiàn)一個具有兩端對齊功能的新控件;③在TexwView控件的基礎(chǔ)上,擴(kuò)展實(shí)現(xiàn)兩端對齊功能。第一種方法需要在文本顯示前把文本轉(zhuǎn)換為html格式,這需要程序員對html格式相當(dāng)熟悉;第二種方法是重新實(shí)現(xiàn)一個文本顯示控件,需要對Android控件的實(shí)現(xiàn)機(jī)理有深入研究[1],對程序員的能力要求比較高;第三種方法相對簡單,只需在現(xiàn)有控件的基礎(chǔ)上,覆蓋文本輸出方法。本文采用第三種方法,即擴(kuò)展TextView控件功能。

1 TextView控件機(jī)理

要擴(kuò)展TextView控件功能,首先要對TextView控件的實(shí)現(xiàn)機(jī)理有一定了解。Android的每一個控件雖然實(shí)現(xiàn)起來相當(dāng)復(fù)雜,但除了具體實(shí)現(xiàn)細(xì)節(jié)外,幾乎所有的可視控件都包含兩個要素:一個是與用戶的交互界面(UI),另一個是與用戶交互的用戶輸入事件。TextView作為Android的一個基礎(chǔ)控件也不例外,用戶界面是通過在畫布上繪制UI,用戶主要是鍵盤輸入以及觸摸屏輸入。因此,分析TextView控件的機(jī)理就是要搞清控件界面的繪制框架及其輸入過程。本文主要關(guān)注TextView控件的界面繪制步驟。控件的UI繪制操作通常分為3步,分別是測量、布局和繪制。

1.1 測量

對于一個可視控件,必須確定其所占空間的大小,所以TextView要重寫父類View的成員函數(shù)onMeasure。該函數(shù)有兩個參數(shù),分別是用來描述寬度測量規(guī)范的widthMeasureSpec和高度測量規(guī)范的heightMeasureSpec。測量規(guī)范使用1個int值來表示,這個int值包含了2個分量。第1個是mode分量,使用最高2位來表示。測量模式有3種,分別是MeasureSpec.UNSPECIFIED(0)、MeasureSpec.EXACTLY(1)和MeasureSpec.AT_MOST(2);第2個是size分量,使用低30位來表示。當(dāng)mode分量等于MeasureSpec.EXACTLY時,size分量的值就是父視圖設(shè)置的寬度或者高度;當(dāng)mode分量等于MeasureSpec.AT_MOST時,size分量的值就是父視圖限定當(dāng)前控件設(shè)置的最大寬度或者高度;當(dāng)mode分量等于MeasureSpec.UNSPECIFIED時,父視圖不限定當(dāng)前控件所設(shè)置的寬度或者高度,這時候當(dāng)前控件就按照實(shí)際需求來設(shè)置寬度和高度。

1.2 布局

通過測量后確定了控件的大小,但是控件的位置還未確定??丶奈恢檬峭ㄟ^布局這個操作來完成的。Android可視控件是按照樹形結(jié)構(gòu)組織在一起的,其中,子控件的位置由父控件來設(shè)置,也就是說,只有容器類控件才執(zhí)行布局操作,通過重寫父類View的成員函數(shù)onLayout來實(shí)現(xiàn)。由于TextView控件不是容器類控件,因此,它可以不重寫父類View的成員函數(shù)onLayout。

1.3 繪制

經(jīng)過測量和布局操作后,就確定了控件TextView的大小和位置,接下來繪制UI??丶榱死L制UI,必須重寫父類View的成員函數(shù)onDraw。該函數(shù)只有一個參數(shù)canvas,canvas描述的是一塊畫布,控件的UI就是繪制在這塊畫布上的。畫布提供了豐富的接口來繪制UI,例如畫線(drawLine)、畫圓(drawCircle)、輸出文字(drawText)和貼圖(drawBitmap)等等。有了這些UI畫圖接口之后,就可以隨心所欲地繪制控件的UI了。

通過分析TextView控件的機(jī)理,不難發(fā)現(xiàn),TextView對文本的顯示是通過在畫布(canvas)上輸出文本實(shí)現(xiàn)的。因此要在TextView的基礎(chǔ)上實(shí)現(xiàn)文本的兩端對齊,關(guān)鍵是要重新安排每一行的字符數(shù),控制字間距,在TextView的畫布(canvas)上精確地輸出每一個字符,從而確保每一行的第一個字符和最后一個字符是對齊的。

2 擴(kuò)展TextView設(shè)計(jì)

2.1 兩端對齊顯示的基本要求

要實(shí)現(xiàn)文本的兩端對齊,表面上是每行的最后一個字符在縱坐標(biāo)上保持一致,其實(shí)還要考慮文本在顯示格式上的要求,特別是每一行的第一個字符(行首)和最后一個字符(行尾)是否符合格式規(guī)范。需要考慮的格式規(guī)范要求有:①行首字符不能是以下字符:句號(。.)、問號(??)、嘆號(!?。⒍禾枺?,,)、冒號(::)和分號(;;)和引號(””)、括號())]})、書名號(》>)的后一半等;②行尾字符不能是以下字符:引號(“”)、括號((([{)、書名號(《<)的前一半。

2.2 ExTextView設(shè)計(jì)[2]

ExTextView繼承了Android的TextView,其繼承關(guān)系如圖1所示,ExTextView的類圖如圖2所示。

ExTextView主要的屬性包括文本高度(m_iTextHeight)、文本寬度(m_iTextWidth)、畫筆(mPaint)、文本(text)、行間距(LineSpace)、左邊距(left_Margin)、右邊距(right_Margin)、上邊距(top_Margin)、下邊距(bottom_Margin)、字體高度(m_iFontHeight)、所有行屬性(strings)。

其中,描述行屬性的內(nèi)部類Tlineattr包含3個成員,分別是該行所包含的字符串(linetext)、該行的字間距(extrawidth)和該行每一個字符的輸出寬度(widths)。

ExTextView的機(jī)理是:確定每一行字符屬性的IniLines()方法和覆蓋父類的OnDraw()方法。IniLines()方法主要是確定第一行所包含的字符,保證行首字符和行尾字符符合格式規(guī)范的要求,其判斷邏輯如圖3所示。OnDraw()方法主要是根據(jù)每一行所包含的字符,確定字間距,保證所有行首字符的水平坐標(biāo)值一致,所有行尾字符的水平坐標(biāo)值一致,操作流程如圖4所示。

3 擴(kuò)展TextView實(shí)現(xiàn)

3.1 主要代碼

ExTextView要能夠?qū)崿F(xiàn)兩端對齊,其核心是先要分配每一行的字符,然后重寫父類的成員函數(shù)onDraw ()。

分配字符的方法IniLines()實(shí)現(xiàn)如下:

private void IniLines() {

strings.clear();

String Text = text;//得到要輸出的文本

intm_LineWidth = m_iTextWidth - left_Margin - right_Margin; //可輸出的畫布寬度

float[] widths = new float[Text.length()]; //保存每個字符所占寬度的數(shù)組

mPaint.getTextWidths(Text, widths); //得到每個字符輸出時的寬度

final String Laststr = "((《“{[<"; //不能是行尾的字符

final String Firststr = "、,。;:)?”,.;:)》?/-]}>"; //不能是行首的字符

float curwidth = 0; //保存當(dāng)前行所含字符的總的寬度

intstartindex = 0; //行開始的字符位置

intendindex = 0; //行結(jié)束的字符位置

String lastch = ""; //上一個字符

Boolean IsCalExtraWidth = false;//是否需要計(jì)算字間距

for (inti = 0; i

Boolean lineclosed = false; //當(dāng)前行是否結(jié)束

String ch = Text.substring(i, i + 1); //得到當(dāng)前字符

curwidth = curwidth + widths[i]; //當(dāng)前行已輸出字符的寬度

if (curwidth>m_LineWidth) { //超出了行寬

lineclosed = true;//當(dāng)前行結(jié)束

IsCalExtraWidth = true;//需要計(jì)算字間距

if (Firststr.contains(ch)) { //如果當(dāng)前字符不能為行首,則當(dāng)前字符為該行的行尾

endindex = i; //當(dāng)前行結(jié)束的字字符位置

curwidth = 0; //下一行的行寬

} else { //當(dāng)前字符可以為行首

if (Laststr.contains(lastch)) { //上一個字符不能為行尾,則上一個字符為下一行的行首

endindex = i - 2;//當(dāng)前行的行尾為當(dāng)前字符的前兩個字符

curwidth = widths[i - 1] + widths[i]; //下一行的行寬

} else { //上一個字符可以成為行尾

endindex = i - 1; //當(dāng)前行的行尾為上一個字符

curwidth = widths[i]; //下一行的行寬

}

}

lastch = ch;

}

if (!lineclosed) {

if (ch.equals("n") || i == Text.length() - 1) { //或當(dāng)前字符為換行符或文本最后一個字符,則當(dāng)前行結(jié)束

lineclosed = true;

endindex = i;

IsCalExtraWidth = false;//不需要計(jì)算字間距

curwidth = 0;

}

}

if (lineclosed) { //如果當(dāng)前行結(jié)束

intlen = endindex - startindex + 1;//當(dāng)前行字符個數(shù)

float[] linewidths = new float[len]; //當(dāng)前行每一個字符的寬度

float linewidth = 0; //當(dāng)前行所有字符輸出寬度之和

for (int j = startindex; j

linewidths[j - startindex] = widths[j];

linewidth = linewidth + widths[j];

}

float extrawidth = 0;//調(diào)整字間距

if (IsCalExtraWidth) { //需要計(jì)算調(diào)整字間距

extrawidth = (m_LineWidth - linewidth) / len;

}

stringlist.add(Text.substring(startindex, endindex + 1)); //當(dāng)前行所包含的字符

strings.add(new Tlineattr(Text.substring(startindex, endindex + 1), extrawidth, linewidths));

startindex = endindex + 1; //下一行的起始字符的位置

}

}//結(jié)束對字符的歷遍

if (endindex != (Text.length() - 1)) { //若上一行的行尾不是文本的最后一個字符,則還剩最后一行

endindex = Text.length() - 1;

intlen = endindex - startindex + 1;//當(dāng)前行字符個數(shù)

float[] linewidths = new float[len]; //當(dāng)前行每一個字符的寬度

float linewidth = 0; //當(dāng)前行所有字符輸出寬度之和

for (int j = startindex; j

linewidths[j - startindex] = widths[j];

linewidth = linewidth + widths[j];

}

stringlist.add(Text.substring(startindex, endindex + 1)); //當(dāng)前行所包含的字符

strings.add(new Tlineattr(Text.substring(startindex, endindex + 1), 0, linewidths));

}

}

成員函數(shù)onDraw()的實(shí)現(xiàn)代碼如下。

@Override

protected void onDraw(Canvas canvas) {

float drawx=0; //繪制字符的橫坐標(biāo)

float drawy=m_iFontHeight; //繪制字符的縱坐標(biāo)

for(inti=0;i

drawx = left_Margin;

for (int j=0;j

String ch=strings.get(i).linetext.substring(j,j+1); //取一個字符

canvas.drawText(ch,drawx,drawy,mPaint); // 繪制當(dāng)前字符

drawx=drawx+ strings.get(i).extrawidth+strings.get(i).widths[j]; //下一個字符的橫坐標(biāo)

}//結(jié)束當(dāng)前行的循環(huán)

drawy=drawy+m_iFontHeight; //下一行字符的縱坐標(biāo)

}//結(jié)束所有行的循環(huán)

}

3.2 應(yīng)用實(shí)例

ExTextView控件的使用與TextView控件的使用是一樣的,這里不作詳細(xì)介紹。對于同一段文本,圖5是Android自帶的TextView顯示效果,圖6是ExTextView的顯示效果。

4 結(jié)語

具有兩端對齊功能的ExTextView控件,還不十分完善。比如,為防止英文單詞被截?cái)?,需要加上中英文混合分詞技術(shù),等等。本文只是提供了擴(kuò)展TextView功能一種思路,讀者可以沿著這種思路不斷擴(kuò)展,實(shí)現(xiàn)所需功能。

贡山| 田林县| 睢宁县| 安阳市| 郑州市| 桐城市| 宣化县| 鲁甸县| 高安市| 乌兰浩特市| 正定县| 津南区| 孟州市| 阿巴嘎旗| 辛集市| 绩溪县| 双桥区| 文安县| 桐庐县| 吕梁市| 资源县| 云南省| 游戏| 板桥市| 从江县| 新干县| 三河市| 云南省| 景德镇市| 托克托县| 邢台市| 泾川县| 江达县| 揭西县| 喀喇沁旗| 镇坪县| 鹤峰县| 甘德县| 绥芬河市| 儋州市| 武山县|