<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>

          高仿馬蜂窩旅游頭像泡泡動(dòng)畫(huà)

          共 7461字,需瀏覽 15分鐘

           ·

          2020-06-21 23:49

          72dd8ac3cfb320f94bde6949095f276e.webp

          本篇文章轉(zhuǎn)自 明朗__ 的博客,分享了一個(gè)炫酷的動(dòng)畫(huà)效果,希望對(duì)大家有所幫助!

          原文地址:https://www.jianshu.com/p/dd29f1ae5239

          前言

          當(dāng) pm 制定完下一版本需求打開(kāi)馬蜂窩旅游 app 準(zhǔn)備出去嗨一圈的時(shí)候看到了馬蜂窩旅游 app 的一個(gè)用戶(hù)頭像動(dòng)畫(huà)后。。。(=@__@=) 先看看效果圖

          45d4ff83390212fa422dade4722ae16d.webp

          效果分析:

          39d9f2b13457fb837e92ccdf44ff8c84.webp
          1. 涉及到有多個(gè) view 在做動(dòng)畫(huà)操作 這里需要繼承 FrameLayout 來(lái)左父布局 供圖片做動(dòng)畫(huà)操作
          2. 每個(gè)子 view 的動(dòng)畫(huà)路徑類(lèi)似于 S 型 我這里采用的是三階貝塞爾曲線和 PathMeasure 來(lái)完成動(dòng)畫(huà)運(yùn)動(dòng)路徑的封裝
          3. 每個(gè)子 view 動(dòng)畫(huà)執(zhí)行完后 是移除添加新的 view 進(jìn)來(lái) 還是回收重新利用 本案例是直接移除再添加新的(回收重新利用還沒(méi)來(lái)得及去考慮該怎么寫(xiě))
          4. 動(dòng)畫(huà)是循環(huán)不停的播放 我采用的是 RxJava timer()操作符 不斷的發(fā)送隨機(jī)延遲消息去通知?jiǎng)赢?huà)的執(zhí)行
          5. 最后就剩下一些停止動(dòng)畫(huà)操作的開(kāi)關(guān)設(shè)定

          實(shí)現(xiàn)步驟

          1. 一些基本的初始化工作

          public?class?HeadBubbleView?extends?FrameLayout?{
          ????//這個(gè)position很重要?不斷的取出圖片資源?靠它累加完成的
          ????private?int?position?=?0;

          ????public?HeadBubbleView(@NonNull?Context?context)?{
          ????????this(context,null);
          ????}

          ????public?HeadBubbleView(Context?context,?AttributeSet?attrs)?{
          ????????super(context,?attrs);
          ????????mContext?=?context;
          ????????setFocusable(false);
          ????????//三階貝塞爾曲線控制點(diǎn)一
          ????????controlPointOne?=?new?Point();
          ????????//三階貝塞爾曲線控制點(diǎn)二
          ????????controlPointTwo?=?new?Point();
          ????????//每個(gè)子view的寬高是固定的
          ????????viewWidth?=?viewHeight?=?SizeUtils.dp2px(context,?22);
          ????????marginLeft?=?SizeUtils.dp2px(context,?15);
          ????????marginBot?=?SizeUtils.dp2px(context,?21);
          ????????//父View的高度也是固定的
          ????????height?=?SizeUtils.dp2px(context,?130);
          ????????//用于從PathMeasure?中不斷的取出?曲線的路徑值
          ????????pos?=?new?float[2];
          ????????tan?=?new?float[2];
          ????????initView();
          ????}

          2. 初始化的時(shí)候數(shù)據(jù)的加載狀態(tài)

          private?void?initView()?{
          ????????//這個(gè)ImageView將不執(zhí)行動(dòng)畫(huà)?用于底部不斷切換圖片展示
          ????????tempImageView?=?getImageView();
          ????????textView?=?getTextView();
          ????????initData(tempImageView);
          ????}
          //創(chuàng)建執(zhí)行動(dòng)畫(huà)的具體角色
          private?ImageView?getImageView()?{
          ????????LayoutParams?layoutParams?=?new?LayoutParams(viewWidth,?viewHeight);
          ????????ImageView?roundedImageView?=?new?ImageView(getContext());
          ????????roundedImageView.setScaleType(ImageView.ScaleType.FIT_XY);
          ????????layoutParams.gravity?=?Gravity.BOTTOM?|?Gravity.END;
          ????????layoutParams.setMargins(0,?0,?marginLeft,?marginBot);
          ????????addView(roundedImageView,?layoutParams);
          ????????return?roundedImageView;
          ????}
          //創(chuàng)建用于顯示坐標(biāo)xx來(lái)過(guò)的TextView
          private?TextView?getTextView()?{
          ????????int?bottom?=?SizeUtils.dp2px(mContext,?23);
          ????????int?right?=?SizeUtils.dp2px(mContext,?41);
          ????????LayoutParams?layoutParams?=?new?LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,?ViewGroup.LayoutParams.WRAP_CONTENT);
          ????????layoutParams.gravity?=?Gravity.END?|?Gravity.BOTTOM;
          ????????layoutParams.setMargins(0,?0,?right,?bottom);

          ????????TextView?tv_name?=?new?TextView(mContext);
          ????????tv_name.setTextSize(12);
          ????????tv_name.setTextColor(Color.WHITE);
          ????????addView(tv_name,?layoutParams);
          ????????return?tv_name;
          ????}
          //第一次加載數(shù)據(jù)
          private?void?initData(ImageView?roundedImageView)?{
          ????????if?(null?!=?browseEntities?&&?browseEntities.size()?>?0)?{
          ????????????//第一次去第0個(gè)數(shù)據(jù)
          ????????????BrowseEntity?browseEntity?=?browseEntities.get(position);
          ????????????if?(null?!=?browseEntity)?{
          ????????????????roundedImageView.setBackgroundResource(browseEntity.drawableId);
          ????????????????String?username?=?browseEntity.name;
          ????????????????if?(!TextUtils.isEmpty(username))?{
          ????????????????????textView.setText(username?+?"來(lái)過(guò)");
          ????????????????}
          ????????????}
          ????????}
          ????}

          由上面的操作就完成基礎(chǔ)顯示

          86da2586efb1a03b9737123d0587abae.webp

          3. 接下來(lái)完成第一階段動(dòng)畫(huà) 由最小縮放到最大

          private?boolean?createAnimView()?{
          ????????if?(!isStop)?{
          ????????????return?true;
          ????????}
          ????????ImageView?imageView?=?getImageView();
          ????????//創(chuàng)建好后?設(shè)置縮放到最小
          ????????imageView.setScaleX(0);
          ????????imageView.setScaleY(0);
          ????????initData(imageView);
          ????????startScaleAnim(imageView);
          ????????return?false;
          ????}
          //執(zhí)行縮放動(dòng)畫(huà)
          private?void?startScaleAnim(final?ImageView?imageView)?{
          ????????ValueAnimator?valueAnimator?=?ValueAnimator.ofFloat(0.0f,?1.0f);
          ????????valueAnimator.setDuration(800);
          ????????valueAnimator.addUpdateListener(new?ValueAnimator.AnimatorUpdateListener()?{
          ????????????@Override
          ????????????public?void?onAnimationUpdate(ValueAnimator?animation)?{
          ????????????????float?animatedValue?=?(float)?animation.getAnimatedValue();
          ????????????????imageView.setScaleX(0.1f?+?animatedValue);
          ????????????????imageView.setScaleY(0.1f?+?animatedValue);
          ????????????}
          ????????});
          ????????valueAnimator.addListener(new?AnimatorListenerAdapter()?{
          ????????????@Override
          ????????????public?void?onAnimationEnd(Animator?animation)?{
          ????????????????if?(position?==?browseEntities.size()?-?1)?{
          ????????????????????position?=?0;
          ????????????????}?else?{
          ????????????????????position++;
          ????????????????}
          ??????????BrowseEntity?browseEntity?=?browseEntities.get(position);
          ????????//動(dòng)畫(huà)執(zhí)行完后要立馬取出下一個(gè)圖片?把底部的圖片顯示更新
          ????????tempImageView.setBackgroundResource(browseEntity.drawableId);
          ????????//動(dòng)畫(huà)執(zhí)行完執(zhí)行平移動(dòng)畫(huà)
          ????????startTranslationAnimator(imageView);
          ????????????}
          ????????});
          ????????valueAnimator.start();
          ????}
          8f4edd93da87726363482ffcfd872b35.webp

          4. 第二階段的曲線運(yùn)動(dòng)縮小動(dòng)畫(huà)

          private?void?startTranslationAnimator(final?ImageView?imageView)?{
          ????????Path?path;
          ????????int?seed?=?(int)?(Math.random()?*?100);
          ????????//根據(jù)隨機(jī)數(shù)來(lái)確定是走左邊曲線還是右邊曲線
          ????????if?(seed?%?2?==?0)?{
          ????????????//曲線路徑的封裝
          ????????????path?=?createRightPath();
          ????????}?else?{
          ????????????//曲線路徑的封裝
          ????????????path?=?createLeftPath();
          ????????}
          ????????//通過(guò)PathMeasure?和ValueAnimator結(jié)合?在不同的階段取出運(yùn)動(dòng)路徑的x,y值
          ????????final?PathMeasure?pathMeasure?=?new?PathMeasure(path,?false);
          ????????final?ValueAnimator?valueAnimator?=?ValueAnimator.ofFloat(1.0f,?0.0f);
          ????????valueAnimator.setDuration(riseDuration);
          ????????valueAnimator.setInterpolator(new?LinearInterpolator());
          ????????valueAnimator.addUpdateListener(new?ValueAnimator.AnimatorUpdateListener()?{
          ????????????@Override
          ????????????public?void?onAnimationUpdate(ValueAnimator?animation)?{
          ????????????????float?animatedValue?=?(float)?animation.getAnimatedValue();
          ????????????????int?length?=?(int)?(pathMeasure.getLength()?*?animatedValue);
          ???????????????//在不同的階段取出運(yùn)動(dòng)路徑的x,y值
          ????????????????pathMeasure.getPosTan(length,?pos,?tan);
          ????????????????imageView.setTranslationX(pos[0]);
          ????????????????imageView.setTranslationY(pos[1]);
          ????????????????//同時(shí)做透明度動(dòng)畫(huà)
          ????????????????imageView.setAlpha(animatedValue);
          ????????????????if?(animatedValue?>=?0.5f)?{
          ????????????????????imageView.setScaleX(0.2f?+?animatedValue);
          ????????????????????imageView.setScaleY(0.2f?+?animatedValue);
          ????????????????}
          ????????????}
          ????????});
          ????????valueAnimator.addListener(new?AnimatorListenerAdapter()?{
          ????????????@Override
          ????????????public?void?onAnimationEnd(Animator?animation)?{
          ????????????????//動(dòng)畫(huà)執(zhí)行完就移除View
          ????????????????removeView(imageView);
          ????????????}
          ????????});
          ????????valueAnimator.start();
          ????}

          5. 三階賽貝爾曲線的計(jì)算

          下面以左邊的為例

          這里我也沒(méi)有更好的辦法去計(jì)算 是通過(guò)不斷預(yù)估嘗試出來(lái)的 如果有大佬在這里有很好的計(jì)算方法 請(qǐng)務(wù)必告知下

          6d6c6d0738336de1515fc1f85ad53f10.webp
          private?Path?createLeftPath()?{
          ????????Path?path?=?new?Path();
          ????????float?nextFloat?=?new?Random().nextFloat();
          ????????path.moveTo(nextFloat,?-height?*?1.0f?/?1.8f);
          ????????//曲線控制點(diǎn)一
          ????????controlPointOne.x?=?-(viewWidth);
          ????????controlPointOne.y?=?-height?/?5;
          ????????//曲線控制點(diǎn)二
          ????????controlPointTwo.x?=?-(viewWidth?+?marginLeft?/?2);
          ????????controlPointTwo.y?=?(int)?(-height?*?0.15);
          ????????//生成三階貝塞爾曲線
          ????????path.cubicTo(controlPointOne.x,?controlPointOne.y,?controlPointTwo.x,?controlPointTwo.y,?0,?0);
          ????????return?path;
          ????}

          最后連貫起來(lái)看看效

          f4adc6dda9bb9a096d582f4609f4651b.webp

          6. 最后使用 RxJava 的 timer()操作符 發(fā)延遲消息來(lái)讓整個(gè)動(dòng)畫(huà)循環(huán)執(zhí)行起來(lái)

          這里也可以用 handler 來(lái)發(fā)消息處理

          public?void?startAnimation(int?innerDelay)?{
          ????????subscribe?=?Observable.timer(innerDelay,?TimeUnit.MILLISECONDS)
          ????????????????.observeOn(AndroidSchedulers.mainThread())
          ????????????????.subscribe(new?Consumer()?{
          ????????????????????@Override
          ????????????????????public?void?accept(Long?aLong)?throws?Exception?{
          ????????????????????????if?(createAnimView())?return;

          ????????????????????????int?duration?=?(int)?(1500?*?Math.random());
          ????????????????????????if?(duration?500)?{
          ????????????????????????????duration?=?500;
          ????????????????????????}
          ????????????????????????//循環(huán)調(diào)用
          ????????????????????????startAnimation(500?+?duration);
          ????????????????????}
          ????????????????});
          ????}

          //動(dòng)畫(huà)執(zhí)行的一些開(kāi)關(guān)操作
          public?void?stopAnimator()?{
          ????????isStop?=?false;
          ????????if?(null?!=?subscribe)?{
          ????????????subscribe.dispose();
          ????????}
          ????}
          bbc74012b71e8873ce4fcbc350b77e70.webp

          到這里整個(gè)動(dòng)畫(huà)流程到這里就結(jié)束了,當(dāng)然在內(nèi)存的管理上還沒(méi)有做到極致,大家可以去自由發(fā)揮,希望這篇水文能幫助到那些有類(lèi)似需求的同學(xué),我們應(yīng)該把時(shí)間拿去做一些更有用的事情,不過(guò)截止到目前,馬蜂窩最新版 已經(jīng)沒(méi)有該頭像的泡泡動(dòng)畫(huà),想必他們也改了吧!



          -?End -



          猜你喜歡Android 通訊錄索引效果三個(gè)值得學(xué)習(xí)的自定義 View 開(kāi)源項(xiàng)目使用 AirPods Pro 遇到的問(wèn)題

          瀏覽 49
          點(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>
                  黄色直接观看 | 国产人伦子伦一级A片下载 | 翔田千里一区二区 | 日逼逼av| 久久人人妻人人做人人玩精品 |