欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android 使用cos和sin繪制復(fù)合曲線動(dòng)畫

 更新時(shí)間:2021年03月17日 11:02:21   作者:九心  
這篇文章主要介紹了Android 使用cos和sin繪制復(fù)合曲線動(dòng)畫的方法,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下

前言

前兩周在開發(fā)新需求的時(shí)候,設(shè)計(jì)給了一份類似這樣的動(dòng)畫:

看著不難,即使一遍看不懂,嘿嘿,不還有設(shè)計(jì)稿。

作為一個(gè)平時(shí)很少寫動(dòng)畫的 Android 開發(fā)仔,看到一段段的緩入緩出曲線的設(shè)計(jì)稿時(shí),我的心情是這樣的:

雖然,Android 動(dòng)畫默認(rèn)的插值器 AccelerateDecelerateInterpolator 有這樣緩入緩出的效果:

我總不能一整個(gè)動(dòng)畫給它拆成4段動(dòng)畫來寫,還別說,我第一次寫的代碼還真的是這么干的。

第一次分析

本著能少寫一行絕不多寫一字的原則,詢問了大佬同事的意見,大佬大手一揮:PathInterpolator(后證實(shí)有問題)。

簡單看了一下使用方式,需要使用 Path,再看了一眼,好家伙,有可能會(huì)用到貝塞爾曲線,放棄~

為了能夠快速的解決問題,就使用了上面談到的方案:

private fun animateTagView(tagView: TextView) {
 // [0,200]區(qū)間的動(dòng)畫 
 val valueAnimatorOne = ValueAnimator.ofInt(0, 200)
 valueAnimatorOne.addUpdateListener {
  val per = it.animatedValue as Int / 200f
  tagView.rotation = 4 * per
  tagView.scaleX = (1 - 0.1 * per).toFloat()
  tagView.scaleY = (1 - 0.1 * per).toFloat()
 }
 valueAnimatorOne.duration = 200
 // [200,560]區(qū)間的動(dòng)畫
 val valueAnimatorTwo = ValueAnimator.ofInt(200, 560)
 valueAnimatorTwo.addUpdateListener {
  val per = (it.animatedValue as Int - 200) / 360f
  tagView.rotation = 3 - 11 * per
  tagView.scaleX = (0.9 + 0.1 * per).toFloat()
  tagView.scaleY = (0.9 + 0.1 * per).toFloat()
 }
 valueAnimatorTwo.duration = 360
 // [560,840]區(qū)間的動(dòng)畫
 val valueAnimatorThree = ValueAnimator.ofInt(560, 840)
 valueAnimatorThree.addUpdateListener {
  val per = (it.animatedValue as Int - 560) / 280f
  tagView.rotation = -8 + 12 * per
  tagView.scaleX = (1 - 0.2 * per).toFloat()
  tagView.scaleY = (1 - 0.2 * per).toFloat()
 }
 valueAnimatorThree.duration = 280
 // [840,1000]的動(dòng)畫
 val valueAnimatorFour = ValueAnimator.ofInt(840, 1000)
 valueAnimatorFour.addUpdateListener {
  val per = (it.animatedValue as Int - 840) / 160f
  tagView.rotation = 4 - 4 * per
  tagView.scaleX = (0.8 + 0.2 * per).toFloat()
  tagView.scaleY = (0.8 + 0.2 * per).toFloat()
 }
 valueAnimatorFour.duration = 160
 // 使用AnimatorSet串行執(zhí)行動(dòng)畫
 val animationSet = AnimatorSet()
 animationSet.playSequentially(valueAnimatorOne, valueAnimatorTwo, valueAnimatorThree, valueAnimatorFour)
 tagView.post {
  tagView.pivotX = 0f
  tagView.pivotY = ad_tag_two.measuredHeight.toFloat()
  animationSet.start()
 }
}

整個(gè)動(dòng)畫被我拆成了[0,200]、[200,560]、[560,840]和[840,1000]四段屬性動(dòng)畫,因?yàn)楫a(chǎn)品說只需要播放一次,所以使用 AnimatorSet 將動(dòng)畫組裝起來,就可以解決問題。

