Как программно добавить представления и ограничения в ConstraintLayout?

У меня проблема с программным добавлением просмотров в Constraint Layoutи установите все ограничения, необходимые для работы макета.

То, что у меня на данный момент не работает:

ConstraintLayout layout = (ConstraintLayout) findViewById(R.id.mainConstraint);
ConstraintSet set = new ConstraintSet();
set.clone(layout);

ImageView view = new ImageView(this);
layout.addView(view,0);
set.connect(view.getId(), ConstraintSet.TOP, layout.getId(), ConstraintSet.TOP, 60);
set.applyTo(layout);

ImageView даже не появляется на макете. При добавлении в RelativeLayout, Отлично работает.

Что я могу сделать, чтобы создать необходимые ограничения, чтобы мой макет снова работал?

4 ответа

Решение

Я думаю, что вы должны клонировать макет после добавления вашего ImageView.

ConstraintLayout layout = (ConstraintLayout)findViewById(R.id.mainConstraint);
ConstraintSet set = new ConstraintSet();

ImageView view = new ImageView(this);
layout.addView(view,0);
set.clone(layout);
set.connect(view.getId(), ConstraintSet.TOP, layout.getId(), ConstraintSet.TOP, 60);
set.applyTo(layout);

Объединение этого Как мне динамически добавлять элементы в представление, созданное с помощью XML, с помощью /questions/29540259/kak-programmno-dobavit-predstavleniya-i-ogranicheniya-v-constraintlayout/29540277#29540277

Я нашел более простое решение. Это лучше всего подходит для добавления нескольких согласованных представлений, использующих Viewbinding и ViewModel.

  1. Создайте файл fragment_home.xml
  2. Добавьте LinearLayout в конец fragment_home.xml
  3. Создайте файл dynamic_view.xml, который вы надуете.
  4. Получить элементы из ViewModel / Array или любого другого доступного источника данных
  5. Надуть и добавить элементы

В этом случае я создаю текстовое представление (заголовок) с помощью recyclerview[для этого есть библиотеки, однако я обнаружил, что для этого больше контроля]

Шаг 1,2

      <?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.hometab.HomeFragment">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/constraint_parent"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- otherviews here -->

        <LinearLayout
            android:id="@+id/dynamic_linear_layout"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/margin_large"
            android:orientation="vertical"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/curated_recycler" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

Шаг 3

          <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/dynamic_constraint"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_begin="@dimen/margin_large"
        app:layout_constraintStart_toStartOf="parent" />

    <View
        android:id="@+id/title_view"
        android:layout_width="0dp"
        android:layout_height="40dp"
        android:layout_gravity="center"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <TextView
        android:id="@+id/shop_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/diary"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        android:textColor="@android:color/black"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="@+id/title_view"
        app:layout_constraintStart_toStartOf="@id/guideline"
        app:layout_constraintTop_toTopOf="@+id/title_view" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/shop_recycler"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:clipToPadding="false"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@id/guideline"
        app:layout_constraintTop_toBottomOf="@+id/title_view" />

</androidx.constraintlayout.widget.ConstraintLayout>

Шаг 4,5

          binding = FragmentHomeBinding.inflate(getLayoutInflater());

    ShopViewModel shopViewModel = new ViewModelProvider(this).get(ShopViewModel.class);
    shopViewModel.getAllShops(false).observe(getViewLifecycleOwner(), shopEntities -> {
        List<ConstraintLayout> shopConstraints = new ArrayList<>();

        for (ShopEntity shopEntity : shopEntities) {
            // get an instance of the dynamic_view.xml
            DynamicViewBinding dynamicViewBinding = DynamicViewBinding.inflate(getLayoutInflater());
            // add text view content
            dynamicViewBinding.shopName.setText(shopEntity.getName());
            // initialize the recycler layout adapter
            dynamicViewBinding.shopRecycler.setLayoutManager(new ProductCardLayoutManager(getContext(), 1,
                    GridLayoutManager.HORIZONTAL, false, 80));

            // get all products and add them to recycler view
            productViewModel.getAllProductsByShop(shopEntity.getShopId(), 10).observe(getViewLifecycleOwner(), productEntities ->
                    dynamicViewBinding.shopRecycler.setAdapter(new ProductAdapter(getActivity(), productEntities)));

            // attach dynamic view to list of dynamic constraint layouts
            shopConstraints.add(dynamicViewBinding.getRoot());
        }

        // remove all previous views from the linear layout
        binding.dynamicLinearLayout.removeAllViews();

        // add the new views
        for (ConstraintLayout shopConstraint : shopConstraints) {
            binding.dynamicLinearLayout.addView(shopConstraint);
        }
    });

Решение MotionLayout

MotionLayout сложнее, так как требует специальной обработки из-за наличия нескольких ConstraintSet. Если вы используете решение, предназначенное для ConstraintLayout, по какой-то причине оно будет размещать ваши представления где-то в правом нижнем углу. Правильный способ добавить представление в MotionLayout — использоватьupdateStateвместоapplyTo:

      /* ...up until this point the steps are the same: create constraint set,
set view's id, clone, connect, etc. */

layout.updateState(R.id.start, set)

Чтобы сделать ваше представление видимым в других определенных ConstraintSets (в данном случаеendConstraintSet, он жеR.id.end), воспользуйтесь специальнымcloneConstraintSetметод и установите размер представления:

      // Clone the constraint set using MotionLayout's `cloneConstraintSet`
val setEnd = layout.cloneConstraintSet(R.id.end)
// Set the view's width and height
setEnd.constrainWidth(view.id, ConstraintLayout.LayoutParams.WRAP_CONTENT)
setEnd.constrainHeight(view.id, ConstraintLayout.LayoutParams.WRAP_CONTENT)
// Update state
layout.updateState(R.id.end, setEnd)

Добавление ограничений к программно созданному представлению также возможно без создания нового набора ограничений, а просто путем установки параметров макета представления. Вот пример на Котлине и Java:

      val view = ImageView(this)
view.layoutParams = ConstraintLayout.LayoutParams(
    ConstraintLayout.LayoutParams.WRAP_CONTENT,
    ConstraintLayout.LayoutParams.WRAP_CONTENT
).apply {
    topToTop = R.id.mainConstraint
    topMargin = 60
}

val layout = findViewById<ConstraintLayout>(R.id.mainConstraint)
layout.addView(view)
      ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(
    ConstraintLayout.LayoutParams.WRAP_CONTENT,
    ConstraintLayout.LayoutParams.WRAP_CONTENT
);
layoutParams.topToTop = R.id.mainConstraint;
layoutParams.topMargin = 60;

ImageView view = new ImageView(this);
view.setLayoutParams(layoutParams);

ConstraintLayout layout = findViewById(R.id.mainConstraint);
layout.addView(view);

Параметры макета также легко изменить после того, как представление уже добавлено в макет. Опять пример на Котлине и Java:

      view.layoutParams = (tv.layoutParams as ConstraintLayout.LayoutParams).apply {
    topMargin = 120
}
      ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) view.getLayoutParams();
layoutParams.topMargin = 120;
view.setLayoutParams(layoutParams);

Только не забудьте переназначить вид измененные параметры макета. В противном случае изменения не будут применены.

Другие вопросы по тегам