成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Android仿百度貼吧客戶端Loading小球

企業(yè)動態(tài)
使用百度貼吧客戶端的時候發(fā)發(fā)現(xiàn)加載的小動畫挺有意思的,于是自己動手寫寫看。想學(xué)習(xí)自定義View以及自定義動畫的小伙伴一定不要錯過哦。

 

封面

前言

使用百度貼吧客戶端的時候發(fā)發(fā)現(xiàn)加載的小動畫挺有意思的,于是自己動手寫寫看。想學(xué)習(xí)自定義View以及自定義動畫的小伙伴一定不要錯過哦。

讀者朋友需要有最基本的canvas繪圖功底,比如畫筆Paint的簡單使用、Path如何畫直線等簡單的操作,不熟悉也沒關(guān)系,下文帶大家擼代碼的時候會簡單的講一下。

此篇文章用到如下知識點:

  • 1)、自定義View的測量
  • 2)、自定義View屬性的自定義及使用
  • 3)、Path繪制貝塞爾曲線
  • 4)、Canvas的裁剪
  • 5)、用ValueAnimator控制動畫
  • 6)、Canvas文字居中

好了,開始正文!

一、準(zhǔn)備工作

1、效果圖

loading小球

2、動畫拆解

直觀的看我們要實現(xiàn)三個方面

1)、波浪動畫(藍色部分)

2)、不規(guī)則的文字(白色的半個“貼”字)

3)、控件顯示部分限制成圓形

3、技術(shù)分析

1)、波浪動畫

要實現(xiàn)波浪動畫,首先要繪制出波浪的形狀,其次再讓他動起來。波浪線看起來有點像是正弦或者余弦函數(shù),但是Android的Path并沒有提供繪制正余弦圖形的函數(shù),但是提供了一個功能更強大的曲線——貝塞爾曲線,貝塞爾曲線分為二階、三階及多階,本案例里使用的是二次貝塞爾曲線,如下圖所示,二階貝塞爾曲線需要三個點才可以確定

二階貝塞爾曲線

我們來看一下Android里貝塞爾曲線的源碼:

  1. /* @param x1 The x-coordinate of the control point on a quadratic curve 
  2.      * @param y1 The y-coordinate of the control point on a quadratic curve 
  3.      * @param x2 The x-coordinate of the end  on a quadratic curve 
  4.      * @param y2 The y-coordinate of the end point on a quadratic curve 
  5.      */ 
  6.     public void quadTo(float x1, float y1, float x2, float y2) { 
  7.         isSimplePath = false
  8.         native_quadTo(mNativePath, x1, y1, x2, y2); 
  9.     } 

由注解可以看出來quadTo(float x1, float y1, float x2, float y2)的四個參數(shù)分別是控制點的x,y坐標(biāo),結(jié)束點的x,y坐標(biāo),少了一個開始點呀!不要著急,開始點是Path路徑的上一次結(jié)束的點,如果你的Path沒有繪制過路徑,那么Path的最后一個點坐標(biāo)就是(0,0)如果想自己定義起始點位置,就用Path.moveTo(float x, float y)即可。

但是每次都需要指定具體的控制點和結(jié)束點既麻煩又容易出錯,那么就需要rQuadTo(float dx1, float dy1, float dx2, float dy2)出馬了,rQuadTo跟quadTo的區(qū)別在于rQuadTo使用的是相對起始點的坐標(biāo),而不是具體的坐標(biāo)點,舉個例子,如下代碼效果等價:

  1. //使用quadTo 
  2.     Path path=new Path(); 
  3.     path.moveTo(100,100); 
  4.     path.quadTo(150,0,200,100); 
  5.  
  6.     //使用rQuadTo 
  7.     Path path=new Path(); 
  8.     path.moveTo(100,100); 
  9.     path.rQuadTo(50,-100,100,0); 

此時畫筆最后的落點都為(200,100)。

畫波浪線的技術(shù)難點解決了那么如何讓波浪動起來呢,想動起來肯定需要波浪在水平方向移動,那么我們需要畫一個很長很長的波浪讓他移動,這樣就實現(xiàn)了上下起伏效果,但是這樣需要畫無數(shù)多條貝塞爾曲線,肯定不行,這時就用到萬能的數(shù)學(xué)理論——周期函數(shù)了,如果我們繪制兩個周期的貝塞爾曲線,每次只讓它顯示一個周期,然后等第二周期顯示結(jié)束的時候再從頭開始,這樣就造成了無限周期的假象,如下圖

初始位置為1,向右前進,當(dāng)走到2位置的時候重置成3的位置,即1原始的位置,如此往復(fù)就成了綿綿不絕的波浪了

