<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android仿QQ個(gè)性標(biāo)簽功能

          共 7620字,需瀏覽 16分鐘

           ·

          2021-04-25 12:51

          QQ效果:


          最終效果:



          分析

          從效果大致可以看出兩個(gè)規(guī)律:
          1. 字體的矩形面積越來越小

          2. 字體大小越來越小


          很像廢話吧,不是的。除了字體,我們還能看到文字有豎向排行有橫向排列,而且沒有規(guī)律。


          2.1 問題分解


          假設(shè)我們只有一個(gè)標(biāo)簽文字,可以選擇自定義View(當(dāng)然可以選擇自定義ViewGroup),然后隨機(jī)標(biāo)簽文字的left和top,文字大小從30sp開始,然后在onDraw里面繪制矩形,在矩形里面繪制文字。


          繪制第一個(gè)標(biāo)簽文字之后,我們想繪制第二個(gè)標(biāo)簽文字,如果我們還在當(dāng)前的View里面去隨機(jī)一個(gè)Rect,可能會(huì)和第一個(gè)標(biāo)簽重合,那怎么辦?我們想到了裁剪,看下圖:




          沿著標(biāo)簽我們可以將View切成Rect①、Rect②、Rect③、Rect④,這個(gè)時(shí)候我們分別將四塊矩形看成新的View去繪制一個(gè)標(biāo)簽文字。

          這樣大問題就化解成了許許多多的小問題。


          2.2 如果Rect寬大于高


          1. 如果標(biāo)簽文字的高度大于Rect的高度,我們可以遞減標(biāo)簽文字的TextSize,一直到標(biāo)簽文字的高度小于Rect的高度,我們直接從Rect的Left開始繪制標(biāo)簽就可以,看圖:

          第一個(gè)標(biāo)簽繪制完成之后,繼續(xù)在這個(gè)標(biāo)簽的右邊重復(fù)繪制第一個(gè)標(biāo)簽大小的標(biāo)簽,一直到Rect剩余的空間不足以繪制一個(gè)當(dāng)前的大小的標(biāo)簽。

          2. 如果文字的寬度大于Rect的寬度,同樣的我們也遞減標(biāo)簽文字的TextSize,一直到標(biāo)簽文字的寬度小于Rect的寬度,我們直接從Rect的Top開始繪制標(biāo)簽就可以,看圖:


          第一個(gè)標(biāo)簽繪制完成之后,繼續(xù)在這個(gè)標(biāo)簽的下邊重復(fù)繪制第一個(gè)標(biāo)簽大小的標(biāo)簽,一直到Rect剩余的空間不足以繪制一個(gè)當(dāng)前的大小的標(biāo)簽。

          3. 如果以上都不滿足,說明標(biāo)簽的寬高都遠(yuǎn)小于Rect的寬高,那就變成了我們最開始的大問題,隨機(jī)標(biāo)簽文字的left和top,再切四個(gè)Rect出來,重復(fù)以上步驟。


          2.3 如果Rect高大于寬


          Rect高大于寬,標(biāo)簽適合豎向排列,豎向排列考慮起來比較簡(jiǎn)單,不需要隨機(jī)一個(gè)位置開始豎向,就從Rect的Left開始排列,看起來整齊,看圖:


          第一個(gè)標(biāo)簽繪制完成之后,繼續(xù)在標(biāo)簽的右邊重復(fù)繪制第一個(gè)標(biāo)簽大小的標(biāo)簽,一直到Rect右邊剩余的空間不足以繪制一個(gè)當(dāng)前的大小的標(biāo)簽,然后將剩下的空間切成Rect①和Rect②,重復(fù)以上步驟。


          核心代碼

          3.1 定義Tag對(duì)象


          public class Tag {    private String name;    private int left;    private int top;    private int right;    private int bottom;    private int textsize;
          // 省略構(gòu)造函數(shù)和setter getter}

          這個(gè)class的作用類似記錄器,記錄每一個(gè)Tag的位置和文字大小信息。


          3.2 核心函數(shù)

              public void computeSingleRect(List<String> tags, int textSize, int pLeft, int pTop, int pRight, int pBottom) {        if (tags == null || tags.size() == 0 || textSize < MIN_TEXT_SIZE || pBottom == 0 || pRight == 0 || pLeft >= pRight || pTop >= pBottom) {            return;        }        int cLeft = 0;        int cTop = 0;        int cRight = 0;        int cBottom = 0;        int textWidth = 0;        int textHeight = 0;        int size = tags.size();        int index = (int) (Math.random() * (size - 1));        String name = tags.get(index);        //計(jì)算當(dāng)前rect的寬高        int rectWidth = pRight - pLeft;        int rectHeight = pBottom - pTop;        if (rectWidth > rectHeight) {            //父布局長(zhǎng)大于高,橫向布局合適            paint.setTextSize(textSize);            textWidth = (int) paint.measureText(name);            textHeight = (int) (paint.getFontMetrics().bottom - paint.getFontMetrics().top);            if (textHeight > rectHeight) {                //記錄之前的textsize                int beforeTextSize = textSize;                while (textHeight > rectHeight) {                    textSize--;                    paint.setTextSize(textSize);                    textHeight = (int) (paint.getFontMetrics().bottom - paint.getFontMetrics().top);                }                textWidth = (int) paint.measureText(name);                while (textWidth > rectWidth) {                    textSize--;                    paint.setTextSize(textSize);                    textWidth = (int) paint.measureText(name);                }                if(textSize < MIN_TEXT_SIZE){                    return;                }                textHeight = (int) (paint.getFontMetrics().bottom - paint.getFontMetrics().top);                cLeft = pLeft;                cTop = pTop;                cRight = textWidth + pLeft;                cBottom = textHeight + pTop;                showTags.add(new Tag(name, textSize, cLeft, cTop, cRight, cBottom));
          textWidth = (int) paint.measureText(name); if (pRight - cRight > textWidth) { //右 computeSingleRect(tags, beforeTextSize, cRight, pTop, pRight, pBottom); } else { //右 computeSingleRect(tags, --textSize, cRight, pTop, pRight, pBottom); } } else { if (textWidth >= rectWidth) { while (textWidth > rectWidth) { textSize--; paint.setTextSize(textSize); textWidth = (int) paint.measureText(name); } if(textSize < MIN_TEXT_SIZE){ return; } textHeight = (int) (paint.getFontMetrics().bottom - paint.getFontMetrics().top); cLeft = pLeft; cTop = pTop; cRight = pRight; cBottom = cTop + textHeight; showTags.add(new Tag(name, textSize, cLeft, cTop, cRight, cBottom));
          //下 textSize += 4; computeSingleRect(tags, textSize, cLeft, cBottom, cRight, pBottom); } else { cLeft = (int) (Math.random() * (rectWidth / 3)) + pLeft; // 除以3是為了盡快找到合適的位置 while (cLeft + textWidth > pRight) { cLeft--; } cTop = (int) (Math.random() * (rectHeight / 2)) + pTop; while (cTop + textHeight > pBottom) { cTop--; } cRight = cLeft + textWidth; cBottom = cTop + textHeight; showTags.add(new Tag(name, textSize, cLeft, cTop, cRight, cBottom));
          //左 computeSingleRect(tags, --textSize, pLeft, pTop, cLeft, cBottom); //上 computeSingleRect(tags, --textSize, cLeft, pTop, pRight, cTop); //右 computeSingleRect(tags, --textSize, cRight, cTop, pRight, pBottom); //下 computeSingleRect(tags, --textSize, pLeft, cBottom, cRight, pBottom); } } } else { //父布局高大于長(zhǎng),縱向布局合適 int beforeTextSize = textSize; paint.setTextSize(textSize); textHeight = (int) (paint.getFontMetrics().bottom - paint.getFontMetrics().top); while (textHeight * name.length() > rectHeight) { textSize--; paint.setTextSize(textSize); textHeight = (int) (paint.getFontMetrics().bottom - paint.getFontMetrics().top); } if(textSize < MIN_TEXT_SIZE){ return; } textWidth = (int) (paint.measureText(name) / name.length()); int length = name.length(); if (pLeft + textWidth > pRight) { //右 右邊空間不足 computeSingleRect(tags, --textSize, pLeft, pTop, pRight, pBottom); return; } for (int i = 0; i < length; i++) { cLeft = pLeft; cTop = pTop + i * textHeight; cRight = cLeft + textWidth; cBottom = cTop + textHeight; showTags.add(new Tag(String.valueOf(name.charAt(i)), textSize, cLeft, cTop, cRight, cBottom)); } if (pRight - cRight > textWidth) { //右 computeSingleRect(tags, beforeTextSize, cRight, pTop, pRight, cBottom); //下 computeSingleRect(tags, --textSize, pLeft, cBottom, pRight, pBottom); } else { //右 computeSingleRect(tags, --textSize, cRight, pTop, pRight, cBottom); //下 computeSingleRect(tags, --textSize, pLeft, cBottom, pRight, pBottom); } } }

          很清楚的看到,是一個(gè)遞歸函數(shù),一開始就是遞歸的結(jié)束條件。注意里面的切割Rect的方法,pLeft、pTop、pRight、pBottom代表父Rect的邊界,cLeft、cTop、cRight、cBottom代表Tag的邊界。里面有一個(gè)很巧妙的記錄進(jìn)入條件時(shí)候的TextSize,目的是讓下一次遞歸還繼續(xù)進(jìn)入這個(gè)條件下,也就做到了重復(fù)繪制相同大小的Tag的目的。

          但是在textWidth >= rectWidth這個(gè)條件下記錄TextSize卻容易造成最后一個(gè)Tag繪制不出來,導(dǎo)致留白區(qū)域大,有一點(diǎn)瑕疵,但是整體目的達(dá)到了。

          Github地址:
          https://github.com/rjpacket/QQTagCloudView

          到這里就結(jié)束啦。


          瀏覽 51
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  99这里只有精品 | 一级 黄 色情 片 | 免费国产A片 | 免费操逼黄片 | 久9热在线视频 |