Cách định kích thước chế độ xem Android dựa trên kích thước gốc của nó


104

Làm cách nào để định kích thước một dạng xem dựa trên kích thước của bố cục mẹ của nó. Ví dụ: tôi có một RelativeLayoutcái lấp đầy toàn màn hình và tôi muốn một chế độ xem trẻ em, giả sử ImageView, để chiếm toàn bộ chiều cao và 1/2 chiều rộng?

Tôi đã thử tất cả các trọng trên onMeasure, onLayout, onSizeChanged, vv và tôi không thể làm cho nó làm việc ....


Có thể làm điều đó bằng cách sử dụng ConstraintLayout, hãy kiểm tra điều này: stackoverflow.com/questions/37318228/…
Ali Nem

Câu trả lời:


124

Tôi không biết liệu có ai vẫn đang đọc chủ đề này hay không, nhưng giải pháp của Jeff sẽ chỉ giúp bạn đạt được nửa chặng đường (nghĩa đen). Những gì onMeasure của anh ấy sẽ làm là hiển thị một nửa hình ảnh trong một nửa hình ảnh cha mẹ. Vấn đề là việc gọi super.onMeasure trước setMeasuredDimensionsẽ đo lường tất cả các phần tử con trong chế độ xem dựa trên kích thước ban đầu, sau đó chỉ cần cắt chế độ xem làm đôi khi setMeasuredDimensionthay đổi kích thước.

Thay vào đó, bạn cần gọi setMeasuredDimension(theo yêu cầu đối với ghi đè onMeasure) cung cấp thông tin mới LayoutParamscho chế độ xem của bạn, sau đó gọi super.onMeasure. Hãy nhớ rằng, của bạn LayoutParamscó nguồn gốc từ loại chính của chế độ xem của bạn, không phải loại của chế độ xem của bạn.

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

Tôi tin rằng lần duy nhất bạn gặp vấn đề với cha mẹ LayoutParamslà khi cha mẹ là một AbsoluteLayout(không được dùng nữa nhưng đôi khi vẫn hữu ích).


12
Theo kinh nghiệm của tôi, điều này hiếm khi hoạt động, đặc biệt. khi điền / match_parent hoặc weights được gọi. Tốt hơn nên gọi ra MeasureChildren sau setMeasuredDimension (ví dụ: MeasureSpec.makeMeasureSpec (newWidth, MeasureSpec.AT_MOST)).
M. Schenk

1
MeasureSpec.getSize(widthMeasureSpec)thực sự cung cấp đúng chiều rộng của cha mẹ không? Đối với tôi, nó chỉ trả về chiều rộng ngẫu nhiên khá gần với chiều rộng thực tế, nhưng không chính xác.
Konsumierer

1
@ M.Schenk có nó. Tôi có một VideoView bên trong một bố cục có trọng số. Tôi cần chiều rộng là một tỷ lệ phần trăm của kích thước màn hình và tôi cần duy trì tỷ lệ khung hình. Điều này kéo dài video, vì vậy wrap_contentsẽ không làm. Câu trả lời của Vic không phù hợp với trường hợp này, nhưng bình luận của @ M.Schenk thì có. Nó cũng tiết kiệm thêm một thẻ bố cục. Bạn cũng nên đăng nó như một câu trả lời để hiển thị.
dokkaebi

7
Cuộc gọi sẽ không super.onMeasure()chỉ đơn giản là ghi đè và vô hiệu hóa các tác động của cuộc gọi trước đó tới this.setMeasuredDimension()sao?
Spinner

1
Tôi cũng tò mò, như @Spinner đã đề cập, tại sao lệnh gọi super.onMeasure()không ghi đè các tính toán trước đó.
jwir3

39

Bạn có thể giải quyết điều này bằng cách tạo Chế độ xem tùy chỉnh và ghi đè phương thức onMeasure (). Nếu bạn luôn sử dụng "fill_parent" cho layout_width trong xml của mình thì tham số widthMeasureSpec được truyền vào phương thức onMeasusre () phải chứa chiều rộng của giá trị gốc.

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 của bạn sẽ trông giống như sau:

<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 => tại sao parentWidth / 2 ??
Saeed-rz

6
@Leon_SFS: Câu hỏi đặt ra cho điều đó: "toàn bộ chiều cao và 1/2 chiều rộng"
EdgarT

28

Tôi thấy rằng tốt nhất là bạn không nên tự đặt các kích thước đo được. Trên thực tế, có một chút thương lượng đang diễn ra giữa chế độ xem phụ huynh và trẻ em và bạn không muốn viết lại tất cả đó .