綿延原理

做成效果如下:黃色區(qū)域就是要顯示的區(qū)域,藍色豎線是波浪線兩個周期的總長度

連綿不絕的波浪線

2)、不規(guī)則的文字

我們可以看到圓球里的“貼”字在波浪區(qū)域顯示的是白色,波浪區(qū)域之外顯示的是藍色,Android并不支持給文字部分區(qū)域著色的功能,那么我們只能靠控制顯示區(qū)域讓文字只顯示特定形狀,強大的Canvas正好有畫布裁剪功能,通過裁剪畫布就能控制繪制區(qū)域,畫布的裁剪可以用Canvas.clipPath(Path path)實現(xiàn),傳入一個閉合的Path既可以隨心所欲裁剪畫布,裁剪示意圖如下

裁剪文字

利用波浪形閉合路徑講畫布裁剪成波浪形,那么在此接下來的Canvas繪制操的內(nèi)容只能在這個波浪形區(qū)域里顯示,這樣就解決了文字的部分區(qū)域顯示問題。那么接下來我們只用在相同位置繪制相同字體、字號不同色的文字即可實現(xiàn)一個文字顯示兩種顏色了(注意:實際操作的時候,被裁剪的文字要蓋在未被裁減的文字的上邊,即先在畫布裁剪之前繪制藍色的“貼”字,然后再裁剪畫布再在裁剪后的畫布上繪制白色的“貼”)

3)、控件顯示部分限制成圓形

經(jīng)過2)的分析,將顯示部分限制在圓形區(qū)域里不是易如反掌嗎,使用一個圓形的Path裁剪畫布即可。感興趣的同學(xué)也可以嘗試BitmapShader或者Xfermode來將顯示區(qū)域變成圓形

好了,最主要的步驟都分析完了,上一張圖更直觀地展示一下繪制流程

整體分析圖

圖中可以看出波浪形的閉合Path有兩個作用,一個是負責(zé)裁剪畫布,一個是負責(zé)繪制藍色,其實只用第一個功能即可,此處只是方便分解步驟。

二、代碼實現(xiàn)

文章只貼出主要代碼,完整代碼文末提供鏈接

既然是自定義控件,那就要有通用性比如下邊的效果:

各種顏色的球球

loading小球需文字和顏色都可以改變,所以我們要給自己的控件添加這兩個屬性。首先在“res/values/”路徑下新建一個attrs.xml文件,在里邊定義如下屬性:

  1. <declare-styleable name="Wave"
  2.         <attr name="color" format="color"/> 
  3.         <attr name="text" format="string"/> 
  4.     </declare-styleable> 

接下來開始自定義View

