Как изменить размер представления 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
По моему опыту, это редко срабатывает, особенно. когда вызываются fill / match_parent или веса. Лучше вызывать measureChildren после setMeasuredDimension (например, с помощью MeasureSpec.makeMeasureSpec (newWidth, MeasureSpec.AT_MOST)).
M. Schenk
1
MeasureSpec.getSize(widthMeasureSpec)Действительно ли дает правильную родительскую ширину? Для меня он просто возвращает случайную ширину, которая довольно близка к фактической, но не точна.
Konsumierer
1
@ M.Schenk это есть. У меня есть VideoView внутри взвешенного макета. Мне нужна ширина в процентах от размера экрана, и мне нужно поддерживать соотношение сторон. Это растягивает видео, поэтому wrap_contentне годится. Ответ Вика не работает для этого сценария, но комментарий @M.Schenk работает. Это также экономит дополнительный проход макета. Вы также должны опубликовать его в качестве ответа для наглядности.
dokkaebi
7
Разве призыв super.onMeasure()просто не переопределит и не сведет на нет эффекты предыдущего вызова this.setMeasuredDimension()?
Spinner
1
Мне также любопытно, как упоминал @Spinner, почему вызов super.onMeasure()не отменяет более ранние вычисления.
jwir3
39

Вы можете решить эту проблему, создав пользовательский View и переопределив метод 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

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

Однако вы можете изменить параметры measureSpec, а затем вызвать super с ними. Ваше представление никогда не узнает, что оно получает измененное сообщение от своего родителя, и позаботится обо всем за вас:

@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>

Придание двум вещам одинакового веса означает, что они будут растягиваться, занимая одинаковую часть экрана. Дополнительные сведения о макетах см. В документации для разработчиков.

Шерил Саймон
источник
Я не рекомендую это, но ... в качестве хака, если вы все же воспользуетесь описанным выше подходом, вы можете сделать одно из этих представлений «пустышкой» и установить android: visibility = «invisible» на половину площади
RickNotFred
С какой целью использовать только половину площади? Планируете ли вы показать что-нибудь в другой половине? То, что вы сделаете с оставшейся областью, будет способствовать лучшему подходу
RickNotFred
это определенно должно быть №1 - если вы можете сделать это в своем xml, зачем делать это в своем java?
fandang
9

Я считаю, что XML-подход Mayras может пригодиться . Однако можно сделать его более точным, только с одним представлением, установив 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 (и центрирование) - это вес, который я люблю использовать для кнопок.

Pinkwerther
источник
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), то это возможно. Хитрость в том, что вам нужно реализовать как методы onMeasure, так и onLayout. Сначала вызывается onMeasure, и вам нужно там «измерить» подпредставление (по сути, установить его размер до желаемого значения). Вам нужно снова изменить его размер в вызове onLayout. Если вы не выполните эту последовательность или не вызовете setMeasuredDimension () в конце кода onMeasure, вы не получите результатов. Почему это так сложно и хрупко, я не понимаю.

Павел Лахода
источник
1

Если другая сторона пуста. Я предполагаю, что самым простым способом было бы добавить пустое представление (например, линейный макет), затем установить ширину обоих представлений на fill_parent и их веса на 1.

Это работает в LinearLayout ...

jpm
источник
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>
Марио
источник
-1

Я долго боролся с этим вопросом, я хочу изменить ширину ящика 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
}
Иоанна 6
источник