Як розмістити представлення Android на основі розмірів його батьків


104

Як я можу розмір представлення на основі розміру його батьківського макета. Наприклад, у мене є такий, RelativeLayoutщо заповнює весь екран, і я хочу, щоб дитячий перегляд, скажімо ImageView, займав всю висоту і 1/2 ширини?

Я намагався перекриваючи всі на onMeasure, onLayout, onSizeChangedі т.д. , і я не міг змусити його працювати ....


Це можна зробити за допомогою ConstraintLayout, перевірте це: stackoverflow.com/questions/37318228/…
Алі Нем

Відповіді:


124

Я не знаю, чи все ще хтось читає цю тему чи ні, але рішення Джеффа отримає вас лише на півдорозі (начебто буквально). Що він буде робити onMeasure - це показати половину зображення в половині батьківського. Проблема полягає в тому, що зателефонувавши до super.onMeasure перед setMeasuredDimensionзаповітом, буде виміряти всіх дітей у представленні даних на основі вихідного розміру, а потім просто скоротити подання навпіл, коли setMeasuredDimensionрозмір буде змінено.

Натомість вам потрібно зателефонувати setMeasuredDimension(як це потрібно для переопрацювання onMeasure) та надати нове LayoutParamsдля перегляду, а потім зателефонувати super.onMeasure. Пам'ятайте, що LayoutParamsвони походять від типу батьківського перегляду, а не типу вашого перегляду.

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
   int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
   int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
   this.setMeasuredDimension(parentWidth/2, parentHeight);
   this.setLayoutParams(new *ParentLayoutType*.LayoutParams(parentWidth/2,parentHeight));
   super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

Я вважаю, що єдиний раз, коли у вас виникнуть проблеми з батьком, LayoutParamsце якщо батько є AbsoluteLayout(що застаріло, але все ще іноді корисно).


12
На мій досвід, це рідко працює, особливо. коли викликається заповнення / match_parent або ваги. Краще викликати ukreChildren після setMeasuredDimension (наприклад, з MeasureSpec.makeMeasureSpec (newWidth, MeasureSpec.AT_MOST)).
М. Шенк

1
Чи MeasureSpec.getSize(widthMeasureSpec)справді відповідає правильна ширина батьків? Для мене це просто повертає випадкові ширини, які досить близькі до фактичної ширини, але не точні.
Konsumierer

1
@ M.Schenk має. У мене є VideoView у зваженому макеті. Мені потрібно, щоб ширина була відсотковою від розміру екрана, і мені потрібно підтримувати співвідношення сторін. Це розтягує відео, тому wrap_contentне буде робити. Відповідь Віка не працює за цим сценарієм, але коментар @ M.Schenk робить. Це також економить додатковий пропуск макета. Ви також повинні розмістити його як відповідь на видимість.
dokkaebi

7
Чи не буде заклик super.onMeasure()просто переосмислити та звести нанівець наслідки попереднього дзвінка this.setMeasuredDimension()?
Спінер

1
Мені також цікаво, як згадував @Spinner, чому заклик super.onMeasure()не перекриває попередні обчислення.
jwir3

39

Ви можете вирішити це, створивши нестандартний перегляд і замінивши метод onMeasure (). Якщо ви завжди використовуєте "fill_parent" для layout_width у вашому xml, то параметр widthMeasureSpec, який передається методу onMeasusre (), повинен містити ширину батьківського.

public class MyCustomView extends TextView {

    public MyCustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
        this.setMeasuredDimension(parentWidth / 2, parentHeight);
    }
}   

Ваш XML буде виглядати приблизно так:

<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <view
        class="com.company.MyCustomView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

2
@jeff => чому parentWidth / 2 ??
Saeed-rz

6
@Leon_SFS: Питання задається так: "вся висота і 1/2 ширина"
EdgarT

28

Я виявив, що краще не возитися з встановленням виміряних розмірів самостійно. Насправді трохи переговорів триває між переглядами батьків і дитини, і ви не хочете переписувати весь цей код .