復(fù)寫三個構(gòu)造函數(shù),將單參數(shù)和雙參數(shù)的構(gòu)造函數(shù)的super方法都改為this,保證無論調(diào)用哪個構(gòu)造方法都會跳到三個參數(shù)的構(gòu)造方法中,這樣就可以偷懶只用在三個參數(shù)的構(gòu)造方法里初始化各種參數(shù)了

  1. public class Wave extends View { 
  2.     public Wave(Context context) { 
  3.         this(context,null); 
  4.     } 
  5.  
  6.     public Wave(Context context, AttributeSet attrs) { 
  7.         this(context, attrs,0); 
  8.     } 
  9.  
  10.     public Wave(Context context, AttributeSet attrs, int defStyleAttr) { 
  11.         super(context, attrs, defStyleAttr); 
  12.         //初始化參數(shù) 
  13.         init(context,attrs); 
  14.     } 

接下來是初始化函數(shù),在此處我們獲取到自定義的顏色及文字參數(shù),并初始化各種畫筆,代碼比較簡單,看注釋內(nèi)容即可

  1. private void init(Context context, AttributeSet attrs) { 
  2.         //獲取自定義參數(shù)值 
  3.         TypedArray array =  context.obtainStyledAttributes(attrs, R.styleable.Wave); 
  4.         //自定義顏色和文字 
  5.         color = array.getColor(R.styleable.Wave_color, Color.rgb(41, 163, 254)); 
  6.         text = array.getString(R.styleable.Wave_text); 
  7.         array.recycle(); 
  8.         //圖形及路徑填充畫筆(抗鋸齒、填充、防抖動) 
  9.         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
  10.         mPaint.setStyle(Paint.Style.FILL); 
  11.         mPaint.setColor(color); 
  12.         mPaint.setDither(true); 
  13.         //文字畫筆(抗鋸齒、白色、粗體) 
  14.         textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
  15.         textPaint.setColor(Color.WHITE); 
  16.         textPaint.setTypeface(Typeface.DEFAULT_BOLD); 
  17.         //閉合波浪路徑 
  18.         path = new Path(); 
  19.     } 

接下來是生成波浪線的方法,示意圖如下:

波浪生成原理

將Path起點移動到最左邊粉色點處,然后繪制兩個周期的長度的波形(一上一下是一個周期),每個周期在x軸的跨度為此控件的寬度控制點距波形的軸線的絕對高度是整個控件的3/20,當(dāng)然想讓波形波動幅度大的話這個比例可以隨意調(diào)整,接下來就用前邊講到的rQuadTo( )來生成閉合的波浪圖形,其中mWidth為控件的寬度,mHeight為控件的高度

  1. private Path getActionPath(float percent) { 
  2.         Path path = new Path(); 
  3.         int x = -mWidth; 
  4.         //當(dāng)前x點坐標(biāo)(根據(jù)動畫進度水平推移,一個動畫周期推移的距離為一個周期的波長) 
  5.         x += percent * mWidth; 
  6.         //波形的起點 
  7.         path.moveTo(x, mHeight / 2); 
  8.         //控制點的相對寬度 
  9.         int quadWidth = mWidth / 4; 
  10.         //控制點的相對高度 
  11.         int quadHeight = mHeight / 20 * 3; 
  12.         //第一個周期波形 
  13.         path.rQuadTo(quadWidth, quadHeight, quadWidth * 2, 0); 
  14.         path.rQuadTo(quadWidth, -quadHeight, quadWidth * 2, 0); 
  15.         //第二個周期波形 
  16.         path.rQuadTo(quadWidth, quadHeight, quadWidth * 2, 0); 
  17.         path.rQuadTo(quadWidth, -quadHeight, quadWidth * 2, 0); 
  18.         //右側(cè)的直線 
  19.         path.lineTo(x + mWidth * 2, mHeight); 
  20.         //下邊的直線 
  21.         path.lineTo(x, mHeight); 
  22.         //自動閉合補出左邊的直線 
  23.         path.close(); 
  24.         return path; 
  25.     } 

上邊代碼所表示的閉合路徑如下圖

閉合的波浪圖形

接下來就是重頭戲onDraw了

  1. @Override 
  2.     protected void onDraw(Canvas canvas) { 
  3.         //底部的字 
  4.         textPaint.setColor(color); 
  5.         drawCenterText(canvas, textPaint, text); 
  6.         //上層的字 
  7.         textPaint.setColor(Color.WHITE); 
  8.         //生成閉合波浪路徑 
  9.         path = getActionPath(currentPercent); 
  10.         canvas.clipPath(path); 
  11.         //裁剪成圓形 
  12.         canvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, mPaint); 
  13.         drawCenterText(canvas, textPaint, text); 
  14.     } 

這里繪制思路是:在canvas上繪制藍色的文字 ——>將畫布裁剪成波浪形 ——>在波浪形畫布上繪制圓 ——>在波浪形畫布上繪制文字,這里一定要注意繪制順序,先繪制的在下部,后繪制的在上部。

細心的朋友一定看到了一個函數(shù)drawCenterText(canvas, textPaint, text)沒錯,這個函數(shù)就是講文字繪于控件正中心的方法。有的讀者可能一直在使用Canvas.drawText( String text, float x, float y, Paint paint) 這個方法,但是參數(shù)中的(x,y)到底是哪個坐標(biāo)呢,是文字左上角的點的坐標(biāo)嗎?不是的,接下來我們用代碼驗證一下這個(x,y)到底在文字的哪個部位

  1. canvas.drawText(text,600,200,textPaint); 
  2.         canvas.drawCircle(600,200,3,paint); 
  3.         canvas.translate(600, 200); 
  4.         Rect bgRect=new Rect(0,0,1000,400); 
  5.         canvas.drawRect(bgRect,bgPaint); 
  6.  
  7.         Rect textBound=new Rect(); 
  8. textPaint.getTextBounds(text,0,text.length(),textBound); 
  9.         paint.setColor(Color.RED); 
  10.         canvas.drawRect(textBound,paint); 
  11.  
  12.         Paint.FontMetrics metrics=textPaint.getFontMetrics(); 
  13.         paint.setColor(Color.RED); 
  14.         // ascent 橙色 
  15.         paint.setColor(Color.rgb(255,126,0)); 
  16.         canvas.drawLine(0, metrics.ascent, 500,metrics.ascent, paint); 
  17.         // descent 
  18.         paint.setColor(Color.rgb(255,0,234)); 
  19.         canvas.drawLine(0, metrics.descent, 500, metrics.descent, paint); 
  20.         // top 
  21.         paint.setColor(Color.DKGRAY); 
  22.         canvas.drawLine(0, metrics.top, 500, metrics.top, paint); 
  23.         // bottom 
  24.         paint.setColor(Color.GREEN); 
  25.         canvas.drawLine(0, metrics.bottom, 500, metrics.bottom, paint); 