Tuy nhiên, những gì bạn có thể làm là sửa đổi các MeasureSpec, sau đó gọi super bằng chúng. Chế độ xem của bạn sẽ không bao giờ biết rằng nó đang nhận được thông báo đã được sửa đổi từ cha mẹ của nó và sẽ chăm sóc mọi thứ cho bạn:

@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

Khi bạn xác định một bố cục và chế độ xem trên XML, bạn có thể chỉ định chiều rộng và chiều cao bố cục của một chế độ xem là bao bọc nội dung hoặc điền vào phụ huynh. Việc chiếm một nửa khu vực khó hơn một chút, nhưng nếu bạn có thứ gì đó mà bạn muốn ở nửa kia, bạn có thể làm những điều như sau.

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

Cho hai vật có cùng trọng lượng nghĩa là chúng sẽ căng ra để chiếm cùng tỷ lệ của màn hình. Để biết thêm thông tin về bố cục, hãy xem tài liệu dành cho nhà phát triển.


Tôi không khuyến khích điều này, nhưng ... như một cuộc tấn công, nếu bạn sử dụng cách tiếp cận ở trên, bạn có thể biến một trong những chế độ xem đó thành "hình nộm" và đặt android:
vision

Mục đích của việc chỉ sử dụng một nửa diện tích là gì? Bạn có dự định trưng bày bất cứ thứ gì ở nửa kia không? Những gì bạn làm với các khu vực còn lại sẽ thông báo cho các phương pháp tốt nhất
RickNotFred

điều này chắc chắn phải là # 1 - nếu bạn có thể làm điều đó trong xml của mình, tại sao lại làm điều đó trong java của bạn?
fandang

9

Tôi tin rằng cách tiếp cận XML của Mayras có thể thành công. Tuy nhiên, có thể làm cho nó chính xác hơn, chỉ với một lần xem bằng cách đặt weightSum. Tôi sẽ không gọi đây là một vụ hack nữa nhưng theo ý kiến ​​của tôi thì cách tiếp cận đơn giản nhất:

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

Như thế này, bạn có thể sử dụng bất kỳ trọng lượng nào, ví dụ 0,6 (và căn giữa) là trọng lượng tôi muốn sử dụng cho các nút.


3

Thử cái này

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

thì bạn có thể sử dụng LinearLayout.LayoutParams để thiết lập các thông số của chileView

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

2

Bây giờ bạn có thể sử dụng PercentRelativeLayout. Bùm! Vấn đề đã được giải quyết.


2
PercentRelativeLayout không được dùng nữa trong API cấp 26.0.0-beta1.
dzikovskyy

"Ở cấp độ API" nghĩa là gì? Ý bạn là số phiên bản thư viện? PRL nằm trong thư viện hỗ trợ, không có cấp API.
androidguy

Theo cấp độ API, ý tôi là phiên bản Android. Thông tin từ đây developer.android.com/reference/android/support/percent/… . Thông tin thêm về mức độ API đây developer.android.com/guide/topics/manifest/...
dzikovskyy

Bạn vẫn có thể sử dụng lớp này trừ khi vì lý do nào đó, bạn cần phiên bản 26.0.0 trở lên.
androidguy

1

Roman, nếu bạn muốn thực hiện bố cục của mình bằng mã Java (hậu duệ của ViewGroup), bạn hoàn toàn có thể. Bí quyết là bạn phải thực hiện cả hai phương pháp onMeasure và onLayout. OnMeasure được gọi trước tiên và bạn cần "đo lường" lượt xem phụ (định kích thước hiệu quả thành giá trị mong muốn) ở đó. Bạn cần kích thước lại nó trong cuộc gọi onLayout. Nếu bạn không thực hiện được trình tự này hoặc không gọi được setMeasuredDimension () ở cuối mã onMeasure, bạn sẽ không nhận được kết quả. Tại sao điều này được thiết kế một cách phức tạp và mong manh là ngoài tôi.


1

Nếu bên kia trống. Tôi đoán cách đơn giản nhất là thêm một chế độ xem trống (ví dụ: bố cục tuyến tính), sau đó đặt chiều rộng của cả hai chế độ xem thành fill_parent và cả trọng số của chúng thành 1.

Điều này hoạt động trong LinearLayout ...


0

Nó giống như thế này:

<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

Tôi đã đấu tranh với câu hỏi này trong một thời gian dài, tôi muốn thay đổi chiều rộng ngăn kéo của DrawerLayout, là con thứ hai của cha mẹ nó. Gần đây tôi đã tìm ra nó, nhưng không biết có ý tưởng nào hay hơn không.

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
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.