• 
    

    
    

      99热精品在线国产_美女午夜性视频免费_国产精品国产高清国产av_av欧美777_自拍偷自拍亚洲精品老妇_亚洲熟女精品中文字幕_www日本黄色视频网_国产精品野战在线观看 ?

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

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

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

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

      DOIDOI:10.11907/rjdk.143658

      中圖分類號:TP301

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

      編號:16727800(2015)001003303

      基金項目基金項目:

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

      0 引言

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

      1 TextView控件機理

      要擴展TextView控件功能,首先要對TextView控件的實現(xiàn)機理有一定了解。Android的每一個控件雖然實現(xiàn)起來相當(dāng)復(fù)雜,但除了具體實現(xiàn)細節(jié)外,幾乎所有的可視控件都包含兩個要素:一個是與用戶的交互界面(UI),另一個是與用戶交互的用戶輸入事件。TextView作為Android的一個基礎(chǔ)控件也不例外,用戶界面是通過在畫布上繪制UI,用戶主要是鍵盤輸入以及觸摸屏輸入。因此,分析TextView控件的機理就是要搞清控件界面的繪制框架及其輸入過程。本文主要關(guān)注TextView控件的界面繪制步驟??丶腢I繪制操作通常分為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è)置寬度和高度。

      1.2 布局

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

      1.3 繪制

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

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

      2 擴展TextView設(shè)計

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

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

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

      3 擴展TextView實現(xiàn)

      3.1 主要代碼

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

      分配字符的方法IniLines()實現(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;//是否需要計算字間距

      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;//需要計算字間距

      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;//不需要計算字間距

      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) { //需要計算調(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()的實現(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)用實例

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

      4 結(jié)語

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

      辽阳市| 始兴县| 泊头市| 梅州市| 英德市| 杭锦旗| 佛坪县| 宁陕县| 万全县| 锦州市| 瑞安市| 辉南县| 浏阳市| 昭苏县| 会东县| 屯门区| 滦平县| 桂东县| 广平县| 公主岭市| 阜康市| 台东市| 乌恰县| 延长县| 佛冈县| 镶黄旗| 新晃| 南投县| 大竹县| 云梦县| 老河口市| 综艺| 博白县| 衡南县| 盐池县| 息烽县| 江华| 徐水县| 丰原市| 盘山县| 慈利县|