Проблема с ConstraintLayout в Android

158
24 сентября 2019, 15:40

Постараюсь вкратце описать сложившуюся ситуацию. Каковы исходные данные? Представим себе довольно-таки простенький макет, корневым элементом которого является контейнер ConstraintLayout. Внутри родительского элемента у нас располагается 4 View-компонента Button, которые имеют вид квадратной матрицы размером 2x2. Таким образом мы имеем 2 строки, каждая из которых насчитывает по 2 столбца. Компоненты, которые распологаются в одной строке, объединены в горизонтальную цепочку и выровнены по всей ширине родителя при помощи атрибута layout_constraintHorizontal_weight в соотношении 1:1, также присутствуют небольшие отступы для лучшей наглядности.

Макет создан при помощи языка разметки XML, вот исходный текст макета:

<?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/cl_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/b_button1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="15dp"
        android:layout_marginEnd="15dp"
        android:layout_marginBottom="8dp"
        android:text="@string/button_1"
        app:layout_constraintBottom_toTopOf="@+id/b_button3"
        app:layout_constraintEnd_toStartOf="@+id/b_button2"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintStart_toStartOf="parent" />
    <Button
        android:id="@+id/b_button2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="15dp"
        android:layout_marginBottom="8dp"
        android:text="@string/button_2"
        app:layout_constraintBottom_toTopOf="@+id/b_button4"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintStart_toEndOf="@+id/b_button1" />
    <Button
        android:id="@+id/b_button3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="15dp"
        android:layout_marginBottom="15dp"
        android:text="@string/button_3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/b_button4"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintHorizontal_chainStyle="spread_inside"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintStart_toStartOf="parent" />
    <Button
        android:id="@+id/b_button4"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="15dp"
        android:layout_marginEnd="15dp"
        android:layout_marginBottom="15dp"
        android:text="@string/button_4"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintStart_toEndOf="@+id/b_button3" />
</android.support.constraint.ConstraintLayout>

А теперь к самой сути проблемы. Передо мной стоит цель поменять первые 2 кнопки местами (b_button1 и b_button2, которые находятся в одной строке), при этом сохранив их привязки, включая горизонтальную цепочку (chain) и такие же исходные размеры. В процессе программной перепривязки столкнулся с тем, что у меня перестал работать атрибут weight, при помощи которого я выравнивал размер кнопок по всей ширине родителя. Как только не крутил код, но кнопки сначала вообще улетали за экран, а теперь никак не хотят менять свой размер и приобретают ширину в зависимости от своего содержимого, что мне абсолютно не подходит. Подскажите, пожалуйста, что я делаю не так? Привожу часть исходного текста программы, чтобы было понятно, что я пытаюсь сделать:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button bt1, bt2, bt3, bt4;
    ConstraintLayout constraintLayout;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.some_layout);
        bt1 = findViewById(R.id.b_button1);
        bt2 = findViewById(R.id.b_button2);
        bt3 = findViewById(R.id.b_button3);
        bt4 = findViewById(R.id.b_button4);
        bt1.setOnClickListener(this);
        bt2.setOnClickListener(this);
        bt3.setOnClickListener(this);
        bt4.setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        ConstraintSet set = new ConstraintSet();
        constraintLayout = findViewById(R.id.cl_layout);
        set.clone(constraintLayout);
        changeConstraints(set);
        TransitionManager.beginDelayedTransition(constraintLayout);
        set.applyTo(constraintLayout);
    } 
    private void changeConstraints(ConstraintSet set) {
        set.removeFromHorizontalChain(R.id.b_button1);
        set.removeFromHorizontalChain(R.id.b_button2);
        set.clear(R.id.b_button1, ConstraintSet.END);
        set.clear(R.id.b_button2, ConstraintSet.START);
        set.connect(R.id.b_button1, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END);
        set.connect(R.id.b_button1, ConstraintSet.BOTTOM, R.id.b_button4, ConstraintSet.TOP);
        set.connect(R.id.b_button2, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START);
        set.connect(R.id.b_button2, ConstraintSet.BOTTOM, R.id.b_button3, ConstraintSet.TOP);
        set.constrainWidth(R.id.b_button1, ConstraintSet.MATCH_CONSTRAINT);
        set.constrainWidth(R.id.b_button2, ConstraintSet.MATCH_CONSTRAINT);
        set.setMargin(R.id.b_button2, ConstraintSet.START, 15);
        set.setMargin(R.id.b_button1, ConstraintSet.END, 15);
        set.createHorizontalChain(ConstraintSet.PARENT_ID, ConstraintSet.LEFT,
                ConstraintSet.PARENT_ID, ConstraintSet.RIGHT, new int[]{R.id.b_button2, R.id.b_button1}, new float[]{1, 1}, ConstraintSet.CHAIN_SPREAD);
    }
}

P.S. Должно быть так:

А получается вот так:

Answer 1

Очистите все связи переставляемых кнопок.

set.clear(R.id.b_button1, ConstraintSet.END);
set.clear(R.id.b_button1, ConstraintSet.START);
set.clear(R.id.b_button1, ConstraintSet.BOTTOM);

И сделайте ещё связку Button_1 <-> Button_2, иначе кнопки прижимает только к границам. После добавления связки кнопки встают в нужном месте:

