Как программно добавить представления и ограничения в 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.
- Создайте файл fragment_home.xml
- Добавьте LinearLayout в конец fragment_home.xml
- Создайте файл dynamic_view.xml, который вы надуете.
- Получить элементы из ViewModel / Array или любого другого доступного источника данных
- Надуть и добавить элементы
В этом случае я создаю текстовое представление (заголовок) с помощью 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 (в данном случаеend
ConstraintSet, он же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);
Только не забудьте переназначить вид измененные параметры макета. В противном случае изменения не будут применены.