1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 7620字,需瀏覽 16分鐘

         ·

        2021-04-25 12:51

        QQ效果:


        最終效果:



        分析

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

        2. 字體大小越來越小


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


        2.1 問題分解


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


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




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

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


        2.2 如果Rect寬大于高


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

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

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


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

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


        2.3 如果Rect高大于寬


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


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


        核心代碼

        3.1 定義Tag對象


        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}

        這個class的作用類似記錄器,記錄每一個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);        //計算當(dāng)前rect的寬高        int rectWidth = pRight - pLeft;        int rectHeight = pBottom - pTop;        if (rectWidth > rectHeight) {            //父布局長大于高,橫向布局合適            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 { //父布局高大于長,縱向布局合適 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); } } }

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

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

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

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


        瀏覽 51
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            久久久久久久中文字幕 | 少妇高潮在线视频 | 久久久999国产视频 | 婷婷久久色 | 40分钟床上色一级毛片 | 强 伦久久久久久久1234裸头 | 日本高清不卡在线视频 | 手指喷水h | 激情五月开心五月一级视频 | 亚洲精品无码成人A片在线网址 |