View与Activity之间共享元素动效原理

探讨Android中未覆盖的视图与Activity及视图间的共享元素动效实现方式,通过传递视图位置和尺寸参数,使视图在跳转过程中能够执行平滑过渡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

篇章目标要点

目前Android源码自带功能已经提供了Activity向Activity, Fragment向Fragment跳转场景的共享元素动效,均提供了非常不错的体验。但是在实际在开发当中,View面向Activity和View与View之间跳转的应用场景是比较频繁的,这2个场景Android源码并未提供相应的接口使用。本篇文章的目的就是针对这2个场景尝试进行探索,虽然效果还有较大的差距,我向先初步共享下自己的实现思路和基础代码

Android共享元素动效和应用场景

此处简要介绍下Android原生的共享元素动效的用法和场景,Android原生提供的共享元素动效用法基本为2步
第一步,在跳转前后画面中关联的元素layout中设置相同的标签,如下所示

android:transitionName="picture"

第二步,设置跳转参数即可

ActivityOptionsCompat options = ActivityOptionsCompat
        .makeSceneTransitionAnimation(getActivity(),
                imageView,"picture");

受限场景,仅在Activity进入onCreate时动效才能生效,故如希望跳转至目标Activity时每次都有动效,则目标Activity的LaunchMode需要设置为standard.
以下是Android原生提供的几种共享元素动效及适用场景

//新的Activity以某个View的参考点为中心点逐步方法
1.	ActivityOptions.makeScaleUpAnimation   
//适用于Activity之间的跳转,仅有一个共享元素
2.ActivityOptions.makeSceneTransitionAnimation (Activity activity, View sharedElement, String sharedElementName)
//适用于Activity之间的跳转,有多个共享元素
3.ActivityOptions makeSceneTransitionAnimation(Activity activity, Pair<View, String>... sharedElements) 

View 与Activity之间跳转共享元素动效实现

关于悬浮窗View与Activity之间跳转的共享元素动效才是本文的重点内容

1. 实现效果

在这里插入图片描述

2. 实现思路

过程的核心要点就是将关联的共享元素View的原位置和尺寸带入新的视图当中,然后设置新的视图当中相应的共享元素View从原位置开始执行属性动效,提供一种类似从原位置移动过来的感受。注意执行属性动效时,为了避免共享元素View的父布局限制了其超出边界无法显示,故需要在其父类视图设置不切除子视图边界。
在这里插入图片描述

3. View拉起Activity主要代码
  1. Intent中带入原View的位置和尺寸参数
//打包原视图的位置和尺寸参数
private Bundle getTransitionBundle(){
    int[] location = new int[2];
    mImageView.getLocationOnScreen(location);
    Rect rect = new Rect();
    mImageView.getLocalVisibleRect(rect);
    Bundle b = new Bundle();
    b.putIntArray("loc", location);
    b.putInt("width", rect.width());
    b.putInt("height", rect.height());
    Log.d(TAG, "origin x = "+location[0]+", y = "+location[1]+ ", width = "+rect.width()+",height = "+rect.height());
    return b;
}
……
//Intent中附带参数
Intent intent = new Intent(AnimApplication.getInstance().getApplicationContext(), MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtras(getTransitionBundle());
AnimApplication.getInstance().startActivity(intent);

  1. 设置Activity中相应View从原位置执行属性动画
public void enterAnimation(){
    Log.d(TAG, "execute animation");
    if(null == getIntent()){
        Log.d(TAG, "intent is null");
        return;
    }
    Bundle b = getIntent().getExtras();
    if(null == b){
        Log.d(TAG, "bundle is null");
        return;
    }
    mAlbumImageView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            mAlbumImageView.getViewTreeObserver().removeOnPreDrawListener(this);
            //获取bundle传递过来的原视图的位置和尺寸参数
            int[] originLoc = b.getIntArray("loc");
            int originWidth = b.getInt("width");
            int originHeight = b.getInt("height");
            mAlbumImageView.getLocationOnScreen(latestLoc);
            Log.d(TAG, "get album location on screen x = " + latestLoc[0] +", y = "+ latestLoc[1]);
//设置共享元素View的初始大小和位置
            mAlbumImageView.setPivotX(0.5f);
            mAlbumImageView.setPivotY(0.5f);
            mAlbumImageView.setScaleX(originWidth*1.0f/mAlbumImageView.getWidth());
            mAlbumImageView.setScaleY(originHeight*1.0f/mAlbumImageView.getHeight());
            mAlbumImageView.setTranslationX(originLoc[0] - latestLoc[0]);
            mAlbumImageView.setTranslationY(originLoc[1] - latestLoc[1]);
            width = mAlbumImageView.getWidth();
            height = mAlbumImageView.getHeight();
            //设置共享元素View执行属性动画缩放移动到新视图的位置
mAlbumImageView.animate().scaleX(1).scaleY(1).translationX(0).translationY(0).setDuration(500).setInterpolator(new LinearInterpolator()).setListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {
                    Log.d(TAG, "animation start");
                }

                @Override
                public void onAnimationEnd(Animator animator) {
                    Log.d(TAG, "animation end");
                }

                @Override
                public void onAnimationCancel(Animator animator) {

                }

                @Override
                public void onAnimationRepeat(Animator animator) {

                }
            });

            return false;
        }
    });
}

  1. Activity拉起View主要代码
    这部分与第2点是逆过程,原理和代码基本类似,在此不赘述了。需要特别提醒的是回到View执行位移动效时,需要确保View属性动画轨迹完全可见,需要在添加悬浮窗View时为之设置一个尺寸为全屏的容器,待属性动画结束之后再将其布局大小缩小为目标大小。