第二次分析

第一次得到的方案雖然能夠解決問題,如果遇到循環(huán)播放,AnimatorSet 就不行了,有沒有其他方案呢?

趁著周末的時(shí)間,學(xué)了一下 PathInterpolator,發(fā)現(xiàn)這個(gè)玩意也解決不了問題,或者說不好解決問題,雖然可以用三階貝塞爾曲線分段畫出上述曲線,但 PathInterpolator 要求起點(diǎn)和終點(diǎn)分別在 (0,0) 和 (1,1)。

既然插值器不行,可以試試估值器,但一個(gè)估值器也解決不了旋轉(zhuǎn)和縮放兩種動(dòng)畫,看來得靠 AnimatorUpdateListener 去解決問題。

回頭想一下,插值器是將均勻的時(shí)間片段轉(zhuǎn)化成加速或者減速的行為,我們也可以將均勻的時(shí)間片段轉(zhuǎn)化成對應(yīng)的曲線,只要做好兩點(diǎn):

使用線性的插值器 LinearInterpolator。
將上面的曲線拆分,通過不同的 sin 或者 cos 方法表達(dá)。
以旋轉(zhuǎn)動(dòng)畫為例,拆成的 sin 函數(shù):

另外一段動(dòng)畫的函數(shù)可以參考代碼:

override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_main)

 val tvContent = findViewById<TextView>(R.id.tv_content)
 val valueAnimatorOne = ValueAnimator.ofFloat(0.0f, 1.5f)
 valueAnimatorOne.addUpdateListener {
  // 通過對應(yīng)的sin和cos設(shè)置rotation和scale
  val per = it.animatedValue as Float
  var rotation: Float = 0f
  var scale: Float = 0f
  if(per >= 0 && per < 0.2f){
   rotation = sin((per / 0.2f) * Math.PI.toFloat() - Math.PI.toFloat() / 2) * 1.5f + 1.5f
   scale = cos(per / 0.2f * Math.PI.toFloat()) * 0.05f + 0.95f
  }
  if(per >= 0.2f && per < 0.56f){
   rotation = sin(Math.PI.toFloat() / 2 + Math.PI.toFloat() * ( per - 0.2f) / 0.36f) * 5.5f - 2.5f
   scale = cos((per - 0.2f) / 0.36f * Math.PI.toFloat() + Math.PI.toFloat()) * 0.05f + 0.95f
  }
  if(per >= 0.56f && per < 0.84f){
   rotation = sin(Math.PI.toFloat() * (per - 0.56f) / 0.28f - Math.PI.toFloat() / 2) * 6f - 2f
   scale = cos((per - 0.56f) / 0.28f * Math.PI.toFloat()) * 0.1f + 0.9f
  }
  if(per in 0.84f..1f){
   rotation = sin(Math.PI.toFloat() / 2 + Math.PI.toFloat() * (per - 0.84f) / 0.16f ) * 2f + 2f
   scale = cos((per - 0.84f) / 0.16f * Math.PI.toFloat() + Math.PI.toFloat()) * 0.1f + 0.9f
  }
  // 設(shè)置停止時(shí)間
  if(per > 1f && per <= 1.5f){
   rotation = 0f
   scale = 1.0f
  }
  tvContent.rotation = rotation
  tvContent.scaleX = scale
  tvContent.scaleY = scale
 }
 // 設(shè)置線性插值器
 valueAnimatorOne.interpolator = LinearInterpolator()
 // 動(dòng)畫時(shí)間
 valueAnimatorOne.duration = 1500
 // 無線循環(huán)
 valueAnimatorOne.repeatCount = -1
 tvContent.post {
  // 設(shè)置中心點(diǎn)
  tvContent.pivotX = 0f
  tvContent.pivotY = tvContent.measuredHeight.toFloat()
  valueAnimatorOne.start()
 }
}

整個(gè)代碼還是比較簡單的,旋轉(zhuǎn)動(dòng)畫曲線由 sin 得出,縮放由 cos 得出,最后改一下中心點(diǎn)。

