一篇文本跳動(dòng)控件,為你打開一扇大門王污,學(xué)會(huì)這兩點(diǎn)心得罢吃,控件你也會(huì)寫

本篇文章已授權(quán)微信公眾號(鴻洋)獨(dú)家發(fā)布

做開發(fā)已經(jīng)3年有余了,一事無成昭齐,一直靜不下心來安心做好一件事情尿招,很多時(shí)候內(nèi)心很浮躁,迷失了前行的方向阱驾,我該當(dāng)何去何從就谜?一個(gè)人閑下來的時(shí)候,總想放縱自己里覆,二個(gè)人在一起總會(huì)吵吵鬧鬧丧荐,在物質(zhì)驅(qū)使的年代里,活得一點(diǎn)不像自己喧枷,像我這樣的人虹统,還有多少人?

心里的話一直想找人說隧甚,讓大家見笑了车荔。本篇是講技術(shù)的,而不是來聽我的感慨戚扳。先來看看文本跳動(dòng)控件的最終效果圖:

跳動(dòng)文本控件

一款優(yōu)秀的 app 往往會(huì)有一些新穎的控件忧便,讓用戶在視覺與體驗(yàn)都會(huì)覺得很棒,第一時(shí)間留住用戶的心帽借。那么怎樣打造一款炫酷的自定義控件(只考慮編程層面)珠增?

群里有很大一部分童鞋反應(yīng),覺得寫自定義控件好難砍艾,害怕去寫蒂教,有的還沒開始就結(jié)束了,還有的開始沒多久就放棄了辐董,剩下的都在放棄的路上悴品。其實(shí)寫自定義控件并沒有那么難禀综,那我談?wù)勛约旱男牡眉蚝妫袃牲c(diǎn):

  1. 第一,拆分

  2. 第二定枷,模仿

控件的難點(diǎn)在于動(dòng)畫孤澎,動(dòng)畫往往有規(guī)律可循,那么我們需要把動(dòng)畫變慢去尋找規(guī)律欠窒,一是ui設(shè)計(jì)師給出相應(yīng)的參數(shù)覆旭,有時(shí)需要模仿競品的控件效果退子,可以在開發(fā)者選擇中設(shè)置動(dòng)畫縮放時(shí)長:

動(dòng)畫縮放時(shí)長

還可以通過adb命令進(jìn)行錄屏與截圖:

// 截屏
adb shell /system/bin/screencap -p /sdcard/a.png  
// 錄屏
adb shell screenrecord /sdcard/a.mp4
// 拉到電腦上
adb pull /sdcard/a.mp4 

動(dòng)畫變慢了,我們就可以進(jìn)行拆分型将,在平面(2D)動(dòng)畫中我們一般只考慮橫縱坐標(biāo)寂祥,那么就可以拆分為 xy 方向的動(dòng)畫,動(dòng)畫無非就是平移七兜,旋轉(zhuǎn)丸凭,縮放以及透明度變換中的一種或者幾種,化繁為簡腕铸,本篇會(huì)以文本跳動(dòng)控件具體講解惜犀。

第二點(diǎn)心得是模仿,騰訊狠裹,小米一直在模仿虽界,一直很成功,那么寫控件一樣涛菠,也需要模仿莉御,尤其對于初學(xué)者,更需要模仿俗冻。先不論你寫的控件是不是最優(yōu)方案颈将,性能是否有過渡損耗,動(dòng)手模仿寫一寫言疗,可以找些簡單例子練練手晴圾,強(qiáng)力推薦 啟艦大牛,寫的很細(xì)很全面噪奄,有時(shí)候需要知道競品使用了什么控件死姚,可以讓你少走許多彎路?推薦一款sdk自帶的工具:

viewer

看到這里勤篮,你有信心寫好上方跳動(dòng)文本控件嗎都毒?如果是你拿到這個(gè)控件需求,你會(huì)怎么分析碰缔?歡迎留言账劲,很多時(shí)候?qū)崿F(xiàn)方案往往不會(huì)是一種,你的答案金抡,可能對他人有所幫助瀑焦,接下來具體講解跳動(dòng)文本控件的實(shí)現(xiàn)。

跳動(dòng)文本案例

需求分析:

1梗肝、篩選出新舊文本的相同字符并記錄索引值

2榛瓮、計(jì)算篩選出每個(gè)相同字符在動(dòng)畫周期內(nèi)的偏移量

3、舊文本剩余字符的平移與透明度動(dòng)畫

4巫击、新文本剩余字符的平移與透明度動(dòng)畫

