Пользовательская группа ViewGroup с дочерними элементами, вставленными в определенное место
В моем приложении для Android есть несколько операций с одинаковой базовой структурой, и я пытаюсь сделать макеты СУХИЕМИ. Дублированный код выглядит следующим образом. Он содержит прокручиваемую область с нижним колонтитулом, имеющим ссылки "Назад" и "Панель инструментов". Есть также FrameLayout, используемый для применения градиента поверх прокручиваемой области.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<ScrollView
android:layout_width="match_parent"
android:layout_height="689px">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- THE REAL PAGE CONTENT GOES HERE -->
</LinearLayout>
</ScrollView>
<ImageView
android:src="@drawable/GradientBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" />
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="50px"
android:background="?attr/primaryAccentColor">
<Button
android:layout_width="wrap_content"
android:layout_height="26px"
android:layout_gravity="center_vertical"
local:MvxBind="Click GoBackCommand" />
<Button
android:layout_width="wrap_content"
android:layout_height="26px"
local:MvxBind="Click ShowDashboardHomeCommand" />
</FrameLayout>
</LinearLayout>
Я считаю, что для дедупликации моей Деятельности мне нужно создать пользовательскую ViewGroup, унаследованную от LinearLayout. В этом коде загрузите вышеуказанное содержимое из файла XML. Где я потерян, так это как заставить дочерний контент в Деятельности загрузить в нужное место. Например, скажем, моя активность теперь содержит:
<com.myapp.ScrollableVerticalLayoutWithDashboard
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- THE REAL PAGE CONTENT GOES HERE -->
<TextView android:text"blah blah blah" />
</com.myapp.ScrollableVerticalLayoutWithDashboard>
Теперь, как я могу заставить "бла-бла-бла" появляться в правильном месте? Я вполне уверен, что если бы я сделал это, я бы в итоге получил "бла-бла-бла" вверху или внизу страницы, а не в середине ScrollView, как хотелось бы.
Я использую API 21 / v5.0+. Технически я делаю все это с Xamarin, но надеюсь, что это не имеет отношения к ответу?
РЕДАКТИРОВАТЬ: пример того, как будет выглядеть результат, это. Нижний колонтитул и градиент являются частью пользовательской ViewGroup, но остальное будет содержимым в пользовательской ViewGroup.
1 ответ
Я не знаю Xamarin, так что это нативное решение для Android, но его легко перевести.
Я думаю, что мне нужно сделать, это создать пользовательскую ViewGroup, унаследованную от LinearLayout.
Да, вы можете расширить класс LinearLayout.
Где я потерян, так это как заставить дочерний контент в Деятельности загрузить в нужное место.
В вашей пользовательской реализации вам нужно обрабатывать детей вручную. В конструкторе этого пользовательского класса надуйте макет вручную:
private LinearLayout mDecor;
public ScrollableVerticalLayoutWithDashboard(Context context, AttributeSet attrs) {
super(context, attrs);
// inflate the layout directly, this will pass through our addView method
LayoutInflater.from(context).inflate(R.layout.your_layout, this);
}
а затем переопределите метод addView()(который ViewGroup использует для добавления своих дочерних элементов) для обработки различных типов представлений:
private LinearLayout mDecor;
public void addView(View child, int index, ViewGroup.LayoutParams params) {
// R.id.decor will be an id set on the root LinearLayout in the layout so we can know
// the type of view
if (child.getId() != R.id.decor) {
// this isn't the root of our inflated view so it must be the actual content, like
// the bla bla TextView
// R.id.content will be an id set on the LinearLayout inside the ScrollView where
// the content will sit
((LinearLayout) mDecor.findViewById(R.id.content)).addView(child, params);
return;
}
mDecor = (LinearLayout) child; // keep a reference to this static part of the view
super.addView(child, index, params); // add the decor view, the actual content will
// not be added here
}
В Xamarin вы ищете https://developer.xamarin.com/api/member/Android.Views.ViewGroup.AddView/p/Android.Views.View/System.Int32/Android.Views.ViewGroup+LayoutParams/ метод для переопределения. Имейте в виду, что это простая реализация.
РЕДАКТИРОВАТЬ: Вместо того, чтобы помещать LinearLayout в LinearLayout, вы можете просто использовать тег "слияния". Вот окончательный макет, который вы хотите:
<?xml version="1.0" encoding="utf-8" ?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto">
<FrameLayout
android:id="@+id/svfFrame1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<ScrollView
android:layout_width="match_parent"
android:layout_height="689px">
<LinearLayout
android:id="@+id/svfContentLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="23px" />
</ScrollView>
<ImageView
android:src="@drawable/GradientBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" />
</FrameLayout>
<FrameLayout
android:id="@+id/svfFrame2"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="50px"
android:background="?attr/primaryAccentColor">
<Button
android:id="@+id/FooterBackButton"
android:layout_width="wrap_content"
android:layout_height="26px"
android:layout_gravity="center_vertical"
android:layout_marginLeft="24px" />
<Button
android:id="@+id/FooterDashboardButton"
android:layout_width="wrap_content"
android:layout_height="26px"
android:layout_gravity="center_vertical|right"
android:layout_marginRight="24px" />
</FrameLayout>
</merge>
И вот последний рабочий вид C# для Xamarin на основе этого макета:
public class ScrollableVerticalLayoutWithDashboard: LinearLayout
{
public ScrollableVerticalLayoutWithDashboard(Context context, IAttributeSet attrs) : base(context, attrs)
{
LayoutInflater.From(context).Inflate(Resource.Layout.ScrollableVerticalFooter, this);
base.Orientation = Orientation.Vertical;
}
public override void AddView(View child, int index, ViewGroup.LayoutParams @params)
{
// Check to see if the child is either of the two direct children from the layout
if (child.Id == Resource.Id.svfFrame1 || child.Id == Resource.Id.svfFrame2)
{
// This is one of our true direct children from our own layout. Add it "normally" using the base class.
base.AddView(child, index, @params);
}
else
{
// This is content coming from the parent layout, not our own inflated layout. It
// must be the actual content, like the bla bla TextView. Add it at the appropriate location.
((LinearLayout)this.FindViewById(Resource.Id.svfContentLayout)).AddView(child, @params);
}
}
}