Como dimensionar uma visualização do Android com base nas dimensões de seus pais


104

Como posso dimensionar uma visualização com base no tamanho de seu layout pai. Por exemplo, tenho um RelativeLayoutque preenche a tela inteira e quero que uma visualização secundária, digamos ImageView, ocupe toda a altura e metade da largura.

Eu tentei substituir todos em onMeasure, onLayout, onSizeChanged, etc e eu não poderia obtê-lo para o trabalho ....


É possível fazer isso usando ConstraintLayout, verifique isto: stackoverflow.com/questions/37318228/…
Ali Nem

Respostas:


124

Eu não sei se alguém ainda está lendo este tópico ou não, mas a solução de Jeff só vai te levar até a metade do caminho (meio que literalmente). O que seu onMeasure fará é exibir metade da imagem na metade do pai. O problema é que chamar super.onMeasure antes de setMeasuredDimensionmedirá todos os filhos na exibição com base no tamanho original, depois apenas cortará a exibição pela metade quando for setMeasuredDimensionredimensionada.

Em vez disso, você precisa chamar setMeasuredDimension(conforme necessário para uma substituição onMeasure) e fornecer um novo LayoutParamspara sua visualização e, em seguida , ligar super.onMeasure. Lembre-se, você LayoutParamsé derivado do tipo-pai da sua visão, não do tipo da sua visão.

@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);
}

Acredito que a única vez que você terá problemas com o pai LayoutParamsé se ele for um AbsoluteLayout(o que está obsoleto, mas às vezes é útil).


12
Na minha experiência, isso raramente funciona, esp. quando fill / match_parent ou pesos são chamados. Melhor chamar measureChildren após setMeasuredDimension (com, por exemplo, MeasureSpec.makeMeasureSpec (newWidth, MeasureSpec.AT_MOST)).
M. Schenk

1
Isso MeasureSpec.getSize(widthMeasureSpec)realmente dá a largura correta do pai? Para mim, ele apenas retorna larguras aleatórias que são muito próximas da largura real, mas não exatas.
Konsumierer

1
@ M.Schenk tem. Eu tenho um VideoView dentro de um layout ponderado. Preciso que a largura seja uma porcentagem do tamanho da tela e preciso manter a proporção. Isso alonga o vídeo, então wrap_contentnão o fará. A resposta de Vic não funciona para este cenário, mas o comentário de @M.Schenk sim. Ele também salva uma passagem de layout extra. Você também deve postar como uma resposta para visibilidade.
dokkaebi

7
A chamada para super.onMeasure()simplesmente substituir e anular os efeitos da chamada anterior para this.setMeasuredDimension()?
Spinner

1
Também estou curioso, como @Spinner mencionou, por que a chamada para super.onMeasure()não substitui os cálculos anteriores.
jwir3

39

Você poderia resolver isso criando uma Visualização personalizada e substituindo o método onMeasure (). Se você sempre usar "fill_parent" para layout_width em seu xml, o parâmetro widthMeasureSpec que é passado para o método onMeasusre () deve conter a largura do pai.

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);
    }
}   

Seu XML seria mais ou menos assim:

<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 => porque parentWidth / 2 ??
Saeed-rz

6
@Leon_SFS: A pergunta é: "altura total e metade da largura"
EdgarT

28

Descobri que é melhor não mexer em definir as dimensões medidas você mesmo. Na verdade, há um pouco de negociação acontecendo entre as visualizações pai e filho e você não quer reescrever todo o código .

O que você pode fazer, entretanto, é modificar as measureSpecs e, em seguida, chamar super com elas. Sua visualização nunca saberá que está recebendo uma mensagem modificada de seu pai e cuidará de tudo para você:

@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

Ao definir um layout e visualização em XML, você pode especificar a largura e altura do layout de uma visualização para envolver o conteúdo ou preencher o pai. Ocupar metade da área é um pouco mais difícil, mas se você tivesse algo que quisesse na outra metade, poderia fazer algo como o seguinte.

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

Dar a duas coisas o mesmo peso significa que elas se esticarão para ocupar a mesma proporção da tela. Para obter mais informações sobre layouts, consulte os dev docs.


Eu não recomendo isso, mas ... como um hack, se você usar a abordagem acima, pode transformar uma dessas visualizações em "fictícia" e definir android: visibility = "invisible" para preencher metade da área
RickNotFred

Qual é o propósito de usar apenas metade da área? Você está planejando exibir alguma coisa na outra metade? O que você faz com a área restante informará a melhor abordagem
RickNotFred

isso definitivamente deve ser o # 1 - se você pode fazer isso em seu xml, por que fazer em seu java?
fandang de

9

Eu acredito que a abordagem XML da Mayras pode vir bem. No entanto, é possível torná-lo mais preciso, com uma visualização apenas, definindo o weightSum. Eu não chamaria mais isso de hack, mas, em minha opinião, a abordagem mais direta:

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

Assim, você pode usar qualquer peso, 0,6 por exemplo (e centralização) é o peso que eu gosto de usar para botões.


3

Tente isto

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

então você pode usar LinearLayout.LayoutParams para definir os parâmetros do chileView

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

2

Agora você pode usar PercentRelativeLayout. Estrondo! Problema resolvido.


2
PercentRelativeLayout foi descontinuado no nível de API 26.0.0-beta1.
dzikovskyy

O que quer dizer "No nível da API"? Você quer dizer o número da versão da biblioteca? PRL está na biblioteca de suporte, não tem nível de API.
androidguy

Por nível de API, quero dizer versão do Android. Informações aqui developer.android.com/reference/android/support/percent/… . Mais informações sobre o nível de API aqui developer.android.com/guide/topics/manifest/…
dzikovskyy

Você ainda pode usar esta classe, a menos que por algum motivo você precise da versão 26.0.0 ou superior.
androidguy

1

Roman, se você quiser fazer seu layout em código Java (descendente do ViewGroup), é possível. O truque é implementar os métodos onMeasure e onLayout. O onMeasure é chamado primeiro e você precisa "medir" a subvisualização (dimensionando-a efetivamente para o valor desejado) lá. Você precisa dimensioná-lo novamente na chamada onLayout. Se você não conseguir fazer essa sequência ou não chamar setMeasuredDimension () no final do código onMeasure, não obterá resultados. Por que isso é projetado de forma tão complicada e frágil está além de mim.


1

Se o outro lado estiver vazio. Eu acho que a maneira mais simples seria adicionar uma visualização vazia (por exemplo, um layout linear) e definir as larguras de ambas as visualizações como fill_parent e seus pesos como 1.

Isso funciona em um LinearLayout ...


0

É algo assim:

<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

Venho lutando com essa questão há muito tempo, quero mudar a largura da gaveta do meu DrawerLayout, que é o segundo filho de seu pai. Recentemente descobri, mas não sei se existe uma ideia melhor.

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
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.