為了方便理解禀晓,簡化字符精续,延遲動(dòng)畫時(shí)長,效果如下:

跳動(dòng)文本控件

從以上效果圖可以知道粹懒,紅色字體的 wen 為已經(jīng)繪制的文本(舊文本)重付,白色的 tianxia 為即將繪制的文本(新文本),新舊文本相同的字符為 n 凫乖,針對新舊文本拆分 x堪夭,y 軸方向的動(dòng)畫:

舊文本

  • x 軸方向字符 n 平移
  • y 軸方向除 n 外的字符平移,透明度(向上平移拣凹,透明度0~1)

新文本

  • x 軸方向無動(dòng)畫
  • y 軸方向除 n 外的字符平移森爽,透明度(向上平移,透明度1~0)

拆分后你會(huì)發(fā)現(xiàn)就只有簡單的平移與透明度動(dòng)畫嚣镜,是不是一下就覺得簡單了許多爬迟。

篩選相同字符

直接看代碼:

    public static List<CharacterDiffResult> diff(CharSequence oldText, CharSequence newText) {
        List<CharacterDiffResult> differentList = new ArrayList<>();
        Set<Integer> skip = new HashSet<>();
        for (int i = 0; i < oldText.length(); i++) {
            char c = oldText.charAt(i);
            for (int j = 0; j < newText.length(); j++) {
                if (!skip.contains(j) && c == newText.charAt(j)) {
                    skip.add(j);
                    CharacterDiffResult different = new CharacterDiffResult();
                    different.c = c;
                    //  在舊文本中的位置
                    different.fromIndex = i;
                   // 在新文本中的位置
                    different.moveIndex = j;
                    differentList.add(different);
                    break;
                }
            }
        }
        return differentList;
    }

CharacterDiffResult類中存取了新舊文本的相同字符以及在新舊文本中的位置,這個(gè)方法應(yīng)該不難理解菊匿,接下來分析相同字符在動(dòng)畫周期內(nèi)的偏移量付呕。

相同字符偏移量

首先需要弄清一個(gè)概念,那就是繪制字符的x跌捆,y坐標(biāo):


/**
* text:要繪制的文字
* x:繪制原點(diǎn)x坐標(biāo)
* y:繪制原點(diǎn)y坐標(biāo)
* paint:用來做畫的畫筆
*/
public void drawText(String text, float x, float y, Paint paint)

這里的x徽职,y表示的是基線xy坐標(biāo)佩厚,具體請參考自定義控件之繪圖篇( 五):drawText()詳解

通過以下方法可以獲取新舊文本的基線x坐標(biāo):

   int layoutDirection = ViewCompat.getLayoutDirection(EvaporateTextView.this);
  // 獲取x坐標(biāo)
   oldStartX = layoutDirection == LAYOUT_DIRECTION_LTR ? getLayout().getLineLeft(0) : getLayout().getLineRight(0);

為了計(jì)算偏移量姆钉,需要存取新舊字符串中每個(gè)字符的寬度:

    mOldPaint.setTextSize(mTextSize);
    mOldPaint.setColor(getCurrentTextColor());
    mOldPaint.setTypeface(getTypeface());
    oldGapList.clear();
    for (int i = 0; i < mOldText.length(); i++) {
         // measureText 測量文本寬度
        oldGapList.add(mOldPaint.measureText(String.valueOf(mOldText.charAt(i))));
    }
}

通過以上參數(shù)就可以獲取到動(dòng)畫周期內(nèi)相同字符的偏移量(基線的x坐標(biāo)):

    /**
     * 獲取舊文本字符n的x坐標(biāo)
     *
     * @param from      舊文本字符n的索引位置
     * @param move      新文本字符n的索引位置
     * @param progress  當(dāng)前進(jìn)度
     * @param startX    新文本baseline的x坐標(biāo)
     * @param oldStartX 舊文本baseline的y坐標(biāo)
     * @param gaps      新文本每個(gè)字符對應(yīng)的x坐標(biāo)集合
     * @param oldGaps   舊文本每個(gè)字符對應(yīng)的x坐標(biāo)集合
     * @return
     */
    public static float getOffset(int from, int move, float progress, float startX, float oldStartX,
                                  List<Float> gaps, List<Float> oldGaps) {
                                  
        float dist = startX;
        for (int i = 0; i < move; i++) {
            dist += gaps.get(i);
        }
        
        float cur = oldStartX;
        for (int i = 0; i < from; i++) {
            cur += oldGaps.get(i);
        }

        return cur + (dist - cur) * progress;
    }

