鴻蒙開源第三方組件的遷移-加載動畫庫
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
https://harmonyos.51cto.com/#zz
前言
基于安卓平臺的加載動畫庫AVLoadingIndicatorView(https://github.com/81813780/AVLoadingIndicatorView),實(shí)現(xiàn)了鴻蒙化遷移和重構(gòu),代碼已經(jīng)開源到(https://gitee.com/iscas-ohos/avloading-indicator-view_ohos.git),歡迎各位下載使用并提出寶貴意見!
背景
服務(wù)器在加載數(shù)據(jù)的時(shí)候有時(shí)需要等待一段時(shí)間,加載動畫可以緩解用戶等待過程中的焦慮情緒,使等待過程變得更加友好、流暢。AVLoadingIndicatorView是一個開源的加載動畫庫,通過動畫庫的調(diào)用,可以實(shí)現(xiàn)各種各樣的加載效果。
組件功能展示
1. 動畫效果
在原Android版本中,本組件庫里共有28個加載動畫,鴻蒙版本的組件庫成功實(shí)現(xiàn)其中21種動畫效果。由于鴻蒙系統(tǒng)部分API功能缺失,目前有7個動畫效果未成功遷移。

圖1 AVLoadingIndicatorView效果示意圖
圖1所示為鴻蒙平臺的AVLoadingIndicatorView的動畫效果展示,可分為6行4列,動畫效果的對應(yīng)名稱(從左至右)如下:
Row 1 BallPulseIndicator,BallGridPulseIndicator,BallClipRotateIndicator,BallClipRotatePulseIndicator
Row 2 PacmanIndicator,BallClipRotateMultipleIndicator, SemiCircleSpinIndicator,BallRotateIndicator
Row 3 BallScaleIndicator,LineScaleIndicator,LineScalePartyIndicator,BallScaleMultipleIndicator
Row 4 BallPulseSyncIndicator,BallBeatIndicator,LineScalePulseOutIndicator,LineScalePulseOutRapidIndicator
Row 5 BallScaleRippleIndicator,BallScaleRippleMultipleIndicator,BallSpinFadeLoaderIndicator,LineSpinFadeLoaderIndicator
Row 6 BallGridBeatIndicator
2. 動畫控制效果
AVLoadingIndicatorView組件支持對各加載動畫的效果進(jìn)行控制,控制類型分為4種:動畫啟動、動畫停止、動畫顯示和動畫隱藏。動畫啟動和動畫停止相互關(guān)聯(lián),二者用于控制動畫是否運(yùn)行;動畫顯示和動畫隱藏相互關(guān)聯(lián),二者用于控制動畫是否出現(xiàn),控制效果如圖2所示。

圖2 四種效果控制示意圖
Sample解析
本組件庫中的每個動畫都有啟動、停止、隱藏和顯示四種控制效果。為了方便調(diào)試和演示,Sample中將所有動畫的對象放入同一個list,通過四個不同的按鈕,控制所有動畫同時(shí)啟動、停止、隱藏和顯示,效果如圖2所示。
下面介紹控制所有動畫同時(shí)啟動、停止、隱藏和顯示的步驟:
1、導(dǎo)入AVLoadingIndicatorView類
- import com.wang.avi.AVLoadingIndicatorView;
2、動畫添加到Layout
此處需要將所有動畫添加到Layout中,并將各動畫的對象放入同一list(即代碼中的animatorList)。
- //以BallPulseIndicator為例
- myLayout.addComponent(ballPulseIndicator);//BallPulseIndicator添加到Layout
- animatorList.add(ballPulseIndicator);//BallPulseIndicator對象放入list
- //以BallGridPulseIndicator為例
- myLayout.addComponent(ballGridPulseIndicator);
- animatorList.add(ballGridPulseIndicator);
3、設(shè)置四個按鈕,用以控制所有動畫同時(shí)啟動、停止、隱藏和顯示。
以"啟動"效果為例,startBtn按鈕設(shè)置了Click監(jiān)聽,按下按鈕時(shí),會執(zhí)行startAllAnimator ()方法,startAllAnimator ()方法中借助for循環(huán),遍歷animatorList中的每一個動畫的對象,并調(diào)用每個對象的start()方法,達(dá)到動畫啟動的效果。
- //按鈕監(jiān)聽
- startBtn.setClickedListener(component-> startAllAnimator(animatorList));//啟動
- endBtn.setClickedListener(component-> stopAllAnimator(animatorList));//停止
- hideBtn.setClickedListener(component-> hideAllAnimator(animatorList));//隱藏
- showBtn.setClickedListener(component-> showAllAnimator(animatorList));//顯示
- //start
- private void startAllAnimator(ArrayList<AVLoadingIndicatorView> avLoadingIndicatorViews){
- for (int i = 0; i < avLoadingIndicatorViews.size(); i++) {
- avLoadingIndicatorViews.get(i).start();//啟動
- }
- }
- //stop
- private void stopAllAnimator(ArrayList<AVLoadingIndicatorView> avLoadingIndicatorViews){
- for (int i = 0; i < avLoadingIndicatorViews.size(); i++) {
- avLoadingIndicatorViews.get(i).stop();//停止
- }
- }
- //hide
- private void hideAllAnimator(ArrayList<AVLoadingIndicatorView> avLoadingIndicatorViews){
- for (int i = 0; i < avLoadingIndicatorViews.size(); i++) {
- avLoadingIndicatorViews.get(i).hide();//隱藏
- }
- }
- //show
- private void showAllAnimator(ArrayList<AVLoadingIndicatorView> avLoadingIndicatorViews){
- for (int i = 0; i < avLoadingIndicatorViews.size(); i++) {
- avLoadingIndicatorViews.get(i).show();//顯示
- }
- }
Library解析
1. 功能實(shí)現(xiàn)
每個動畫效果的繪制都需要執(zhí)行初始化設(shè)置、添加繪畫任務(wù)、創(chuàng)建動畫三個步驟,下面以BallPulseIndicator為例對這三個步驟進(jìn)行詳細(xì)介紹。
(1) 初始化設(shè)置 聲明setPaint(),setBound(),draw()方法。
- public BallPulseIndicator(Context context) {
- super(context);
- Component.DrawTask task = (component, canvas) -> {
- setPaint();//設(shè)置畫筆
- setBound();//設(shè)置動畫邊界
- draw(canvas,getPaint());//內(nèi)容繪制
- };
- addDrawTask(task);
- }
(2) 動畫繪制 用draw()方法在畫布上繪制動畫樣式。
由于BallPulseIndicator動畫主體是三個圓,第二、第三個圓均是由前一個圓平移得到,因此要設(shè)置每兩個圓之間的距離,圓的半徑大小等屬性。
- public void draw(Canvas canvas, Paint paint) {
- float circleSpacing=4; //設(shè)置圓之間距離
- float radius=(Math.min(getWidth(),getHeight())-circleSpacing*2)/6; //設(shè)置圓的半徑大小
- float x = getWidth()/ 2-(radius*2+circleSpacing);//圓心的x軸坐標(biāo)
- float y=getHeight() / 2;//圓心的y軸坐標(biāo)
- for (int i = 0; i < 3; i++) {
- canvas.save();
- float translateX=x+(radius*2)*i+circleSpacing*i;//平移后新圓心的x軸坐標(biāo)
- canvas.translate(translateX, y);
- canvas.scale(scaleFloats[i], scaleFloats[i]);//縮放效果繪制
- canvas.drawCircle(0, 0, radius, paint);//繪制圓
- canvas.restore();
- }
- }
(3) 動畫參數(shù)設(shè)置
通過AnimatorValue對動畫參數(shù)進(jìn)行具體設(shè)置,包括動畫的持續(xù)時(shí)間、重復(fù)次數(shù)、啟動延遲時(shí)間、縮放和旋轉(zhuǎn)等(BallPulseIndicator只涉及縮放而無旋轉(zhuǎn))。
- public ArrayList<AnimatorValue> onCreateAnimators() {
- ArrayList<AnimatorValue> animators=new ArrayList<>();
- int[] delays=new int[]{120,240,360}; //設(shè)置三個圓的延遲時(shí)間
- for (int i = 0; i < 3; i++) {
- final int index=i;
- AnimatorValue scaleAnim=new AnimatorValue(); //值動畫
- scaleAnim.setDuration(750); //動畫持續(xù)時(shí)間
- scaleAnim.setLoopedCount(-1); //動畫無限次重復(fù)
- scaleAnim.setDelay(delays[i]); //每個圓的延遲時(shí)間
- addUpdateListener(scaleAnim, (animatorValue, v) -> {
- scaleFloats[index] = v;//控制縮放
- invalidate();//刷新界面
- });
- animators.add(scaleAnim);
- }
- return animators;
- }
2. 移植方法
(1) API直接替換
在安卓中使用ValueAnimator類設(shè)置加載動畫的屬性,移植之后這些功能主要基于鴻蒙的AnimatorValue類實(shí)現(xiàn)。這兩個類中的方法名也不同,在進(jìn)行鴻蒙化遷移時(shí)需要根據(jù)功能逐一替換。例如:鴻蒙中使用setLoopedCount()方法替換原setRepeatCount()方法來實(shí)現(xiàn)動畫重復(fù)次數(shù)的設(shè)置。
(2) 函數(shù)重寫
鴻蒙的AnimatorValue類相比較于安卓,缺少很多接口,若在實(shí)現(xiàn)部分復(fù)雜動畫時(shí),需要調(diào)用這些接口,只能采用函數(shù)重寫的方法,這也是移植中的主要難點(diǎn)。如安卓中用ValueAnimator.ofFloat(1,0.5f,1)來設(shè)置動畫的屬性值1—0.5f—1的兩次變化,實(shí)現(xiàn)動畫的運(yùn)行效果,而鴻蒙中缺少該接口,屬性值只能在0—1之間單次變化,無法實(shí)現(xiàn)動畫的完美效果,需要進(jìn)行功能重寫,下面給出此功能重寫的代碼 。
- public void onUpdate(AnimatorValue animatorValue, float v) {
- if(v<=0.5f)
- scaleFloats[index] =1-v;
- else
- scaleFloats[index] = v;
- invalidate();
- }
©著作權(quán)歸作者和HarmonyOS技術(shù)社區(qū)共同所有,如需轉(zhuǎn)載,請注明出處,否則將追究法律責(zé)任。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
https://harmonyos.51cto.com/#zz