set.connect(R.id.b_button1, ConstraintSet.START, R.id.b_button2, ConstraintSet.END);
set.connect(R.id.b_button2, ConstraintSet.END, R.id.b_button1, ConstraintSet.START);

Возникла проблема с margin - не совпадали границы кнопок после перестановки, метод set.setMargin(R.id.b_button2, ConstraintSet.START, 15); устанавливает значение не 15dp (как в xml-разметке), а 15px, поэтому с margin сами потом поиграйте, я, в этом тестовом варианте, убрал из xml-разметки margin и установил margin в коде, изначально ведь не в margin был вопрос. Ещё у вас в разметке, на мой взгляд, лишнее app:layout_constraintHorizontal_bias="0.5" т.к. у вас идет расположение по весам, но если очень хочется добавить, то после перестановки в головной элемент цепи добавьте set.setHorizontalBias(R.id.b_button2,0.5f)

Ниже дополненный код:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button bt1, bt2, bt3, bt4;
    private ConstraintLayout constraintLayout;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bt1 = findViewById(R.id.b_button1);
        bt2 = findViewById(R.id.b_button2);
        bt3 = findViewById(R.id.b_button3);
        bt4 = findViewById(R.id.b_button4);
        constraintLayout = findViewById(R.id.cl_layout);
        bt1.setOnClickListener(this);
        bt2.setOnClickListener(this);
        bt3.setOnClickListener(this);
        bt4.setOnClickListener(this);
        ConstraintSet set = new ConstraintSet();
        set.clone(constraintLayout);
        setMarginToAllButtons(set);
        TransitionManager.beginDelayedTransition(constraintLayout);
        set.applyTo(constraintLayout);
    }
    @Override
    public void onClick(View v) {
        ConstraintSet set = new ConstraintSet();
        set.clone(constraintLayout);
        changeConstraints(set);
        TransitionManager.beginDelayedTransition(constraintLayout);
        set.applyTo(constraintLayout);
    }
    private void changeConstraints(ConstraintSet set) {
        set.removeFromHorizontalChain(R.id.b_button1);
        set.removeFromHorizontalChain(R.id.b_button2);
        set.clear(R.id.b_button1, ConstraintSet.END);
        set.clear(R.id.b_button1, ConstraintSet.START);
        set.clear(R.id.b_button1, ConstraintSet.BOTTOM);
        set.clear(R.id.b_button2, ConstraintSet.END);
        set.clear(R.id.b_button2, ConstraintSet.START);
        set.clear(R.id.b_button2, ConstraintSet.BOTTOM);
        set.connect(R.id.b_button1, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END);
        set.connect(R.id.b_button1, ConstraintSet.BOTTOM, R.id.b_button4, ConstraintSet.TOP);
        set.connect(R.id.b_button2, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START);
        set.connect(R.id.b_button2, ConstraintSet.BOTTOM, R.id.b_button3, ConstraintSet.TOP);
        set.connect(R.id.b_button1, ConstraintSet.START, R.id.b_button2, ConstraintSet.END);
        set.connect(R.id.b_button2, ConstraintSet.END, R.id.b_button1, ConstraintSet.START);
        setMarginToAllButtons(set);
        set.constrainWidth(R.id.b_button1, ConstraintSet.MATCH_CONSTRAINT);
        set.constrainWidth(R.id.b_button2, ConstraintSet.MATCH_CONSTRAINT);
        set.createHorizontalChain(ConstraintSet.PARENT_ID, ConstraintSet.LEFT,
                ConstraintSet.PARENT_ID, ConstraintSet.RIGHT,
                new int[]{R.id.b_button2, R.id.b_button1},
                new float[]{1, 1}, ConstraintSet.CHAIN_SPREAD);
    }
    private void setMarginToAllButtons(ConstraintSet set) {
        setMarginToButton(set, R.id.b_button1);
        setMarginToButton(set, R.id.b_button2);
        setMarginToButton(set, R.id.b_button3);
        setMarginToButton(set, R.id.b_button4);
    }
    private void setMarginToButton(ConstraintSet set, int id) {
        set.setMargin(id, ConstraintSet.START, 16);
        set.setMargin(id, ConstraintSet.END, 16);
        set.setMargin(id, ConstraintSet.BOTTOM, 8);
    }
}
READ ALSO
Исключение IOException в классе для работы c Bluetooth

Исключение IOException в классе для работы c Bluetooth

Всем приветПишу класс для работы с Bluetooth

153
Работа WebSocket в Java (spring и android)

Работа WebSocket в Java (spring и android)

Никак не могу понять, как реализовывать сокеты на java на клиентеЕсть несколько проблем:

126
При добавлении зависимости в проект MAVEN и развертывании на GLASSFISH вылетает ERROR

При добавлении зависимости в проект MAVEN и развертывании на GLASSFISH вылетает ERROR

В web проекте Java есть 2 объекта типа String - JSON и JSONSchemaПытаюсь проверить соответствует ли json схеме

139
transceive() возвращает не то, что должен

transceive() возвращает не то, что должен

Пытаюсь записать/прочитать информацию с NFC тэга (NfcV) M24LR64E-R

123