【问题标题】:Copy properties from one view in constraint layout to another when it is hidden隐藏时将约束布局中的一个视图的属性复制到另一个视图
【发布时间】:2019-12-03 09:19:38
【问题描述】:

我是使用 ConstraintLayout 的初学者

我有一个用例,当一个 TextView 在约束布局中消失时,我想将另一个 TextView 放置到与现在隐藏的 TextView 完全相同的位置。 在 xml 中很容易,是否有可能以编程方式实现相同的目标?

这是更改前的布局 xml:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:ignore="ContentDescription">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:focusable="true">

        <TextView
            android:id="@+id/some_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="16dp"
            android:layout_marginTop="20dp"
            android:ellipsize="end"
            android:maxLines="2"
            app:layout_constrainedWidth="true"
            app:layout_constraintBottom_toTopOf="@+id/textView1"
            app:layout_constraintEnd_toStartOf="@+id/textView2"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="SOME TITLE" />

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="16dp"
            android:ellipsize="end"
            android:maxLines="1"
            android:textColor="@color/stone"
            app:layout_constrainedWidth="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/textView2"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/some_title"
            tools:text="I WILL BE HIDDEN" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:layout_marginEnd="16dp"
            android:layout_marginStart="16dp"
            android:ellipsize="end"
            android:enabled="false"
            android:maxLines="2"
            android:maxWidth="88dp"
            android:gravity="end"
            android:paddingTop="32dp"
            app:layout_constrainedWidth="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            tools:text="I WILL CHANGE THE POSITION"
            tools:textColor="@color/green" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</merge>

下面是它在更改后应该如何处理:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:ignore="ContentDescription">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/card_container"
        android:clickable="true"
        android:focusable="true">

        <TextView
            android:id="@+id/someTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="16dp"
            android:layout_marginTop="20dp"
            android:ellipsize="end"
            android:maxLines="2"
            app:layout_constrainedWidth="true"
            app:layout_constraintBottom_toTopOf="@+id/textView2"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="SOME TITLE"  />

        <TextView
            android:id="@+id/textView1"
            android:visibility="gone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="16dp"
            android:ellipsize="end"
            android:maxLines="1"
            android:textColor="@color/stone"
            app:layout_constrainedWidth="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/textView1"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/someTitle"
            tools:text="I AM HIDDEN NOW" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="16dp"
            android:ellipsize="end"
            android:maxLines="1"
            android:textColor="@color/stone"
            app:layout_constrainedWidth="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/someTitle"
            tools:text="I SHOULD REPLACED THE TEXTVIEW1 POSITION!"
            tools:textColor="@color/green" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</merge>

所以,如您所见,我刚刚将所有定位值从“textView1”复制到“textView2”(对“someTitle”进行了一些调整) 如何以编程方式进行?

【问题讨论】:

  • 是的,@RahulShukla 是对的。 K. Os,你为什么要为这么简单的问题设置赏金? :)
  • 好吧,在这种情况下,我必须从头开始创建所有内容,这非常糟糕,因为 android 中没有这样的 API 可以简单地将所有属性复制到另一个,所以我不需要做这一切

标签: android android-constraintlayout


【解决方案1】:

我只是想出了自己的解决方案,因为其他解决方案都没有完全涵盖我的用例。 所以我刚刚引入了一个屏障https://developer.android.com/reference/android/support/constraint/Barrier 来将底部引用从底部some_titleTextView 保持到其他TextView 的顶部。在那种情况下,在布局 xml 中进行了调整:

 <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/card_container"
        android:clickable="true"
        android:focusable="true">

        <TextView
            android:id="@+id/someTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="16dp"
            android:layout_marginTop="20dp"
            android:ellipsize="end"
            android:maxLines="2"
            app:layout_constrainedWidth="true"
            app:layout_constraintBottom_toTopOf="@+id/bottomAreaBarrier"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="SOME TITLE" />

        <androidx.constraintlayout.widget.Barrier
            android:id="@+id/bottomAreaBarrier"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:barrierDirection="top"
            app:constraint_referenced_ids="textView1,textView2" />

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="16dp"
            android:ellipsize="end"
            android:maxLines="1"
            android:textColor="@color/stone"
            app:layout_constrainedWidth="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toStartOf="parent"
            tools:text="TEXTVIEW TO BE REPLACED SOMETIMES" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:layout_marginEnd="16dp"
            android:layout_marginStart="16dp"
            android:ellipsize="end"
            android:enabled="false"
            android:maxLines="2"
            android:maxWidth="88dp"
            android:gravity="end"
            app:layout_constrainedWidth="true"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            tools:text="I WILL CHANGE POSITION FOR TEXTVIEW1 SOMETIMES"
            tools:textColor="@color/green" />

    </androidx.constraintlayout.widget.ConstraintLayout>