總結(jié)

本次的動(dòng)畫案例不難,在面對復(fù)合緩入緩出曲線的情形,我們可以拆成一段段,用 sin 或者 cos 去描述,這樣的好處是可以只使用一個(gè)屬性動(dòng)畫,且可以循環(huán)播放。

如果你有更好的方案,歡迎評論區(qū)交流。

以上就是Android 使用cos和sin繪制復(fù)合曲線動(dòng)畫的詳細(xì)內(nèi)容,更多關(guān)于Android 繪制復(fù)合曲線動(dòng)畫的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳細(xì)講解AsyncTask使用說明(值得收藏)

    詳細(xì)講解AsyncTask使用說明(值得收藏)

    AsyncTask就相當(dāng)于Android給我們提供了一個(gè)多線程編程的一個(gè)框架,其介于Thread和Handler之間,我們?nèi)绻x一個(gè)AsyncTask,就需要定義一個(gè)類來繼承AsyncTask這個(gè)抽象類,并實(shí)現(xiàn)其唯一的一doInBackgroud 抽象方法,這篇文章主要介紹了AsyncTask詳解,需要的朋友可以參考下
    2024-01-01
  • Android圖片處理實(shí)例介紹(圖)

    Android圖片處理實(shí)例介紹(圖)

    本篇文章介紹了,Android中圖片處理實(shí)例介紹,需要的朋友參考下
    2013-04-04
  • Android播放多張圖片形成的一個(gè)動(dòng)畫示例

    Android播放多張圖片形成的一個(gè)動(dòng)畫示例

    這篇文章主要介紹了Android播放多張圖片形成的一個(gè)動(dòng)畫實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了Android逐幀播放動(dòng)畫圖片及ImageView控件的相關(guān)使用技巧,需要的朋友可以參考下
    2016-10-10
  • android實(shí)現(xiàn)獲取有線和無線Ip地址的方法

    android實(shí)現(xiàn)獲取有線和無線Ip地址的方法

    這篇文章主要介紹了android實(shí)現(xiàn)獲取有線和無線Ip地址的方法,較為詳細(xì)的分析了Android獲取IP地址的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-08-08
  • android實(shí)現(xiàn)拖拽裁剪功能

    android實(shí)現(xiàn)拖拽裁剪功能

    這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)拖拽裁剪功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Android自定義PopupWindow小案例

    Android自定義PopupWindow小案例

    這篇文章主要為大家詳細(xì)介紹了Android自定義PopupWindow小案例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • 詳解android 中文字體向上偏移解決方案

    詳解android 中文字體向上偏移解決方案

    這篇文章主要介紹了詳解android 中文字體向上偏移解決方案,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-01-01
  • 淺談Android截屏和指定View生成截圖

    淺談Android截屏和指定View生成截圖

    本文主要介紹了Android如何截屏和指定View生成截圖,感興趣的同學(xué),可以參考下
    2021-06-06
  • Android實(shí)現(xiàn)一個(gè)倒計(jì)時(shí)自定義控件

    Android實(shí)現(xiàn)一個(gè)倒計(jì)時(shí)自定義控件

    大家好,本篇文章主要講的是Android實(shí)現(xiàn)一個(gè)倒計(jì)時(shí)自定義控件,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-02-02
  • 解決Android 沉浸式狀態(tài)欄和華為虛擬按鍵沖突問題

    解決Android 沉浸式狀態(tài)欄和華為虛擬按鍵沖突問題

    對于現(xiàn)在的 App 來說,布局頁面基本都會(huì)用到沉浸式狀態(tài)欄,單純的沉浸式狀態(tài)欄很容易解決,但是在華為手機(jī)上存在一個(gè)底部虛擬按鍵的問題,會(huì)導(dǎo)致頁面底部和頂部出現(xiàn)很大的問題,下面通過本文給大家分享Android 沉浸式狀態(tài)欄和華為虛擬按鍵沖突問題,一起看看吧
    2017-07-07

最新評論