ConstraintLayout ConstraintSet - 无法正常使用 Start/End 约束

ConstrainLayout ConstraintSet - not working properly with Start/End constrains

看起来 ConstraintSet 很难应付 Start/End 的限制。

此示例取自 Google 个示例。 Github: android-ConstraintLayoutExamples


当您将左右约束替换为 StartEnd 时,ConstraintSet - 无法正常工作,它仅适用于 Left/Right 约束。
例如替换 layout_constraintStart_toStartOf 替换为 layout_constraintLeft_toLeftOf layout_constraintEnd_toEndOf 和 layout_constraintRight_toRightOf

在以下文件中:
constraintset_example_main.xml
constraintset_example_big.xml

行为:

点击图片:

private ConstraintSet mConstraintSetNormal = new ConstraintSet();

private ConstraintSet mConstraintSetBig = new ConstraintSet();

public void toggleMode(View v) {
    TransitionManager.beginDelayedTransition(mRootLayout);
    mShowBigImage = !mShowBigImage;
    applyConfig();
}

private void applyConfig() {
    if (mShowBigImage) {
       mConstraintSetBig.applyTo(mRootLayout);
    } else {
        mConstraintSetNormal.applyTo(mRootLayout);
    }
}

默认情况下 Android 工作室使用 start/end 约束,因此我想知道根本原因和可能的修复方法。
或者这是 ConstrainSet 本身的错误?

这看起来像是 ConstraintSet 的问题,但让我们看看。以下分析基于 sample project 和您提供的 link。

在示例项目中,我已将 ConstraintLayout 更新到最新版本:

compile 'com.android.support.constraint:constraint-layout:1.1.0-beta5'

我这样做是为了防止我们试图追踪已经解决的问题。我还更新了布局 constraintset_example_big 并将所有 left/right 约束替换为 start/end 约束,如下所示:

constraintset_example_big.xml

<android.support.constraint.ConstraintLayout 
    android:id="@+id/activity_constraintset_example"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginEnd="24dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="24dp"
        android:onClick="toggleMode"
        android:scaleType="centerCrop"
        android:src="@drawable/lake"
        app:layout_constraintDimensionRatio="h,16:9"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:contentDescription="@string/lake_tahoe_image" />

    <TextView
        android:id="@+id/textView9"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/lake_tahoe_title"
        android:textSize="30sp"
        app:layout_constraintStart_toStartOf="@+id/imageView"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toBottomOf="@+id/imageView" />

    <TextView
        android:id="@+id/textView11"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:text="@string/lake_discription"
        app:layout_constraintStart_toStartOf="@+id/textView9"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toBottomOf="@+id/textView9"
        app:layout_constraintEnd_toEndOf="@+id/imageView"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginBottom="16dp"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintVertical_bias="0.0" />

</android.support.constraint.ConstraintLayout>

有了这些变化,这就是我们所看到的。

这显然是不对的。过渡后应该看起来像 this

经过一些调试,我将问题追踪到 ConstraintSetExampleActivity.java 中的这一行:

mConstraintSetBig.load(this, R.layout.constraintset_example_big);

ConstraintSet#load() 看起来很简单,但是如果我们用布局的显式 inflation 替换上面的代码,然后在膨胀布局上克隆 ConstraintSet 如下:

// mConstraintSetBig.load(this, R.layout.constraintset_example_big);
ConstraintLayout cl = (ConstraintLayout) getLayoutInflater().inflate(R.layout.constraintset_example_big,null);
mConstraintSetBig.clone(cl);

我们在应用程序中看到这种行为要好得多。

所以我的结论是 ConstraintSet#load() 在 start/end 约束方面存在问题。解决方法是膨胀 ConstraintLayout 然后进行克隆。

ConstraintSetExampleActivity#onCreate()

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.constraintset_example_main);

    mRootLayout = (ConstraintLayout) findViewById(R.id.activity_constraintset_example);
    // Note that this can also be achieved by calling
    // `mConstraintSetNormal.load(this, R.layout.constraintset_example_main);`
    // Since we already have an inflated ConstraintLayout in `mRootLayout`, clone() is
    // faster and considered the best practice.
    mConstraintSetNormal.clone(mRootLayout);
    // Load the constraints from the layout where ImageView is enlarged.

    // Toggle the comment status on the following three lines to fix/break.
    // mConstraintSetBig.load(this, R.layout.constraintset_example_big);
    ConstraintLayout cl = (ConstraintLayout) getLayoutInflater().inflate(R.layout.constraintset_example_big,null);
    mConstraintSetBig.clone(cl);

    if (savedInstanceState != null) {
        boolean previous = savedInstanceState.getBoolean(SHOW_BIG_IMAGE);
        if (previous != mShowBigImage) {
            mShowBigImage = previous;
            applyConfig();
        }
    }
}

这个问题是已知的,将在 1.1 beta 6 版本中修复

https://issuetracker.google.com/issues/74253269

对于那些面临约束集克隆无法正常工作等问题的人,当我在 api 调用后调用 clone 和 applyTo 方法时,我的布局没有更新到新的约束,事实证明这是由于加载我在导致错误的更改之前显示的对话框。