Однак ви можете змінити мераSpecs, а потім зателефонувати супер. Ваша думка ніколи не дізнається, що вона отримує змінене повідомлення від свого батька і піклується про все за вас:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
    int myWidth = (int) (parentHeight * 0.5);
    super.onMeasure(MeasureSpec.makeMeasureSpec(myWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
}

16

Коли ви визначаєте макет і перегляд на XML, ви можете вказати ширину макета та висоту перегляду для того, щоб бути вмістом загортання або батьківським заповненням. Зайняти половину площі трохи складніше, але якщо у вас було щось, що ви хотіли на другій половині, ви могли б зробити щось на кшталт наступного.

   <LinearLayout android:layout_width="fill_parent"
                android:layout_height="fill_parent">
        <ImageView android:layout_height="fill_parent"
                 android:layout_width="0dp"
                 android:layout_weight="1"/>
        <ImageView android:layout_height="fill_parent"
                 android:layout_width="0dp"
                 android:layout_weight="1"/>
   </LinearLayout>

Якщо дві речі мають однакову вагу, це означає, що вони розтягнуться, щоб зайняти однакову долю екрана. Для отримання додаткової інформації про макети дивіться документи розробників.


Я не рекомендую цього, але ... як хак, якщо ви використовуєте вищезазначений підхід, ви можете зробити один із цих поглядів "манекеном" і встановити андроїд: visibility = "невидимий", щоб заповнити половину площі
RickNotFred

Яка мета використання лише половини площі? Чи плануєте ви щось показувати в другій половині? Що ви робите з зоною, що залишилася, повідомить про найкращий підхід
RickNotFred

це безумовно має бути №1 - якщо ви можете це зробити у своєму xml, навіщо це робити у вашій Java?
фанданг

9

Я вважаю, що Mayras XML-підхід може бути акуратним. Однак можна зробити це більш точним, з одним видом, лише встановивши weightSum. Я б більше не називав це злом, але, на мою думку, найбільш прямим підходом:

<LinearLayout android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:weightSum="1">
    <ImageView android:layout_height="fill_parent"
               android:layout_width="0dp"
               android:layout_weight="0.5"/>
</LinearLayout>

У такий спосіб ви можете використовувати будь-яку вагу, наприклад, 0,6 (і центрування) - це вага, який я люблю використовувати для кнопок.


3

Спробуйте це

int parentWidth = ((parentViewType)childView.getParent()).getWidth();
int parentHeight = ((parentViewType)childView.getParent()).getHeight();

тоді ви можете використовувати LinearLayout.LayoutParams для встановлення параметрів chileView

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(childWidth,childLength);
childView.setLayoutParams(params);

2

Тепер ви можете використовувати PercentRelativeLayout. Бум! Проблема вирішена.


2
PercentRelativeLayout був застарілий на рівні API 26.0.0-beta1.
dzikovskyy

Що ви маєте на увазі "На рівні API"? Ви маєте на увазі номер версії бібліотеки? PRL знаходиться в бібліотеці підтримки, не має рівня API.
androidguy

Під рівнем API я маю на увазі версію Android. Інформація тут developer.android.com/reference/android/support/percent / ... . Більше інформації про рівень API тут developer.android.com/guide/topics/manifest/…
dzikovskyy

Ви все ще можете використовувати цей клас, якщо вам з якоїсь причини не потрібна версія 26.0.0 або новіша.
androidguy

1

Романе, якщо ви хочете зробити свій макет в коді Java (ViewGroup descendant), можливо. Хитрість полягає в тому, що вам доведеться реалізувати і методи onMeasure, і onLayout. Спочатку onMeasure викликається спочатку, і вам потрібно там "виміряти" підперегляд (фактично встановивши його до потрібного значення). Вам потрібно його розмір ще раз під час виклику onLayout. Якщо ви не виконаєте цю послідовність або не подзвоните setMeasuredDimension () наприкінці коду onMeasure, результати не отримають. Чому це сконструйовано таким складним і тендітним способом, поза мною.


1

Якщо інша сторона порожня. Я думаю, найпростішим способом було б додати порожній вигляд (наприклад, лінійний макет), а потім встановити ширину обох поглядів на fill_parent і обидва їх ваги до 1.

Це працює в LinearLayout ...


0

Це щось подібне:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.company.myapp.ActivityOrFragment"
    android:id="@+id/activity_or_fragment">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/linearLayoutFirst"
    android:weightSum="100">   <!-- Overall weights sum of children elements -->

    <Spinner
        android:layout_width="0dp"   <!-- It's 0dp because is determined by android:layout_weight -->
        android:layout_weight="50"
        android:layout_height="wrap_content"
        android:id="@+id/spinner" />

</LinearLayout>


<LinearLayout
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:layout_below="@+id/linearLayoutFirst"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:id="@+id/linearLayoutSecond">

    <EditText
        android:layout_height="wrap_content"
        android:layout_width="0dp"
        android:layout_weight="75"
        android:inputType="numberDecimal"
        android:id="@+id/input" />

    <TextView
        android:layout_height="wrap_content"
        android:layout_width="0dp"
        android:layout_weight="25"
        android:id="@+id/result"/>

</LinearLayout>
</RelativeLayout>

0

Я давно бореться з цим питанням, хочу змінити ширину ящика моєї DrawerLayout, яка є другою дитиною її батька. Нещодавно я зрозумів це, але не знаю, чи є краща ідея.

override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
    super.onLayout(changed, l, t, r, b)
    (getChildAt(1).layoutParams as LayoutParams).width = measuredWidth/2
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.