首先是在畫布的(600,200)處畫上文字,為了方便觀察(600,200)在文字的什么部位,我在(600,200)處畫了一個半徑3像素的圓圈。然后平移畫布到(600,200)的地方然后依次畫出了文字的邊框圖以及FontMetrics信息里的top、ascent、descent、bottom信息

我把運行結(jié)果截圖做了處理,方便大家看

文字的各個邊界

從結(jié)果看(600,200)那個藍色的點并不是在文字的左上角,而是左下角,這個點所在的y坐標(biāo)即是大家常說的BaseLine的位置,那現(xiàn)在這個函數(shù)Canvas.drawText( String text, float x, float y, Paint paint)就可以理解為——將文字的基準(zhǔn)點放在(x,y)處,那么這個基準(zhǔn)點可以改變嗎?答案是肯定的,可以通過繪制文字的畫筆的setTextAlign(Align align)方法設(shè)置為Paint.Align.CENTER或者Paint.Align.RIGHT,如果不設(shè)置的話默認是Paint.Align.LEFT。讀者朋友們有興趣的話可以試試設(shè)置成CENTER之后(600,200)的藍圈圈是不是跑到了文字的中部呢?從上圖我們也可以看出,整個文字是介于FontMetrics.top和FontMetrics.bottom之間。

好了,貼上文字居中的代碼,相信認真看上邊那段話的朋友一定能輕松讀懂

  1. private void drawCenterText(Canvas canvas, Paint textPaint, String text) { 
  2.         Rect rect = new Rect(0, 0, mWidth, mHeight); 
  3.         textPaint.setTextAlign(Paint.Align.CENTER); 
  4.  
  5.         Paint.FontMetrics fontMetrics = textPaint.getFontMetrics(); 
  6.         //文字框最高點距離baseline的距離(負數(shù)) 
  7.         float top = fontMetrics.top
  8.         //文字框最低點距離baseline的距離(正數(shù)) 
  9.         float bottom = fontMetrics.bottom; 
  10.  
  11.         int centerY = (int) (rect.centerY() - top / 2 - bottom / 2); 
  12.  
  13.         canvas.drawText(text, rect.centerX(), centerY, textPaint); 
  14.     } 

分析好上邊的代碼 我們就能繪制出一個靜態(tài)的小球了,動畫既然要動,肯定就像汽車一樣需要一個"引擎",在上面說到的繪制波浪路徑的函數(shù)中我們忽略了getActionPath(float percent)的參數(shù)percent,這個參數(shù)即是當(dāng)前動畫的進度,那么我們?nèi)绾蝸碇圃爝@個進度呢?需要怎樣把這個動畫“引擎”點燃呢。我們可以通過各種手段計時,生成一個計時Thread或者自己寫一個Handler等等,只要能均勻的生成進度即可。

本文中用到一個巧妙的定時器ValueAnimator 大家常說的屬性動畫ObjectAnimator就是它的一個子類,使用它來作為動畫的引擎再方便不過了,從字面翻譯"ValueAnimator"那就是“值動畫者”直譯雖然low但是恰恰更好理解,就是讓數(shù)值動起來,從什么值動到什么值呢?

  1. ValueAnimator animator = ValueAnimator.ofFloat(0, 1); 

這句話就是定義一個值從0變化到1的一個animator,我們的percent值就是從0變化到1的中間過程值,那么怎么得到這個過程值呢?——監(jiān)聽器!對!

  1. animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 
  2.             @Override 
  3.             public void onAnimationUpdate(ValueAnimator animation) { 
  4.                 float percent = animation.getAnimatedFraction(); 
  5.             } 
  6.         }); 

那么數(shù)值從0變到1需要多久呢?怎么能無限重復(fù)呢?重復(fù)的時候是重頭開始還是反轉(zhuǎn)進行呢?別急下面三句話就是讓動畫無限重復(fù),每次從頭開始,一個周期1000毫秒

  1. animator.setDuration(1000); 
  2.         animator.setRepeatCount(ValueAnimator.INFINITE); 
  3.         animator.setRepeatMode(ValueAnimator.RESTART); 

