背景:工作中遇到 需要用到 ShareElement 效果的 需求兼雄,就查閱了一下相關(guān)文章偷线,和github 按自己的理解封裝下
參考: Android高階轉(zhuǎn)場動畫-ShareElement完全攻略
我自己的實(shí)現(xiàn)
優(yōu)點(diǎn):一次配置貌矿,回調(diào)獲取需要的 元素雷蹂,不需要反復(fù)設(shè)置window 的動畫(enterTransition)
碼云 地址:https://gitee.com/zaiqiang231/BaseLib/tree/master/base/src/main/java/com/ziq/base/transition
效果圖:(第二段 沒有配置 字體動畫)
使用方式:
基本思路:
1姆怪、activity.onCreate中 配置要 轉(zhuǎn)場動畫 + ShareElement 需要的配置
TransitionHelper.setUpTransition(this,
shareElementInfoList = {
listOf(
ShareElementInfo<ShareElementInfoDataUpdate>("image", binding.image),
ShareElementInfo<ShareElementInfoDataUpdate>("title", binding.title),
)
}
)
2也物、跳轉(zhuǎn)activity
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, *pairs.toTypedArray())
ActivityCompat.startActivity(activity, intent, options.toBundle())
詳解配置:setUpTransition
class TransitionHelper {
companion object {
fun startActivity(activity: Activity, intent: Intent){
val pairs: MutableList<Pair<View, String>> = mutableListOf()
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, *pairs.toTypedArray())
ActivityCompat.startActivity(activity, intent, options.toBundle())
}
//需要 FEATURE_ACTIVITY_TRANSITIONS
//在 super.oncreate 之前設(shè)置, 或在主題設(shè)置
//activity.window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
fun setUpTransition(
activity: Activity,
shareElementInfoList: (() -> List<ShareElementInfo<*>>)?,
transitionFactory: IShareElementTransitionFactory = DefaultShareElementTransitionFactory(),
){
//是否覆蓋執(zhí)行宫屠,其實(shí)可以理解成是否同時(shí)執(zhí)行還是順序執(zhí)行 false順序執(zhí)行
activity.window.allowEnterTransitionOverlap = true
activity.window.allowReturnTransitionOverlap = true
//是否在透明層做動畫,false 會受到 其他轉(zhuǎn)場動畫影響
activity.window.sharedElementsUseOverlay = true
val customEnterTransition = transitionFactory.buildEnterTransition()
val customExitTransition = transitionFactory.buildExitTransition()
activity.window.enterTransition = customEnterTransition
activity.window.exitTransition = customExitTransition
activity.window.reenterTransition = customExitTransition
activity.window.returnTransition = customEnterTransition
//防止?fàn)顟B(tài)欄閃爍
val enterTransition = activity.window.enterTransition
val exitTransition = activity.window.exitTransition
if (enterTransition != null) {
enterTransition.excludeTarget(Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME, true)
enterTransition.excludeTarget(
Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
true
)
}
if (exitTransition != null) {
exitTransition.excludeTarget(Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME, true)
exitTransition.excludeTarget(Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME, true)
}
activity.window.sharedElementEnterTransition = transitionFactory.buildShareElementEnterTransition()
activity.window.sharedElementExitTransition = transitionFactory.buildShareElementExitTransition()
activity.setEnterSharedElementCallback(object : SharedElementCallback() {
override fun onMapSharedElements(
names: MutableList<String>?,
sharedElements: MutableMap<String, View>?
) {
mapSharedElements(names, sharedElements, shareElementInfoList)
}
override fun onCreateSnapshotView(context: Context?, snapshot: Parcelable?): View? {
var view : View?= null
if(snapshot is ShareElementInfo.ShareElementInfoData<*>){
view = super.onCreateSnapshotView(context, snapshot.snapShot)
ShareElementInfo.ShareElementInfoData.saveToView(view, snapshot)
} else {
view = super.onCreateSnapshotView(context, snapshot)
}
return view
}
override fun onSharedElementStart(
sharedElementNames: MutableList<String>?,
sharedElements: MutableList<View>?,
sharedElementSnapshots: MutableList<View>?
) {
if(sharedElements?.isNotEmpty() == true && sharedElementSnapshots?.isNotEmpty() == true){
val length = sharedElementSnapshots.size
for (i in 0 until length){
val shareElementView = sharedElements.get(i)
val snapshotView = sharedElementSnapshots.get(i)
var info = ShareElementInfo.ShareElementInfoData.getFromView(snapshotView)
if(info == null){
info = ShareElementInfo.ShareElementInfoData.getFromView(shareElementView)
}
info?.updateByView(ShareElementInfo.ShareElementInfoData.DATA_STATUS_START, shareElementView)
ShareElementInfo.ShareElementInfoData.saveToView(shareElementView, info)
}
}
}
override fun onSharedElementEnd(
sharedElementNames: MutableList<String>?,
sharedElements: MutableList<View>?,
sharedElementSnapshots: MutableList<View>?
) {
if(sharedElements?.isNotEmpty() == true && sharedElementSnapshots?.isNotEmpty() == true){
val length = sharedElementSnapshots.size
for (i in 0 until length){
val shareElementView = sharedElements.get(i)
val snapshotView = sharedElementSnapshots.get(i)
var info = ShareElementInfo.ShareElementInfoData.getFromView(snapshotView)
if(info == null){
info = ShareElementInfo.ShareElementInfoData.getFromView(shareElementView)
}
info?.updateByView(ShareElementInfo.ShareElementInfoData.DATA_STATUS_END, shareElementView)
ShareElementInfo.ShareElementInfoData.saveToView(shareElementView, info)
}
}
}
})
activity.setExitSharedElementCallback(object : SharedElementCallback() {
override fun onMapSharedElements(
names: MutableList<String>?,
sharedElements: MutableMap<String, View>?
) {
mapSharedElements(names, sharedElements, shareElementInfoList)
}
override fun onCaptureSharedElementSnapshot(
sharedElement: View?,
viewToGlobalMatrix: Matrix?,
screenBounds: RectF?
): Parcelable {
val snapshot = super.onCaptureSharedElementSnapshot(sharedElement, viewToGlobalMatrix, screenBounds)
var clz : Class<ShareElementInfoDataUpdate>? = null
shareElementInfoList?.invoke()?.let { list ->
for (info in list) {
if (info.getView() == sharedElement){
clz = info.clz as Class<ShareElementInfoDataUpdate>?
break
}
}
}
val shareElementInfoData = ShareElementInfo.ShareElementInfoData(snapshot, clz)
shareElementInfoData.updateByView(ShareElementInfo.ShareElementInfoData.DATA_STATUS_CAPTURE_SNAPSHOT, sharedElement)
return shareElementInfoData
}
})
}
private fun mapSharedElements(
names: MutableList<String>?,
sharedElements: MutableMap<String, View>?,
shareElementInfoList: (() -> List<ShareElementInfo<*>>)?,
) {
names?.clear()
sharedElements?.clear()
shareElementInfoList?.invoke()?.let { list ->
for (info in list) {
val view: View = info.getView()
ViewCompat.getTransitionName(view)?.let {
names?.add(it)
sharedElements?.put(it, view)
}
}
}
}
}
}
setUpTransition方法分了下面幾步:
1滑蚯、配置頁面轉(zhuǎn)場動畫(activity.window.enterTransition 等)
2浪蹂、配置共享元素動畫(activity.window.sharedElementEnterTransition 等)
3抵栈、設(shè)置 setEnterSharedElementCallback、setExitSharedElementCallback
SharedElementCallback 中這里打算統(tǒng)一 走回調(diào)的形式 執(zhí)行共享元素動畫 之前回調(diào)獲取相關(guān)配置
例子:
進(jìn)入:A -> B
A的Exit SharedElementCallback坤次、B的Enter SharedElementCallback 分別起作用
后退:B -> A
A的Exit SharedElementCallback古劲、B的Enter SharedElementCallback 分別起作用(還是相同的callback 起作用,所以兩個(gè)callback 有相同的回調(diào)缰猴,但enter 和 exit 需要實(shí)現(xiàn)的方法有所 不同)
要想share element 起相關(guān)产艾,需要A、B頁面設(shè)置 能匹配上的配置才行滑绒,關(guān)鍵是onMapSharedElements 去匹配闷堡,這里走回調(diào)去動態(tài)拿配置,本來ActivityOptionsCompat.makeSceneTransitionAnimation 也可以配置 Pair<View, String> 去設(shè)置share info疑故, 但這里統(tǒng)一去用onMapSharedElements 回調(diào)獲取
其他額外 的代碼 就是為了自定義共享元素動畫 去做的,碼云上有詳細(xì)例子缚窿,ChangeTextTransition, 字體大小和字體顏色 變化焰扳。 主要是因問 onSharedElementStart倦零、onSharedElementEnd 進(jìn)入 和后退 的回調(diào)順序不同,所以要額外處理 使得 自定義的數(shù)據(jù)吨悍。能帶到Transition 中 去讀取