在这种情况下,我可以简单地更改textView2 的重力:

  if (textView1.text.isEmpty()) {
      textView2.gravity = Gravity.START
   } else {
      textView2.gravity = Gravity.END
   }

【讨论】:

    【解决方案2】:

    为此,我有 2 种方法,我的第一选择是更改我感兴趣的视图的 ConstraintSets

     ConstraintSet constraintSet = new ConstraintSet();
        constraintSet.clone(constraintLayout);
        constraintSet.connect(textView2.getId(), ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM);
        constraintSet.connect(textView2.getId(), ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START);
        constraintSet.applyTo(constraintLayout);
    

    当然,这并不容易。

    我的第二种方法比第一种更容易,是复制TextView1LayoutParams 或查看为GONE,然后将其从布局中删除并替换为TextView2 像这样

      ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) textView1.getLayoutParams();
    
        constraintLayout.removeView(textView1);
        constraintLayout.removeView(textView2);
        constraintLayout.addView(textView2, params);
    

    要使这两个选项起作用,您需要更改布局,删除将要操作的视图与应保留在屏幕上的视图连接起来的任何约束,例如

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout   xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:ignore="ContentDescription"
    
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/constraintLayout">
    
    <TextView
        android:id="@+id/someTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="8dp"
        android:ellipsize="end"
        android:maxLines="2"
        android:text="SOME TITLE"
        android:textColor="@android:color/black"
        app:layout_constrainedWidth="true"
        app:layout_constraintBottom_toTopOf="@+id/textView1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    
    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginBottom="20dp"
        android:ellipsize="end"
        android:maxLines="1"
        android:text="I WILL BE HIDDEN"
        android:textColor="@android:color/holo_blue_dark"
        app:layout_constrainedWidth="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/someTitle" />
    
        <TextView
            android:id="@+id/textView2"
            android:layout_width="220dp"
            android:background="@android:color/black"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:layout_marginEnd="16dp"
            android:layout_marginStart="16dp"
            android:ellipsize="end"
            android:enabled="false"
            android:maxLines="2"
            android:maxWidth="88dp"
            android:gravity="end"
            android:paddingTop="32dp"
            app:layout_constrainedWidth="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:text="I WILL CHANGE THE POSITION"
            android:textColor="@android:color/holo_green_dark" />
    
           </androidx.constraintlayout.widget.ConstraintLayout>
    

    你会得到你的结果。我通过选项 2 得到了这个

    使用选项 2,您可以创建包含 2 个视图的方法,您可以使用该函数快速使用视图

       private void changeReplaceViews(View viewToReplace, View viewToTakeOver){
        ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) viewToReplace.getLayoutParams();
        constraintLayout.removeView(viewToReplace);
        constraintLayout.removeView(viewToTakeOver);
        constraintLayout.addView(viewToTakeOver, params);   
    }
    

    【讨论】:

    • 如果此布局位于回收站视图内,似乎将不起作用 - 您的解决方案尚未应用于尚未在屏幕上呈现的项目。如果我从另一个屏幕返回后返回此屏幕也是如此
    • @K.Os 如果视图不在回收器中,您需要调用 view.requestLayout() 以使用新的 params 重新渲染它,以便回收器视图调用 notifyDataSetChanged 也可以已在问题中指定您想在回收站中显示该项目,这是一个通用的解决方案。
    【解决方案3】:

    这很简单,Android框架支持通过代码和xml两种方式添加视图。在 getVisibility()isShown() 的帮助下检查视图的可见性。确定可见性状态后,您可以创建一个新视图并将其附加到父 ViewGroup 容器。考虑在这里创建TextView

    RelativeLayout mRelativeLayout = (RelativeLayout)findViewById(R.id.relative_parent_container);
    TextView mTextView = new TextView(this); 
    RelativeLayout.LayoutParams mParams = new RelativeLayout.LayoutParams((int)LayoutParams.WRAP_CONTENT,(int)LayoutParams.WRAP_CONTENT);
    mParams.leftMargin = 50;
    mParams.topMargin  = 50;
    mTextView.setText("Hello!");
    mTextView.setTextSize((float) 20);
    mTextView.setPadding(20, 50, 20, 50);
    mTextView.setLayoutParams(mParams);
    mRelativeLayout.addView(tv[i]);
    

    这是TextView 的示例创建并附加到父RelativeLayout。您可以创建所需的视图并以相同的方式附加到父容器,而无需担心ViewGroup 的类型。在您的情况下,如果您需要将View 附加到ConstraintLayout,则该视图需要 ConstraintLayout.LayoutParams

    如果要复制/克隆现有视图,

    TextView existingTv = (TextView)findViewById(R.id.existing_tv);
    TextView cloned = new TextView(getApplicationContext());
    cloned.setText(existingTv.getText());
    cloned.setLayoutParams(existingTv.getLayoutParams()); //override anything you want
    

    从 ConstraintLayout 克隆视图时请耐心等待。 ConstraintLayout.LayoutParams 缓存其参数,并与您使用它的 View 相关联,因此您不能简单地将一个小部件的 LayoutParams 传递给另一个。

    您必须改为为您的新 View 对象生成一个新的 LayoutParams,并复制相应的字段。

    例如,如果你有这样的东西,

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout 
        xmlns:android="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/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <TextView
            android:text="Hello!"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/tv"
            android:layout_marginTop="16dp"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginStart="16dp"
            app:layout_constraintLeft_toLeftOf="parent"
            android:layout_marginLeft="16dp" />
    
    </android.support.constraint.ConstraintLayout>
    

    那么你就可以按照下面的方式进行了,

    ConstraintLayout layout = (ConstraintLayout) findViewById(R.id.container);
    TextView textView = (TextView) findViewById(R.id.tv);
    ConstraintLayout.LayoutParams mParam = 
                    (ConstraintLayout.LayoutParams) textView.getLayoutParams();
    
    TextView clonedTextView = new TextView(this);
    
    ConstraintLayout.LayoutParams newParams = new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT,ConstraintLayout.LayoutParams.WRAP_CONTENT);
    
    newParams.leftToLeft = mParam.leftToLeft;
    newParams.topToTop = mParam.topToTop;
    newParams.leftMargin = mParam.leftMargin;
    newParams.topMargin = mParam.topMargin;
    
    layout.addView(clonedTextView, -1, newParams);
    

    这会将第二个 TextView 放在与 XML 布局中定义的完全相同的位置。

    【讨论】:

    • 没有像简单地将所有现有参数从一个文本视图复制到另一个这样的api吗?所以我不想从头开始重新创建所有内容
    • 顺便说一句,我用的是 ConstraintLayout,也没关系,用 Kotlin 编码 :)
    • 顺便说一句,请为我的情况提供具体的解决方案,我会给你赏金,因为我不确定你的解决方案是否能详细说明
    • 请查看更新后的答案。如果您使用的是 kotlin,则相同的方法应该适用于它,只需对示例代码进行少量更改。跳它有帮助:-)
    • 感谢更新,但“getLayoutParams”是否适用于 ConstraintLayout 中的约束?正如我正在尝试的那样,我担心它不会
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    • 2021-09-28
    • 1970-01-01
    • 2017-02-11
    • 1970-01-01
    相关资源
    最近更新 更多