由慢速测量/布局传递引起的片段转换卡顿

Sni*_*kow 5 performance animation android android-fragments

我的应用程序有一个项目列表,单击其中一个会带来一个片段,其中包含该项目的详细信息。详细信息包含一个有点复杂的 RecyclerView(许多带有 FlexBoxLayoutManager 的小视图),并且测量/布局过程可能会超过 50 毫秒。

这种延迟会导致应用在片段转换开始时丢掉几帧,从而导致相当明显的“跳跃”。

我看到了一些可能的方法来避免这种情况:

  1. 在转换完成之前不要填​​充 RecyclerView(或使其成为存根)。缺点:用户看到一个半空的片段动画。
  2. 延迟整个过渡,直到新片段被测量和布局。缺点:用户反馈延迟。
  3. 启动列表的退出动画,但延迟进入动画,直到新片段被测量和布局。这就是我想做的。

不幸的是,退出动画必须在主线程上运行,测量/布局也是如此;没有什么比AsyncLayoutInflater测量/布局更好的了,对吧?

那么,真的没有办法实现 #3 吗?如果是这种情况,有没有人有更好的解决方案?

演示应用

此应用程序只要onMeasure()选中“慢速测量”复选框,只需在方法中添加睡眠“。

这是一个屏幕录制- 请注意切换复选框时的区别,它是如何跳转的(最明显的是左侧的文本和旧/新片段之间的右侧边缘)。

主活动.kt

var beSlow = false

class SlowWidget @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : TextView(context, attrs, defStyleAttr) {
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        if (beSlow) {
            SystemClock.sleep(100)
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }
}

class SlowFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, parent: ViewGroup?, state: Bundle?): View {
        return inflater.inflate(R.layout.fragment, parent, false)
    }
}

class MainActivity : AppCompatActivity() {
    var i = 0
    val colors = arrayOf(Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW, Color.CYAN)

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

    fun viewClicked(v: View): Unit {
        beSlow = slowbox.isChecked
        supportFragmentManager.beginTransaction()
                .setCustomAnimations(R.anim.slide_in, R.anim.slide_out)
                .replace(R.id.container, SlowFragment().apply {
                    arguments = Bundle().apply {
                        putInt("color", colors[i++ % colors.size])
                    }
                })
                .commit()
    }
}
Run Code Online (Sandbox Code Playgroud)

res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <Button
        android:text="Switch"
        android:onClick="viewClicked"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <CheckBox
        android:id="@+id/slowbox"
        android:text="slow measure"
        android:checked="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>
Run Code Online (Sandbox Code Playgroud)

res/layout/fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<se.dolkow.debug.animdemo.SlowWidget
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:text="Hello World!"
    android:textColor="#000"
    android:gravity="center_vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/rainbow"
    />
Run Code Online (Sandbox Code Playgroud)

res/drawable/rainbow.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle"
    >
    <gradient
        android:startColor="#ffff0000"
        android:centerColor="#ff00ff00"
        android:endColor="#ff8000ff"
        />
</shape>
Run Code Online (Sandbox Code Playgroud)

res/anim/slide_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:shareInterpolator="false"
    >
    <translate android:fromXDelta="100%p" android:toXDelta="0"
        android:interpolator="@android:anim/linear_interpolator" />
</set>
Run Code Online (Sandbox Code Playgroud)

res/anim/slide_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:shareInterpolator="false">
    <translate android:fromXDelta="0" android:toXDelta="-100%p"
        android:interpolator="@android:anim/linear_interpolator" />
</set>
Run Code Online (Sandbox Code Playgroud)