根據(jù)數(shù)學(xué)公式(n 字符x坐標(biāo) = 起點(diǎn) + (終點(diǎn) - 起點(diǎn))x 進(jìn)度),起點(diǎn)與終點(diǎn)分別對應(yīng)舊新文本字符 n 所在的 x 軸坐標(biāo)抄瓦,進(jìn)度的取值范圍為[0~1]潮瓶。

舊文本平移透明動(dòng)畫

  // 透明度動(dòng)畫
   mOldPaint.setAlpha((int) ((1 - pp) * 255));
  // pp 表示的是進(jìn)度
   float y = startY - pp * mTextHeight;
   // (oldGapList.get(i) - width) / 2 值為0   oldOffset + (oldGapList.get(i) - width) / 2
  // oldOffset 基線x坐標(biāo)  平移動(dòng)畫
   canvas.drawText(mOldText.charAt(i) + "", 0, 1, oldOffset, y, mOldPaint);
   oldOffset += oldGapList.get(i);

請參考注釋,或者下載demo對應(yīng)代碼理解钙姊,文末會(huì)給出demo地址毯辅,還有干貨喲。

新文本平移透明動(dòng)畫

動(dòng)畫比較簡單煞额,直接貼代碼:

 if (i < mText.length()) {
    if (!CharacterUtils.stayHere(i, differentList)) {
        // 漸顯效果 延遲 alpha 的計(jì)算稍微費(fèi)腦一點(diǎn)
        int alpha = (int) (255f / charTime * (progress * duration - charTime * i / mostCount));
        alpha = alpha > 255 ? 255 : alpha;
        alpha = alpha < 0 ? 0 : alpha;
        mPaint.setAlpha(alpha);
        mPaint.setTextSize(mTextSize);
        //   float pp = progress * duration / (charTime + charTime / mostCount * (mText.length() - 1));
        float pp = progress;
        float y = mTextHeight + startY - pp * mTextHeight;
        float width = mPaint.measureText(mText.charAt(i) + "");
        canvas.drawText(mText.charAt(i) + "", 0, 1, offset + (gapList.get(i) - width) / 2, y, mPaint);
    }
    offset += gapList.get(i);
}

跳動(dòng)文本講到這里就差不多了思恐,有什么不懂的地方,請留言討論膊毁。

總結(jié)

寫好控件在于分析與拆分胀莹,再復(fù)雜的動(dòng)畫也是由簡單的動(dòng)畫組合而成,先化繁為簡媚媒,后以簡組繁嗜逻,多練多寫,多借鑒更優(yōu)的實(shí)現(xiàn)方案缭召。本篇的文本跳動(dòng)控件栈顷,是否為你打開了一扇大門?

小編正在維護(hù)MeiWidgetView庫嵌巷,有炫酷的控件可以推薦萄凤,希望有人能夠和小編一起維護(hù),萬分感謝搪哪,給小編一顆 star靡努,才是最好最美的回報(bào)。

源碼地址

參考的相關(guān)文章地址:

https://github.com/hanks-zyh/HTextView
https://blog.csdn.net/harvic880925/article/details/50995268

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晓折,一起剝皮案震驚了整個(gè)濱河市惑朦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌漓概,老刑警劉巖漾月,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異胃珍,居然都是意外死亡梁肿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門觅彰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吩蔑,“玉大人,你說我怎么就攤上這事填抬≈蚍遥” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵飒责,是天一觀的道長蛀骇。 經(jīng)常有香客問我,道長读拆,這世上最難降的妖魔是什么擅憔? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮檐晕,結(jié)果婚禮上暑诸,老公的妹妹穿的比我還像新娘。我一直安慰自己辟灰,他們只是感情好个榕,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著芥喇,像睡著了一般西采。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上继控,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天械馆,我揣著相機(jī)與錄音胖眷,去河邊找鬼。 笑死霹崎,一個(gè)胖子當(dāng)著我的面吹牛珊搀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尾菇,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼境析,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了派诬?” 一聲冷哼從身側(cè)響起劳淆,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎默赂,沒想到半個(gè)月后沛鸵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡放可,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年谒臼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耀里。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蜈缤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出冯挎,到底是詐尸還是另有隱情底哥,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布房官,位于F島的核電站趾徽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏翰守。R本人自食惡果不足惜孵奶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜡峰。 院中可真熱鬧了袁,春花似錦、人聲如沸湿颅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽油航。三九已至崭庸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背怕享。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工执赡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人熬粗。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓搀玖,卻偏偏與公主長得像余境,于是被迫代替她去往敵國和親驻呐。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容