存在问题

  1. View向Activity进行切换时执行动画的时机控制还是存在一些bug,有时出现了Activity中相应的View已经显示了才执行动画的case,虽然概率较低,该处的时机需要再优化一下。
  2. 代码目前较为凌乱,需要封装一下成为使用较为简单的基类和接口

学习心得

本篇文章只是初步提出了悬浮窗View与Activity之间互相跳转的共享元素动效的基本思路和基本实现,作为自己学习过程的笔记信息。还有较多不足之处,各位读者如有更好的想法,欢迎提出一起讨论。下一步是完善本思路存在的问题,以及进行View与View之间的共享元素动效实现。

<think>好的,用户想了解Android中的多阻断、以及滑果的概念和实现方式。首先,我需要明确这几个术语的具体含义。 “多阻断”可能是指处理多个事件或画时的阻断机制,比如在多个画同时运行时如何控制它们的执行顺序或优先级。不过,在Android开发中,并没有直接叫做“多阻断”的术语。这可能是指避免UI阻塞,或者处理多个触摸事件时的阻断策略,比如使用同步机制或线程管理来确保画流畅运行。 然后是,即Motion Effect,通常指界面元素的果,如转场画、属性变化等。Android提供了多种画API,比如属性画(Property Animation)、补间画(Tween Animation)以及Transition框架。 滑果应该是指元素在滑过程中的画,比如列表滚、侧滑菜单等。实现滑画可以使用ScrollView、RecyclerView的滚监听,或者结合ValueAnimator和手势检测(GestureDetector)来自定义滑果。 接下来,我需要结合提供的引用内容。引用[2]提到了基于Animated封装了滑画类,这可能涉及使用Animated库来实现平滑的滑果。引用[4]提到SlideModal组件支持滑方向自定义,可能需要处理滑事件和画的协调。 在实现方式上,属性画(如ObjectAnimator)适合处理视图属性的渐变,而手势检测可以捕捉用户的滑作,结合ValueAnimator来更新视图的位置。此外,需要注意在UI线程中避免耗时操作,防止卡顿,可能涉及异步处理或使用Handler。 关于多阻断,可能需要考虑在多个画同时执行时使用AnimatorSet来管理顺序,或者使用锁机制确保资源竞争时的稳定性。引用[1]提到useLayoutEffect在DOM变更后同步执行,类比Android可能是确保画在布局完成后立即执行,避免闪烁。 最后,用户可能需要具体的代码示例,比如使用ObjectAnimator实现平移画,或者结合GestureDetector处理滑事件。同时,性能优化方面,可以提到使用硬件加速、避免过度绘制等策略。</think>### Android中多阻断、画的实现 #### 一、多阻断(Multi-thread Blocking Control) 在Android中,“多阻断”通常指对多线程任务或异步操作的协调控制,避免UI线程阻塞。例如: 1. **主线程保护**:耗时任务(如网络请求、复杂计算)需放在子线程执行,通过`AsyncTask`、`Handler`或`Kotlin协程`实现异步调度[^1]。 2. **同步机制**:使用`synchronized`关键字或`ReentrantLock`解决多线程资源竞争问题。 3. **画阻断处理**:若多个画同时运行,可通过`AnimatorSet`设置执行顺序,或通过`cancel()`终止冲突画。 --- #### 二、(Motion Effects) 通过改变视图属性实现平滑过渡,核心实现方式包括: 1. **属性画(Property Animation)** ```kotlin ObjectAnimator.ofFloat(view, "translationX", 0f, 100f).apply { duration = 1000 start() } ``` - 特点:支持透明度、位移、旋转等属性变化,通过插值器(如`BounceInterpolator`)控制速率[^2]。 2. **过渡画(Transition API)** - 用于Activity/Fragment间转场,定义`enterTransition`和`exitTransition`实现共享元素画。 --- #### 三、滑画(Swipe Animation) 滑画需结合手势检测属性画: 1. **手势监听**:通过`GestureDetector`识别滑方向速度。 ```kotlin view.setOnTouchListener { v, event -> gestureDetector.onTouchEvent(event) } ``` 2. **滑画实现**(以侧滑菜单为例): ```kotlin val animator = ValueAnimator.ofFloat(startX, endX).apply { addUpdateListener { view.translationX = it.animatedValue as Float } start() } ``` 3. **弹性果**:使用`SpringAnimation`实现滑回弹: ```kotlin SpringAnimation(view, DynamicAnimation.TRANSLATION_X, 0f) .setSpring(SpringForce(0f).setDampingRatio(0.5f)) .start() ``` --- #### 四、性能优化 1. **避免过度绘制**:使用`GPU过度绘制调试工具`识别问题。 2. **硬件加速**:在Manifest中为Activity设置`android:hardwareAccelerated="true"`。 3. **画复用**:对重复画使用`ViewPropertyAnimator`链式调用。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值