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

          Material Components—預(yù)備役選手Transition

          共 18154字,需瀏覽 37分鐘

           ·

          2020-12-18 11:59

          Transition是Android Framework在4.4引入的一個全新的動畫框架,可以說是非常古老了,那為什么我現(xiàn)在還要講Transition呢,其實是想通過Transition來引入Material Design Motion。Transition實際上是MD Motion的基礎(chǔ),同時,也是現(xiàn)代化Android開發(fā)動畫的基礎(chǔ)。

          國際慣例,官網(wǎng)鎮(zhèn)樓。

          https://developer.android.google.cn/training/transitions

          基礎(chǔ)概念

          其實從當時的設(shè)計來看,Google在提出Transition框架的時候,就已經(jīng)準備通過申明式UI的方式來創(chuàng)建動畫了,Transition框架的一個核心概念就是Scene,它描述的是一個場景,一個動畫的狀態(tài)值,通常情況下,是動畫的起始狀態(tài)值,有了這樣一個起始態(tài),再加上動畫的具體類型,就可以完整的描述一個動畫的執(zhí)行過程,所以,在申明式的UI編程中,一切都是以Scene作為基礎(chǔ)來進行的。

          Transition的本質(zhì),實際上就是根據(jù)狀態(tài)差異來生成屬性動畫,它實際上是對屬性動畫的抽象和封裝。

          下面通過一個簡單的例子,來演示下如何使用Scene。

          創(chuàng)建Scene Layout

          首先,創(chuàng)建兩個Scene Layout,用于描述動畫的兩個狀態(tài),這里簡單的創(chuàng)建兩個布局,一個布局在左上角和右下角展示一個ImageView,另一個布局在左下角和右上角展示一個ImageView,代碼如下所示。

          "1.0"?encoding="utf-8"?>
          ????xmlns:android="http://schemas.android.com/apk/res/android"
          ????xmlns:app="http://schemas.android.com/apk/res-auto"
          ????android:layout_width="match_parent"
          ????android:layout_height="match_parent">

          ????????????android:id="@+id/imageView1"
          ????????android:layout_width="100dp"
          ????????android:layout_height="100dp"
          ????????app:layout_constraintStart_toStartOf="parent"
          ????????app:layout_constraintTop_toTopOf="parent"
          ????????app:srcCompat="@mipmap/ic_launcher"?/>

          ????????????android:id="@+id/imageView2"
          ????????android:layout_width="100dp"
          ????????android:layout_height="100dp"
          ????????app:layout_constraintBottom_toBottomOf="parent"
          ????????app:layout_constraintEnd_toEndOf="parent"
          ????????app:srcCompat="@android:mipmap/sym_def_app_icon"?/>


          另一個布局,只有Item的位置發(fā)生的改變,id不變,這里就不貼重復(fù)代碼了,要記住的是,對于一個元素的動畫來說,在不同的Scene中,只要id不變,元素就不變,元素位置、屬性的改變,這就是動畫效果。

          創(chuàng)建Scene Container

          一般來說,在一個靜態(tài)布局下,創(chuàng)建具有多個Scene的布局,會將動靜部分分離,將要展示動畫的部分,放置在一個Container中,便于管理,在前面創(chuàng)建好Scene Layout后,下面在主界面的xml中,創(chuàng)建它們的Container,代碼如下所示。

          "1.0"?encoding="utf-8"?>
          "http://schemas.android.com/apk/res/android"
          ????xmlns:tools="http://schemas.android.com/tools"
          ????android:id="@+id/rootContainer"
          ????android:layout_width="match_parent"
          ????android:layout_height="match_parent"
          ????tools:context=".MainActivity">

          ????"@layout/base_scene1"?/>


          通過TransitionManager驅(qū)動動畫

          在代碼中,通過Scene.getSceneForLayout來創(chuàng)建Scene對象,再通過TransitionManager.go來加載指定的場景,代碼如下所示。

          class?MainActivity?:?AppCompatActivity()?{
          ????var?flag?=?true

          ????override?fun?onCreate(savedInstanceState:?Bundle?)?{
          ????????super.onCreate(savedInstanceState)
          ????????setContentView(R.layout.activity_main)
          ????????val?scene1?=?Scene.getSceneForLayout(rootContainer,?R.layout.base_scene1,?this)
          ????????val?scene2?=?Scene.getSceneForLayout(rootContainer,?R.layout.base_scene2,?this)
          ????????rootContainer.setOnClickListener?{
          ????????????if?(flag)?{
          ????????????????TransitionManager.go(scene2)
          ????????????}?else?{
          ????????????????TransitionManager.go(scene1)
          ????????????}
          ????????????flag?=?!flag
          ????????}
          ????}
          }

          當Scene發(fā)生改變時,TransitionManager會自動為其生成相應(yīng)的動畫效果。默認情況下,TransitionManager使用AutoTransition,即漸隱漸顯合并位移動畫,源碼如下所示。

          image-20201208195756453

          所以這里還可以指定動畫效果,例如我們只指定位置改變的動畫,代碼如下所示。

          TransitionManager.go(scene2,?ChangeBounds())

          SDK內(nèi)置了很多種類的動畫效果,如圖所示。

          截屏2020-12-5.34.07

          其中幾種比較常用的解析如下。

          • ChangeBounds:檢測view的位置邊界,創(chuàng)建移動和縮放動畫
          • ChangeTransform:檢測view的scale和rotation,創(chuàng)建縮放和旋轉(zhuǎn)動畫
          • ChangeClipBounds:檢測view的剪切區(qū)域的位置邊界,和ChangeBounds類似,ChangeBounds指定的是剪切區(qū)域setClipBound中的rect
          • ChangeImageTransform:檢測ImageView的大小、位置以及ScaleType,并創(chuàng)建相應(yīng)動畫
          • ChangeScroll:檢測ViewGroup的Scroll,創(chuàng)建Scroll動畫
          • Fade、Slide、Explode:檢測View的Visibility,創(chuàng)建漸入、滑動、爆炸動畫

          創(chuàng)建Transition動畫的幾種方式

          不論是transition的哪種使用方式,transition動畫都有以下幾種創(chuàng)建方式。

          通過xml創(chuàng)建Transition動畫

          在res/transition下創(chuàng)建一個transitionSet的描述文件,代碼如下所示。

          "1.0"?encoding="utf-8"?>


          ????
          ????
          ????

          在代碼中,就可以通過類似LayoutInflater的方式來創(chuàng)建Transition,代碼如下所示。

          TransitionManager.go(
          ????scene2,
          ????TransitionInflater.from(this).inflateTransition(R.transition.transition_from_xml)
          )

          除了創(chuàng)建transitionSet的復(fù)合動畫效果,創(chuàng)建單個的transition動畫也是一樣的,例如下面的代碼。

          "1.0"?encoding="utf-8"?>
          "http://schemas.android.com/apk/res/android"
          ????android:interpolator="@android:interpolator/bounce"
          ????android:duration="200"
          ????android:transitionOrdering="sequential"?/>

          同樣可以通過TransitionInflater進行創(chuàng)建。

          通過代碼創(chuàng)建

          對于單個的transition動畫,可以通過下面的方式進行創(chuàng)建。

          Slide().apply?{
          ????duration?=?200
          ????slideEdge?=?Gravity.BOTTOM
          }

          對于復(fù)合的transition動畫,可以通過下面的方式進行創(chuàng)建。

          TransitionSet().apply?{
          ????addTransition(Fade())
          ????addTransition(Slide())
          }

          前面提到到AutoTransition,也是繼承的TransitionSet實現(xiàn)的復(fù)合動畫。

          不論是怎么使用transition動畫,這些創(chuàng)建transition的方式都是可以混用的。

          beginDelayedTransition

          在前面的講解中,TransitionManager.go是基于場景Scene切換而產(chǎn)生的動畫效果。而Transition框架還提供一種類似自動檢測的動畫機制,這就是通過beginDelayedTransition來實現(xiàn)的。

          下面通過代碼來演示下。

          rootContainer.setOnClickListener?{
          ????val?size?=?imageView1.width
          ????TransitionManager.beginDelayedTransition(
          ????????rootContainer,?
          ????????TransitionInflater.from(this).inflateTransition(R.transition.transition_from_xml))
          ????val?layoutParams?=?imageView1.layoutParams
          ????if?(flag)?{
          ????????layoutParams.width?=?(size?/?1.2).toInt()
          ????????layoutParams.height?=?(size?/?1.2).toInt()
          ????????imageView1.layoutParams?=?layoutParams
          ????????imageView2.visibility?=?View.VISIBLE
          ????????imageView3.visibility?=?View.VISIBLE
          ????????imageView4.visibility?=?View.VISIBLE
          ????}?else?{
          ????????layoutParams.width?=?(size?*?1.2).toInt()
          ????????layoutParams.height?=?(size?*?1.2).toInt()
          ????????imageView1.layoutParams?=?layoutParams
          ????????imageView2.visibility?=?View.INVISIBLE
          ????????imageView3.visibility?=?View.INVISIBLE
          ????????imageView4.visibility?=?View.INVISIBLE
          ????}
          ????flag?=?!flag
          }

          當我們調(diào)用TransitionManager.beginDelayedTransition后,相當于在當前狀態(tài)下打了個tag,將當前狀態(tài)下的View屬性,創(chuàng)建為初始Scene,在此之后View發(fā)生的屬性改變,都將被生成新的Scene,從而產(chǎn)生動畫效果,這也就是beginDelayedTransition這個API命名的原因。

          在上面的代碼中,在初始場景下,調(diào)用了beginDelayedTransition,創(chuàng)建的動畫是changeBounds和explode,在這之后,修改了4個ImageView的屬性——尺寸和visibility,并被作用了changeBounds和explode的動畫效果,最后效果如下所示。

          demo1

          類似的,你還可以設(shè)置Slide這樣的visibility動畫效果,實現(xiàn)滑動的切換效果。

          動畫效果進階

          Slide

          和Fade效果類似,它們都是繼承自Visibility,它比Fade多了一些屬性,除了可以設(shè)置屬性動畫的一些常見屬性外,還可以設(shè)置Slide方向等屬性。

          Explode

          Explode與Slide十分相似,但是元素將根據(jù)Transition Epicenter,輻射狀移動,這個Epicenter可以通過setEpicenterCallback來設(shè)置。

          在Explode中,動畫通過TransitionPropagation計算每個動畫的開始延遲,例如,默認情況下Explode使用CircularPropagation,動畫的延遲取決于元素和Epicenter之間的距離,在代碼中,可以通過setPropagation來設(shè)置自定義的TransitionPropagation,示例代碼如下所示。

          //?確定Explode中心點坐標
          val?viewRect?=?Rect()
          clickedView.getGlobalVisibleRect(viewRect)
          //?設(shè)置Explode?Epicenter
          val?explode:?Transition?=?Explode().apply?{
          ????epicenterCallback?=?object?:?Transition.EpicenterCallback()?{
          ????????override?fun?onGetEpicenter(transition:?Transition?):?Rect?{
          ????????????return?viewRect
          ????????}
          ????}
          }
          explode.duration?=?1000

          ChangeImageTransform

          ChangeImageTransform會對圖片進行Matrix變換,主要作用的是ImageView的ScalaType屬性,通常情況下,ChangeImageTransform會和ChangeBounds配合使用,示例代碼如下所示。

          TransitionSet().apply?{
          ????addTransition(ChangeBounds())
          ????addTransition(ChangeImageTransform())
          }

          ChangeBounds

          ChangeBounds用于改變元素的尺寸和坐標位置,默認情況下,是直線運動的,通過配置Path,可以設(shè)置ChangeBounds的曲線運動路徑,示例代碼如下所示。

          TransitionManager.beginDelayedTransition(transitionsContainer,
          ????????ChangeBounds().apply?{
          ????????????pathMotion?=?ArcMotion().also?{?duration?=?300?}
          ????????})
          val?params?=?button.getLayoutParams()?as?FrameLayout.LayoutParams
          params.gravity?=?if?(isReturnAnimation)?Gravity.LEFT?or?Gravity.TOP?else?Gravity.BOTTOM?or?Gravity.RIGHT
          button.setLayoutParams(params)

          ArcMotion可以設(shè)置minimumHorizontalAngle、minimumVerticalAngle、maximumAngle這樣的屬性來設(shè)置路徑的具體形態(tài)。

          當然,你也可以通過patternPathMotion來設(shè)置類似SVG的自定義路徑。

          setTransitionName

          在使用beginDelayedTransition執(zhí)行Transition動畫時,可以通過設(shè)置transitionName來指定動畫場景起始的相同元素,并讓這些元素執(zhí)行transition動畫,例如為當前界面中的N個元素setTransitionName,當移除界面上全部元素后,只要setTransitionName的值相同,這些元素依然可以執(zhí)行動畫效果。

          Transition界面切換

          同樣的,官網(wǎng)鎮(zhèn)樓。

          https://developer.android.google.cn/training/transitions/start-activity

          Transition框架的一個重要使用場景,就是Activity和Fragment的切換動畫。通常情況下,界面的切換動畫分為兩種類型——Content Transition和Shared Element Transition。

          Content Transition

          對于一次切換來說,A -> B,使用Transition的流程如下所示。

          • A.exitTransition Transition框架會先遍歷A界面確定要執(zhí)行動畫的view(非共享元素view),執(zhí)行A.exitTransition()前A界面會獲取界面的start scene(view 處于VISIBLE狀態(tài)),然后將所有的要執(zhí)行動畫的view設(shè)置為INVISIBLE,并獲取此時的end scene(view 處于INVISIBLE狀態(tài)).根據(jù)transition分析差異的不同創(chuàng)建執(zhí)行動畫。

          • B.enterTransition Transition框架會先遍歷B界面,確定要執(zhí)行動畫的view,設(shè)置為INVISIBLE。執(zhí)行B.enterTransition()前獲取此時的start scene(view 處于INVISIBLE狀態(tài)),然后將所有的要執(zhí)行動畫的view設(shè)置為VISIBLE,并獲取此時的end scene(view 處于VISIBLE狀態(tài)).根據(jù)transition分析差異的不同創(chuàng)建執(zhí)行動畫。

          同理,在從B -> A,返回到A時,流程類似,只不過調(diào)用的方法不同。

          • A.reenterTransition
          • B.returnTransition

          界面切換動畫是建立在visibility的改變的基礎(chǔ)上的,所以getWindow().setEnterTransition(transition);中的參數(shù)一般傳的是Fade,Slide,Explode類的實例(因為這三個類是通過分析visibility不同創(chuàng)

          簡而言之。

          A.exitTransition(): 從A->B時,A的退出動畫

          B.enterTransition(): 從A->B時,B的進場動畫

          B.returnTransition(): 從B->A時,B的退出動畫

          A.reenterTransition(): 從B->A時,A的進場動畫

          一般來說,如果不設(shè)置returnTransition和reenterTransition,那么這兩個場景的動畫,會使用exitTransition和enterTransition的反轉(zhuǎn)動畫。

          下面就通過一個例子來演示下如何設(shè)置界面切換的動畫效果。

          要注意的是,Transition的實現(xiàn)有兩個版本,platform版和AndroidX版,他們的差異在于,AndroidX版的Transition是后續(xù)會持續(xù)迭代的版本,但是不支持Activity和Window間的動畫(至于為什么要這樣設(shè)計,我在之前的文章中已經(jīng)解釋過了),platform版支持,但是后續(xù)不再維護。

          首先,在Theme中設(shè)置Transition開關(guān),如下所示。

          image-20201209200311726

          如果你使用的是Material Design Theme,那么這個值默認為true。

          在代碼中,可以設(shè)置如下所示。

          window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)

          設(shè)置Transition切換的兩種方式

          Transition的切換設(shè)定,可以在代碼或者Theme中進行設(shè)置。

          在Theme中,可以設(shè)置如下。

          image-20201210175859139

          在代碼中,可以如下所示。

          window.exitTransition?=?Explode()
          window.reenterTransition?=?Slide()

          一般來說,如果是針對全局的設(shè)置,可以放在Theme中,但是在代碼中設(shè)置,會更加靈活。

          動畫默認的持續(xù)時間,也是可以設(shè)置的,代碼如下所示。

          window.transitionBackgroundFadeDuration?=?3000

          Transition View & Transition Group

          前面在講解Content Transition的執(zhí)行過程的時候,提到了在動畫開始前,系統(tǒng)會調(diào)用ViewGroup.captureTransitioningViews函數(shù),來獲取需要進行Transition處理的View,如圖所示。

          image-20201210191105455

          在默認情況下,Transition Group的判斷如下所示。

          image-20201210191206722

          另外,在代碼中,還可以通過View.setTransitionGroup(boolean)來主動將一部分View設(shè)置為Transition Group,從而在整體上執(zhí)行動畫。

          為什么會有這樣一個需求呢?其實很明顯,Transition會遍歷頁面中的所有View,包括Toolbar、StatusBar這類的可能通用的組件,那么這個時候,在生成Transition切換動畫的時候,就會產(chǎn)生一些不和諧的畫面,比如這些通用組件的錯位,所以,Transition框架提供了addTarget和excludeTarget方法來指定需要執(zhí)行Transition切換動畫的元素。

          在代碼中,設(shè)置如下。

          window.returnTransition?=?Slide().apply?{
          ????slideEdge?=?Gravity.BOTTOM
          ????excludeTarget(android.R.id.statusBarBackground,?true)
          ????excludeTarget(androidx.appcompat.R.id.action_bar_container,?true)
          }

          這樣就可以在執(zhí)行Transition動畫的時候,排除StatusBar和默認的ToolBar的動畫效果,在xml中,可以在具體的Transition動畫標簽中設(shè)置,如下所示。


          ????"@android:id/statusBarBackground"?/>

          Transition Overlap

          默認情況下,Transition的動畫執(zhí)行不是線性的,即并非A界面的退出動畫執(zhí)行完畢后才會執(zhí)行B界面的進入動畫,它們的執(zhí)行是有一定的并行時間的(即默認為true),稱之為Overlap,在代碼中可以對這個行為進行控制,如下所示。

          "android:windowAllowEnterTransitionOverlap">false
          "android:windowAllowReturnTransitionOverlap">false

          在代碼中,可以設(shè)置如下。

          window.allowEnterTransitionOverlap?=?true
          window.allowReturnTransitionOverlap?=?true

          啟動Transition

          在啟動新的Activity時,需要傳入一個特殊的Bundle對象,代碼如下所示。

          startActivity(
          ????Intent(this,?AnotherActivity::class.java),
          ????ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
          )

          用這個方法替換傳統(tǒng)的startActivity方法,就可以啟動Transition切換動畫了。

          Shared Element Transition

          對于Transition來說,Content Transition單純的是兩個頁面間的切換動畫,每個頁面間都是單獨的執(zhí)行動畫過程,而Shared Element Transition則不同,它標記了兩個界面切換時需要共享動畫效果的元素,讓某些指定的元素,動畫效果更佳豐富。

          而對于執(zhí)行過程中,Content Transition和Shared Element Transition的流程是一致的,只不過為了區(qū)分這兩種不同的Transition類型,在原有命名的基礎(chǔ)上,增加了sharedElement前綴,如下所示。

          window.sharedElementExitTransition

          不過一般情況下,sharedElementXXXXXTransition不用設(shè)置,因為默認是創(chuàng)建類似ChangeBounds的位移和尺寸改變動畫。對于Content Transition來說,通常會使用Fade、Slide、Explode這類繼承Visibility的Transition動畫,而對于Shared Element Transition來說,動畫執(zhí)行前,需要指定要共享的元素的ID,并分析AB界面中,指定ID的元素的屬性變化,從而生成屬性動畫,所以說,即使是Shared Element Transition,所有的動畫效果實際上都是發(fā)生在B界面中的,共享的元素并沒有在兩個界面中傳遞。

          共享元素這個屬性的指定,就需要使用android:transitionName來進行指定。

          啟動Shared Element Transition與Content Transition類似,只是需要指定下共享元素的transitionName,代碼如下所示。

          val?intent?=?Intent(this,?SecondActivity::class.java)
          val?activityOptionsCompat?=?ActivityOptionsCompat.makeSceneTransitionAnimation(this,
          ????????Pair(imageView,?"share_image"),?Pair(textview,?"share_text"))
          startActivity(intent,?activityOptionsCompat.toBundle())

          延遲共享元素動畫

          在某些情況下,共享元素動畫需要延遲一部分時間再執(zhí)行,例如需要等布局渲染完畢,或者網(wǎng)絡(luò)圖片加載完成后再執(zhí)行動畫。這種場景下,就需要使用延遲加載的方式了,主要涉及的API有兩個,即postponeEnterTransition()和startPostponedEnterTransition(),在需要延遲的場景下,先使用postponeEnterTransition暫停動畫的執(zhí)行過程,再在合適的場景下(例如在ViewTree渲染完成或者圖片加載完成后),使用startPostponedEnterTransition恢復(fù)動畫的執(zhí)行。

          這個API也經(jīng)常用來解決Transition動畫切換過程中閃爍的一些問題,例如在進入B界面的時候先暫停動畫,在ViewTreeObserver中渲染完畢后再開啟Transition動畫執(zhí)行。

          SharedElementCallback

          考慮這樣一個場景,A界面通過RecyclerView展示數(shù)據(jù)列表,點擊Item后跳轉(zhuǎn)B界面,B界面通過ViewPager展示詳細數(shù)據(jù),當在B界面滑動數(shù)據(jù)后,回到A界面,A界面應(yīng)該刷新數(shù)據(jù)到B界面訪問到的數(shù)據(jù),這里就需要用到Shared Element Transition提供的SharedElementCallback了。

          在上面的場景下,給A界面設(shè)置setExitSharedElementCallback(SharedElementCallback),給B界面設(shè)置setEnterSharedElementCallback(SharedElementCallback),這樣就可以實現(xiàn)更新的回調(diào)。

          setExitSharedElementCallback(SharedElementCallback):在Activity exit和reenter時都會觸發(fā) setEnterSharedElementCallback(SharedElementCallback):在Activity enter和return時都會觸發(fā)。

          使用Transition動畫的一般方式

          先來看下這樣一個效果,如圖所示。

          transition

          結(jié)合這樣一個例子,我們來看下一般如何處理transition動畫,首先,要對動畫過程進行拆解,無論做什么動畫,這都是第一步。

          在使用Transition動畫時,大部分的場景都是Content Transition和Shared Element Transition同時使用的,這個例子也是這樣,我們可以發(fā)現(xiàn),Image和Text,使用的是Shared Element Transition,而界面B的其它部分,使用的是Content Transition,而界面A,通常不用設(shè)置Transition。

          Shared Element Transition部分

          sharedElementEnterTransition通常也不用設(shè)置,默認會使用ChangeBounds,當然,你也可以修改ChangeBounds的默認行為,例如interpolator,arcMotion等。這里需要執(zhí)行共享元素的Item,就是Image和Text,所以在B界面的XML中,需要指定對應(yīng)的transitionName即可。界面B的布局代碼如下所示。

          "1.0"?encoding="utf-8"?>
          "http://schemas.android.com/apk/res/android"
          ????android:layout_width="match_parent"
          ????android:layout_height="match_parent"
          ????android:orientation="vertical">

          ????????????android:id="@+id/top"
          ????????android:layout_width="match_parent"
          ????????android:layout_height="0dp"
          ????????android:layout_weight="1">

          ????????????????????android:layout_width="150dp"
          ????????????android:layout_height="150dp"
          ????????????android:layout_gravity="center"
          ????????????android:src="@mipmap/ic_launcher"
          ????????????android:transitionName="share_image"?/>

          ????????????????????android:layout_width="wrap_content"
          ????????????android:layout_height="wrap_content"
          ????????????android:layout_gravity="center_horizontal"
          ????????????android:layout_marginTop="40dp"
          ????????????android:text="xuyisheng"
          ????????????android:textSize="30sp"
          ????????????android:transitionName="share_text"?/>

          ????????????????????android:id="@+id/anotherText"
          ????????????android:layout_width="wrap_content"
          ????????????android:layout_height="wrap_content"
          ????????????android:layout_gravity="center_horizontal|bottom"
          ????????????android:text="Transition"?/>

          ????

          ????????????android:id="@+id/bottom"
          ????????android:layout_width="match_parent"
          ????????android:layout_height="0dp"
          ????????android:layout_weight="1"
          ????????android:orientation="vertical">

          ????????????????????android:id="@+id/item1"
          ????????????android:layout_width="match_parent"
          ????????????android:layout_height="60dp"
          ????????????android:layout_margin="8dp"
          ????????????android:background="#bebebe"?/>

          ????????????????????android:id="@+id/item2"
          ????????????android:layout_width="match_parent"
          ????????????android:layout_height="60dp"
          ????????????android:layout_margin="8dp"
          ????????????android:background="#bebebe"?/>

          ????????????????????android:id="@+id/item3"
          ????????????android:layout_width="match_parent"
          ????????????android:layout_height="60dp"
          ????????????android:layout_margin="8dp"
          ????????????android:background="#bebebe"?/>

          ????


          界面A的代碼比較簡單,如下所示。

          "1.0"?encoding="utf-8"?>
          "http://schemas.android.com/apk/res/android"
          ????xmlns:app="http://schemas.android.com/apk/res-auto"
          ????xmlns:tools="http://schemas.android.com/tools"
          ????android:id="@+id/root"
          ????android:layout_width="match_parent"
          ????android:layout_height="match_parent"
          ????tools:context=".MainActivity">

          ????????????android:id="@+id/imageView"
          ????????android:layout_width="50dp"
          ????????android:layout_height="50dp"
          ????????android:layout_marginLeft="32dp"
          ????????android:src="@mipmap/ic_launcher"
          ????????app:layout_constraintBottom_toBottomOf="parent"
          ????????app:layout_constraintLeft_toLeftOf="parent"
          ????????app:layout_constraintTop_toTopOf="parent"
          ????????app:layout_constraintVertical_bias="0.3"?/>

          ????????????android:id="@+id/textview"
          ????????android:layout_width="wrap_content"
          ????????android:layout_height="wrap_content"
          ????????android:layout_marginRight="32dp"
          ????????android:textSize="30sp"
          ????????android:text="xuyisheng"
          ????????app:layout_constraintBottom_toBottomOf="@+id/imageView"
          ????????app:layout_constraintEnd_toEndOf="parent"
          ????????app:layout_constraintTop_toTopOf="@+id/imageView"?/>


          Content Transition部分

          下面的內(nèi)容和中間的文本,使用的是Content Transition,只需要針對這些元素,做相應(yīng)的enterTransition即可,代碼如下所示enter_anim.xml。

          "1.0"?encoding="utf-8"?>
          "http://schemas.android.com/apk/res/android">

          ????"500">
          ????????
          ????????????"@android:id/statusBarBackground"?/>
          ????????

          ????

          ????"600">
          ????????
          ????????????"@id/item1"?/>
          ????????

          ????

          ????"700">
          ????????
          ????????????"@id/item2"?/>
          ????????

          ????

          ????"800">
          ????????
          ????????????"@id/item3"?/>
          ????????

          ????

          ????????????android:slideEdge="left"
          ????????android:startDelay="500">
          ????????
          ????????????"@id/anotherText"?/>
          ????????

          ????


          TransitionListener

          所有的Transition,都可以設(shè)置TransitionListener來監(jiān)聽其執(zhí)行過程,代碼如下所示。

          window.enterTransition?=
          ????TransitionInflater.from(this).inflateTransition(R.transition.enter_anim).apply?{
          ????????addListener(object?:?Transition.TransitionListener?{
          ????????????override?fun?onTransitionStart(transition:?Transition?)?{
          ????????????}

          ????????????override?fun?onTransitionEnd(transition:?Transition?)?{
          ????????????}

          ????????????override?fun?onTransitionCancel(transition:?Transition?)?{
          ????????????}

          ????????????override?fun?onTransitionPause(transition:?Transition?)?{
          ????????????}

          ????????????override?fun?onTransitionResume(transition:?Transition?)?{
          ????????????}
          ????????})
          ????}

          例如可以在Transition結(jié)束后,執(zhí)行其他的屬性動畫等等。

          退出動畫

          在B界面退出的時候,我這里使用了新的動畫效果,即設(shè)置了returnTransition,并非默認效果,而且這里有一點需要注意,那就是enterTransition時,是針對單獨的元素設(shè)置的,而returnTransition,則是分成了上下兩個部分進行動畫(主要是下部分),所以這里需要使用到前面提到的TransitionGroup的概念。在enterTransition的時候,TransitionGroup要設(shè)置為false,在returnTransition的時候,TransitionGroup要設(shè)置為true(因為ViewGroup只要設(shè)置了background或者TransitionName,就會被判斷為TransitionGroup為true)。代碼如下所示return_anim.xml。

          "1.0"?encoding="utf-8"?>
          "http://schemas.android.com/apk/res/android"
          ????android:duration="800">

          ????"top">
          ????????
          ????????????"@id/top"?/>
          ????????

          ????

          ????"left">
          ????????
          ????????????"@id/bottom"?/>
          ????????

          ????

          ????
          ????????
          ????????????"@android:id/statusBarBackground"?/>
          ????????

          ????



          組裝動畫

          在分解完這些動畫后,就可以將整個過程串聯(lián)起來了,界面A代碼如下所示。

          package?com.example.myapplication

          import?android.content.Intent
          import?android.os.Bundle
          import?androidx.appcompat.app.AppCompatActivity
          import?androidx.core.app.ActivityOptionsCompat
          import?androidx.core.util.Pair
          import?kotlinx.android.synthetic.main.activity_main.*

          class?MainActivity?:?AppCompatActivity()?{
          ????override?fun?onCreate(savedInstanceState:?Bundle?)?{
          ????????super.onCreate(savedInstanceState)
          ????????setContentView(R.layout.activity_main)
          ????????root.setOnClickListener?{
          ????????????val?intent?=?Intent(this,?SecondActivity::class.java)
          ????????????val?activityOptionsCompat?=?ActivityOptionsCompat.makeSceneTransitionAnimation(this,
          ????????????????????Pair(imageView,?"share_image"),?Pair(textview,?"share_text"))
          ????????????startActivity(intent,?activityOptionsCompat.toBundle())
          ????????}
          ????}
          }

          界面B,代碼如下所示。

          package?com.example.myapplication

          import?android.os.Bundle
          import?android.transition.Transition
          import?android.transition.TransitionInflater
          import?androidx.appcompat.app.AppCompatActivity
          import?kotlinx.android.synthetic.main.second.*

          class?SecondActivity?:?AppCompatActivity()?{

          ????override?fun?onCreate(savedInstanceState:?Bundle?)?{
          ????????super.onCreate(savedInstanceState)
          ????????setContentView(R.layout.second)
          ????????window.enterTransition?=
          ????????????TransitionInflater.from(this).inflateTransition(R.transition.enter_anim).apply?{
          ????????????????addListener(object?:?Transition.TransitionListener?{
          ????????????????????override?fun?onTransitionStart(transition:?Transition?)?{
          ????????????????????}

          ????????????????????override?fun?onTransitionEnd(transition:?Transition?)?{
          ????????????????????}

          ????????????????????override?fun?onTransitionCancel(transition:?Transition?)?{
          ????????????????????}

          ????????????????????override?fun?onTransitionPause(transition:?Transition?)?{
          ????????????????????}

          ????????????????????override?fun?onTransitionResume(transition:?Transition?)?{
          ????????????????????}
          ????????????????})
          ????????????}
          ????????bottom.isTransitionGroup?=?false
          ????????window.returnTransition?=
          ????????????TransitionInflater.from(this).inflateTransition(R.transition.return_anim)
          ????}

          ????override?fun?onBackPressed()?{
          ????????bottom.isTransitionGroup?=?true
          ????????super.onBackPressed()
          ????}
          }

          通過這種方式,就完成了Transition動畫的一般開發(fā)過程,總結(jié)一下,主要就是下面幾個步驟。

          • 拆解動畫:將過渡動畫拆分成Content Transition和Shared Element Transition
          • 針對Content Transition,對每個元素編寫相應(yīng)的動畫
          • 針對Shared Element Transition,確定好TransitionGroup后,指定兩個頁面之間的transitionName
          • 組裝動畫:借助生命周期回調(diào)等狀態(tài),將動畫串聯(lián)起來

          自定義Transition

          https://developer.android.google.cn/training/transitions/custom-transitions

          官網(wǎng)中其實已經(jīng)給我們提供了非常詳細的說明,同時,參考默認的Slide、Fade這些SDK默認Transition的實現(xiàn),我們可以很方便的自定義,下面就以一個改變background的Transition為例進行講解。

          首先,需要繼承Transition,實現(xiàn)下面三個方法。

          • captureStartValues
          • captureEndValues
          • createAnimator

          前面兩個方法基本都是設(shè)置需要自定義的屬性值,重要的是最后一個方法,創(chuàng)建屬性動畫。

          const?val?CHANGE_COLOR?=?"xys:change_background_color:color"

          class?ChangeBackgroundColorTransition?:?Transition()?{

          ????override?fun?captureStartValues(transitionValues:?TransitionValues?)?{
          ????????captureValues(transitionValues)
          ????}

          ????override?fun?captureEndValues(transitionValues:?TransitionValues?)?{
          ????????captureValues(transitionValues)
          ????}

          ????private?fun?captureValues(transitionValues:?TransitionValues?)?{
          ????????if?(transitionValues?!=?null)?{
          ????????????transitionValues.values[CHANGE_COLOR]?=?transitionValues.view.background
          ????????}
          ????}

          ????override?fun?createAnimator(
          ????????sceneRoot:?ViewGroup?,
          ????????startValues:?TransitionValues?,
          ????????endValues:?TransitionValues?
          ????):?Animator??{
          ????????if?(startValues?==?null?||?endValues?==?null)?{
          ????????????return?null
          ????????}
          ????????val?endView:?View?=?endValues.view
          ????????val?startColorDrawable?=?startValues.values[CHANGE_COLOR]?as?ColorDrawable?
          ????????val?endColorDrawable?=?endValues.values[CHANGE_COLOR]?as?ColorDrawable?
          ????????if?(startColorDrawable?==?null?||?endColorDrawable?==?null)?{
          ????????????return?super.createAnimator(sceneRoot,?startValues,?endValues)
          ????????}
          ????????val?startColor?=?startColorDrawable.color
          ????????val?endColor?=?endColorDrawable.color
          ????????return?ValueAnimator.ofObject(ArgbEvaluator(),?startColor,?endColor).apply?{
          ????????????duration?=?3000
          ????????????addUpdateListener?{?animation?->
          ????????????????val?animatedValue?=?animation.animatedValue?as?Int
          ????????????????endView.setBackgroundColor(animatedValue)
          ????????????}
          ????????}
          ????}
          }

          在前面兩個函數(shù)中,TransitionValues起到了一個容器的作用,保存了values和views,指定了需要作用的對象和值

          使用和系統(tǒng)默認的Transition一樣,示例代碼如下所示。

          TransitionManager.beginDelayedTransition(root,?ChangeBackgroundColorTransition())
          textview.background?=?ColorDrawable(Color.parseColor("#bebebe"))

          可以發(fā)現(xiàn),實際上Transition就是為不同的屬性創(chuàng)建屬性動畫而已,從自定義Transition就可以看出它的本質(zhì)。

          開源庫

          最后,推薦幾個自定義Transition開源庫。

          https://github.com/HJ-Money/MTransition

          https://github.com/ImmortalZ/TransitionHelper

          https://github.com/lgvalle/Material-Animations

          https://github.com/andkulikov/Transitions-Everywhere

          預(yù)備役選手?

          好了,終于到最后了,講了這么多Transition的使用方法,那么為什么我還叫他預(yù)備役選手呢?這就是因為,在Material Design Component中,對Motion進行了進一步的封裝,即:

          • Container transform
          • Shared axis
          • Fade through
          • Fade

          這樣四種封裝好的Motion,而Transition,則正是它們的基礎(chǔ)原理。

          所以,Transition現(xiàn)在雖然用的不多,但是掌握了它的原理,才能更好的開啟MDC Motion之旅。


          最后,介紹下我的網(wǎng)站:https://xuyisheng.top/? 點擊原文,一鍵直達


          Flutter & Android 關(guān)注 《Android群英傳》

          瀏覽 56
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  99视频一区大学生 | 操操操逼逼逼视频网站 | 做爰 视频毛片下载蜜桃 | 亚洲AV无码一区 | 无码一区二区三区四区五区六区 |