Cara mengukur tampilan Android berdasarkan dimensi induknya

104

Bagaimana cara mengukur tampilan berdasarkan ukuran tata letak induknya. Misalnya saya memiliki a RelativeLayoutyang memenuhi layar penuh, dan saya ingin tampilan anak, katakanlah ImageView, untuk memenuhi seluruh tinggi, dan 1/2 lebarnya?

Saya sudah mencoba mengesampingkan semua pada onMeasure, onLayout, onSizeChanged, dll dan saya tidak bisa mendapatkannya untuk bekerja ....

Mitch
sumber
Dimungkinkan untuk melakukannya menggunakan ConstraintLayout, periksa ini: stackoverflow.com/questions/37318228/…
Ali Nem

Jawaban:

124

Saya tidak tahu apakah ada yang masih membaca utas ini atau tidak, tetapi solusi Jeff hanya akan membuat Anda setengah jalan (agak harfiah). Apa yang akan dilakukan onMeasure-nya adalah menampilkan setengah gambar di setengah induknya. Masalahnya adalah memanggil super.onMeasure sebelum setMeasuredDimensionakan mengukur semua anak dalam tampilan berdasarkan ukuran aslinya, lalu memotong tampilan menjadi dua saat setMeasuredDimensionmengubah ukurannya.

Sebagai gantinya, Anda perlu memanggil setMeasuredDimension(seperti yang diperlukan untuk onMeasure override) dan memberikan tampilan baru LayoutParamsuntuk tampilan Anda, lalu panggil super.onMeasure. Ingat, Anda LayoutParamsberasal dari tipe induk tampilan Anda, bukan tipe tampilan Anda.

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

Saya percaya satu-satunya saat Anda akan memiliki masalah dengan orang tua LayoutParamsadalah jika orang tua adalah AbsoluteLayout(yang usang tetapi terkadang masih berguna).

Vic
sumber
12
Dalam pengalaman saya, ini jarang berhasil, khususnya. ketika fill / match_parent atau bobot dipanggil. Lebih baik panggil measureChildren setelah setMeasuredDimension (dengan misalnya MeasureSpec.makeMeasureSpec (newWidth, MeasureSpec.AT_MOST)).
M. Schenk
1
Apakah MeasureSpec.getSize(widthMeasureSpec)benar-benar memberikan lebar induk yang benar? Bagi saya itu hanya mengembalikan lebar acak yang cukup dekat dengan lebar sebenarnya, tetapi tidak tepat.
Konsumierer
1
@ M.Schen memilikinya. Saya memiliki VideoView di dalam tata letak berbobot. Saya perlu lebar menjadi persentase dari ukuran layar, dan saya harus menjaga rasio aspek. Ini memperpanjang video, jadi wrap_contenttidak akan berhasil. Jawaban Vic tidak berfungsi untuk skenario ini, tetapi komentar @ M.Schenk berhasil. Ini juga menghemat izin tata letak ekstra. Anda juga harus mempostingnya sebagai jawaban untuk visibilitas.
dokkaebi
7
Akankah panggilan untuk super.onMeasure()hanya menimpa dan meniadakan efek dari panggilan sebelumnya ke this.setMeasuredDimension()?
Spinner
1
Saya juga penasaran, seperti yang disebutkan @Spinner, mengapa panggilan ke super.onMeasure()tidak menggantikan penghitungan sebelumnya.
jwir3
39

Anda bisa mengatasi ini dengan membuat Tampilan kustom dan mengganti metode onMeasure (). Jika Anda selalu menggunakan "fill_parent" untuk layout_width di xml Anda, parameter widthMeasureSpec yang diteruskan ke metode onMeasusre () harus berisi lebar induknya.

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 Anda akan terlihat seperti ini:

<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>
Jeff
sumber
2
@jeff => kenapa parentWidth / 2 ??
Saeed-rz
6
@Leon_SFS: Pertanyaannya adalah: "tinggi keseluruhan, dan 1/2 lebar"
EdgarT
28

Saya telah menemukan bahwa yang terbaik adalah tidak mengotak-atik pengaturan sendiri dimensi yang diukur. Sebenarnya ada sedikit negosiasi yang terjadi antara pandangan orang tua dan anak dan Anda tidak ingin menulis ulang semua kode itu .

Namun, yang dapat Anda lakukan adalah memodifikasi measureSpecs, lalu memanggil super dengannya. Pandangan Anda tidak akan pernah tahu bahwa itu mendapatkan pesan yang dimodifikasi dari induknya dan akan mengurus semuanya untuk Anda:

@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);
}
Phil Kulak
sumber
16

Saat Anda menentukan tata letak dan tampilan di XML, Anda bisa menentukan lebar dan tinggi tata letak tampilan untuk membungkus konten, atau mengisi induk. Mengambil separuh area agak sulit, tetapi jika Anda memiliki sesuatu yang Anda inginkan di separuh lainnya, Anda dapat melakukan hal seperti berikut.

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

Memberi dua benda bobot yang sama berarti mereka akan meregang untuk mengambil proporsi layar yang sama. Untuk info lebih lanjut tentang tata letak, lihat dokumen dev.

Cheryl Simon
sumber
Saya tidak merekomendasikan ini, tetapi ... sebagai peretasan, jika Anda menggunakan pendekatan di atas, Anda dapat membuat salah satu tampilan tersebut menjadi "dummy" dan menyetel android: visibility = "invisible" untuk mengisi setengah area
RickNotFred
Apa tujuan hanya menggunakan setengah area? Apakah Anda berencana menampilkan sesuatu di bagian lainnya? Apa yang Anda lakukan dengan area yang tersisa akan menginformasikan pendekatan terbaik
RickNotFred
ini pasti harus # 1 - jika Anda dapat melakukannya di xml Anda, mengapa melakukannya di java Anda?
fandang
9

Saya percaya bahwa pendekatan XML Mayras bisa datang dengan rapi. Namun dimungkinkan untuk membuatnya lebih akurat, dengan satu tampilan hanya dengan mengatur weightSum. Saya tidak akan menyebut ini peretasan lagi tetapi menurut saya pendekatan yang paling mudah:

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

Seperti ini, Anda dapat menggunakan bobot berapa pun, misalnya 0,6 (dan pemusatan) adalah bobot yang ingin saya gunakan untuk tombol.

pinkwerther
sumber
3

Coba ini

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

lalu Anda bisa menggunakan LinearLayout.LayoutParams untuk menyetel parameter chileView

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(childWidth,childLength);
childView.setLayoutParams(params);
Abhishek Gupta
sumber
2

Anda sekarang dapat menggunakan PercentRelativeLayout. Ledakan! Masalah terpecahkan.

androidguy
sumber
2
PercentRelativeLayout sudah tidak digunakan lagi di API level 26.0.0-beta1.
dzikovskyy
Apa maksud Anda "Dalam level API"? Apakah yang Anda maksud nomor versi perpustakaan? PRL ada di pustaka dukungan, tidak memiliki level API.
androidguy
Yang saya maksud dengan API level adalah versi Android. Info dari sini developer.android.com/reference/android/support/percent/… . Info lebih lanjut tentang API level di sini developer.android.com/guide/topics/manifest/…
dzikovskyy
Anda masih dapat menggunakan kelas ini kecuali karena alasan tertentu Anda memerlukan versi 26.0.0 atau lebih tinggi.
androidguy
1

Roman, jika Anda ingin melakukan layout Anda dalam kode Java (keturunan ViewGroup), itu mungkin. Triknya adalah Anda harus mengimplementasikan metode onMeasure dan onLayout. OnMeasure dipanggil lebih dulu dan Anda perlu "mengukur" subview (secara efektif menyesuaikan ukurannya ke nilai yang diinginkan) di sana. Anda perlu mengukurnya lagi di panggilan onLayout. Jika Anda gagal melakukan urutan ini atau gagal memanggil setMeasuredDimension () di akhir kode onMeasure, Anda tidak akan mendapatkan hasil. Mengapa ini dirancang sedemikian rumit dan rapuh berada di luar jangkauan saya.

Pavel Lahoda
sumber
1

Jika sisi lainnya kosong. Saya rasa cara termudah adalah dengan menambahkan tampilan kosong (mis. Tata letak linier) lalu menyetel lebar tampilan ke fill_parent dan keduanya bobotnya ke 1.

Ini bekerja di LinearLayout ...

jpm
sumber
0

Sesuatu seperti ini:

<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>
Mario
sumber
-1

Saya telah bergumul dengan pertanyaan ini sejak lama, saya ingin mengubah lebar laci DrawerLayout saya, yang merupakan anak kedua dari orang tuanya. Baru-baru ini saya menemukan jawabannya, tetapi tidak tahu apakah ada ide yang lebih baik.

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
}
Yohanes 6
sumber