好了,引擎設(shè)置好了,發(fā)動

animator.start();

上效果

鬼畜版

WTF!這是什么鬼,為什么鬼畜地慢幾拍?

打印出來橫坐標(biāo)看看

  1. 07-09 18:18:47.308  E/Jcs: getActionPath: -21 
  2. 07-09 18:18:47.326  E/Jcs: getActionPath: -15 
  3. 07-09 18:18:47.342  E/Jcs: getActionPath: -10 
  4. 07-09 18:18:47.359  E/Jcs: getActionPath: -5 
  5. 07-09 18:18:47.375  E/Jcs: getActionPath: -2 
  6. 07-09 18:18:47.392  E/Jcs: getActionPath: 0 
  7. 07-09 18:18:47.409  E/Jcs: getActionPath: 0 

最后幾拍的數(shù)值差好像不太對呀!拍拍腦門突然一想,我的動畫不均勻是忘記設(shè)置一個均勻的插值器了!哎!

  1. animator.setInterpolator(new LinearInterpolator()); 

補上一個線性插值器,整個世界都順暢了

感謝女朋的默默支持

百度Loading小球Github源碼

三、結(jié)語

第一次寫文章,不免有些疏漏之處,望多多指教!后續(xù)我會不定期更新新的內(nèi)容,爭取把寫文章當(dāng)成自己生活的一部分。

后記(2017年7月27日15:02:39)

有不少讀者問到關(guān)于小球和邊緣鋸齒的問題,我分別用如下方式實現(xiàn)loading小球

1、Canvas的clip方式限制波浪邊界(本文提到的方法)

2、使用Xfermode方式限制波浪和圓形的邊界

3、用Xfermode方式限制白色文字,用shader方式限制圓形的邊界

下邊是效果預(yù)覽圖,代碼已經(jīng)提交到github上了,講解部分盡快補到此文中

三種方式對比

【本文為51CTO專欄作者“季晨生”的原創(chuàng)稿件,轉(zhuǎn)載請通過51CTO聯(lián)系作者獲取授權(quán)】

戳這里,看該作者更多好文

責(zé)任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2011-10-28 16:19:21

百度搜索

2011-11-28 10:02:12

百度知道微問答移動

2015-01-09 11:49:26

Android源碼下載

2011-12-05 09:56:59

2013-05-16 11:12:29

百度百度MTC錄制回放客戶端

2021-12-16 10:08:47

鴻蒙HarmonyOS應(yīng)用

2015-10-28 13:40:28

高仿百度糯米源碼

2020-05-28 13:10:06

僵尸網(wǎng)絡(luò)黑客網(wǎng)絡(luò)攻擊

2009-06-29 15:28:17

百度俞軍

2009-04-30 10:03:33

百度俞軍離職

2009-03-27 09:51:21

百度貼吧改版

2009-04-02 09:26:00

百度貼吧俞軍

2010-08-05 14:12:01

百度日本

2012-07-16 14:43:32

silverlight

2009-08-11 09:20:47

百度舒迅

2017-02-05 10:43:39

Python編寫多線程

2015-03-30 14:24:06

網(wǎng)易布局

2009-04-07 09:23:28

李彥宏百度貼吧

2015-09-01 16:42:55

新聞客戶端百度數(shù)據(jù)源碼

2015-12-09 11:22:24

高仿今日頭條android源碼
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 一区二区av| 欧美在线视频网 | 人人鲁人人莫人人爱精品 | 亚洲福利 | 国产精品免费在线 | 99久久久无码国产精品 | 在线观看免费观看在线91 | 国产一区二区三区久久久久久久久 | 欧美日高清 | 国产精品亚洲一区 | 中文字幕 欧美 日韩 | 国产午夜视频 | 人成久久| 在线播放一区 | 一级毛片观看 | 亚洲福利 | 综合五月婷 | 伊人精品国产 | 日韩欧美亚洲 | 久草视频网站 | 在线播放国产视频 | 精品国产欧美一区二区 | 五月婷六月丁香 | 日韩欧美一区在线 | 国产成人精品一区二区在线 | 亚洲综合色网站 | 亚洲成人一区 | 国产一区h| 国产精品无码专区在线观看 | 亚洲欧美bt | 91毛片网 | 一区二区在线免费观看视频 | 欧美老少妇一级特黄一片 | 国产激情视频网站 | 男女羞羞视频在线 | 国产综合在线视频 | 日韩1区2区 | 久久久久国产精品一区三寸 | 精品国产乱码久久久 | 美女啪啪国产 | 欧美日韩视频 |