Khai báo các thuộc tính có thể tạo kiểu trong Android


81

Có một tài liệu nhỏ quý giá về declare-styleablethẻ mà chúng ta có thể khai báo các kiểu tùy chỉnh cho các thành phần. Tôi đã tìm thấy danh sách các giá trị hợp lệ này cho formatthuộc tính của attrthẻ. Mặc dù điều đó rất hay, nhưng nó không giải thích cách sử dụng một số giá trị đó. Khi duyệt qua attr.xml (nguồn Android cho các thuộc tính chuẩn), tôi phát hiện ra rằng bạn có thể làm những việc như:

<!-- The most prominent text color.  -->
<attr name="textColorPrimary" format="reference|color" />

Các formatthuộc tính rõ ràng có thể được thiết lập để một sự kết hợp của các giá trị. Có lẽ formatthuộc tính giúp trình phân tích cú pháp diễn giải một giá trị kiểu thực tế. Sau đó, tôi phát hiện ra điều này trong attr.xml:

<!-- Default text typeface. -->
<attr name="typeface">
    <enum name="normal" value="0" />
    <enum name="sans" value="1" />
    <enum name="serif" value="2" />
    <enum name="monospace" value="3" />
</attr>

<!-- Default text typeface style. -->
<attr name="textStyle">
    <flag name="normal" value="0" />
    <flag name="bold" value="1" />
    <flag name="italic" value="2" />
</attr>

Cả hai điều này dường như khai báo một tập hợp các giá trị được phép cho kiểu được chỉ định.

Vì vậy, tôi có hai câu hỏi:

  1. Sự khác biệt giữa thuộc tính style có thể nhận một trong một tập hợp enumgiá trị và một thuộc tính có thể nhận một tập flaggiá trị là gì?
  2. Có ai biết bất kỳ tài liệu nào tốt hơn về cách declare-styleablehoạt động (ngoài kỹ thuật đảo ngược mã nguồn Android) không?

Câu trả lời:


72

Có câu hỏi này ở đây: Xác định các phụ đề tùy chỉnh với một số thông tin, nhưng không nhiều.

bài đăng này . Nó có thông tin tốt về cờ và enum:

Cờ thuộc tính XML tùy chỉnh

Cờ là loại thuộc tính đặc biệt ở chỗ chúng chỉ được phép sử dụng một tập hợp con rất nhỏ các giá trị, cụ thể là những giá trị được xác định bên dưới thẻ thuộc tính. Cờ được chỉ định bởi thuộc tính "tên" và thuộc tính "giá trị". Các tên bắt buộc phải là duy nhất trong loại thuộc tính đó nhưng các giá trị không cần phải có. Đây là lý do mà trong quá trình phát triển của nền tảng Android, chúng tôi đã ánh xạ cả “fill_parent” và “match_parent” đến cùng một hành vi. Giá trị của chúng giống hệt nhau.

Thuộc tính name ánh xạ tới tên được sử dụng ở vị trí giá trị trong XML bố cục và không yêu cầu tiền tố vùng tên. Do đó, đối với “tilingMode” ở trên, tôi đã chọn “center” làm giá trị thuộc tính. Tôi có thể dễ dàng chọn "kéo dài" hoặc "lặp lại" nhưng không có gì khác. Thậm chí không được phép thay thế các giá trị thực tế.

Thuộc tính giá trị phải là một số nguyên. Việc lựa chọn biểu diễn số thập lục phân hoặc số tiêu chuẩn là tùy thuộc vào bạn. Có một vài chỗ trong mã Android nơi cả hai đều được sử dụng và trình biên dịch Android sẵn lòng chấp nhận.

Vùng thuộc tính XML tùy chỉnh

Enums được sử dụng theo cách gần giống như cờ với một cung cấp, chúng có thể được sử dụng thay thế cho nhau với các số nguyên. Về cơ bản, Enums và Integer được ánh xạ tới cùng một kiểu dữ liệu, cụ thể là Integer. Khi xuất hiện trong định nghĩa thuộc tính với Số nguyên, Enum dùng để ngăn chặn "số ma thuật" luôn xấu. Đây là lý do tại sao bạn có thể có “android: layout_width” với thứ nguyên, số nguyên hoặc chuỗi được đặt tên “fill_parent”.

Để đặt điều này vào ngữ cảnh, giả sử rằng tôi tạo một thuộc tính tùy chỉnh có tên “layout_scroll_height” chấp nhận một số nguyên hoặc một chuỗi “scroll_to_top”. Để làm như vậy, tôi sẽ thêm một thuộc tính định dạng "số nguyên" và làm theo điều đó với enum:

<attr name="layout_scroll_height" format="integer">  
    <enum name="scroll_to_top" value="-1"/> 
</attr>

Một quy định khi sử dụng Enums theo cách này là nhà phát triển sử dụng Chế độ xem tùy chỉnh của bạn có thể cố ý đặt giá trị “-1 ″ vào các thông số bố cục. Điều này sẽ kích hoạt logic trường hợp đặc biệt của “scroll_to_top.” Hành vi không mong muốn (hoặc dự kiến) như vậy có thể nhanh chóng đưa thư viện của bạn xuống hàng "mã kế thừa" nếu các giá trị Enum được chọn không tốt.


Theo tôi thấy, các giá trị thực mà bạn có thể thêm trong thực tế vào một thuộc tính bị giới hạn bởi những gì bạn có thể nhận được từ nó. Kiểm tra AttributeSettài liệu tham khảo lớp học tại đây để biết thêm gợi ý.

Bạn có thể lấy:

  • booleans ( getAttributeBooleanValue),
  • float ( getAttributeFloatValue),
  • ints ( getAttributeIntValue),
  • ints (as getAttributeUnsignedIntValue),
  • và chuỗi ( getAttributeValue)

Cảm ơn vì những liên kết đó. Blog Statically Typed đặc biệt hay. Nó đủ gần với "tài liệu thực" mà tôi đánh dấu là đã giải quyết xong.
Ted Hopp

@Ted thực sự, bài đăng đó có thông tin tuyệt vời. Nhưng dù sao, trừ khi bạn đang viết một thư viện các khung nhìn có thể tái sử dụng hoặc mở rộng khung công tác, tôi sẽ không lo lắng về những ô nhiễm này. Bools, float, ints và string có thể giúp bạn có được chế độ xem tùy chỉnh thông thường. Đó là, tất nhiên, nếu câu hỏi không chỉ để thỏa mãn sự tò mò rất lành mạnh :)
Aleadam

@Aleadam - Câu hỏi được thúc đẩy bởi một ứng dụng thực tế. Tôi đang sử dụng các thuộc tính tùy chỉnh và cần thêm một thuộc tính mới. Hóa ra định dạng phù hợp cho thuộc tính mới là enum, nhưng cho đến khi đọc liên kết bạn cung cấp, tôi không thể tìm thấy bất kỳ thông tin nào về sự khác biệt giữa việc sử dụng enumflag.
Ted Hopp

@Ted Tôi rất vui vì nó hữu ích. Có một vài bài viết khác trong blog đó có vẻ thú vị, mặc dù tôi chỉ lướt qua nó, tôi chưa có thời gian để đọc chúng.
Aleadam

3
+1 cho bất kỳ ai liên kết blog của tôi. Tôi thực sự đã cố gắng phân tích cú pháp mỗi một trong những thẻ đó được sử dụng để làm gì. Nó có thể không hoàn chỉnh và có lẽ nên được cập nhật khi thời gian trôi qua nhưng nếu ai đó có gì cần thêm vào nó, tôi đều lắng nghe.
wheaties

70

Câu trả lời của @Aleadam rất hữu ích, nhưng imho nó bỏ qua một điểm khác biệt chính giữa enumflag. Giá trị đầu tiên được dự định để chúng tôi chọn một và chỉ một giá trị khi chúng tôi gán thuộc tính tương ứng cho một số Chế độ xem. Tuy nhiên, các giá trị sau này có thể được kết hợp bằng cách sử dụng toán tử OR bitwise.

Một ví dụ, trong res/values/attr.xml

<!-- declare myenum attribute -->
<attr name="myenum">
    <enum name="zero" value="0" />
    <enum name="one" value="1" />
    <enum name="two" value="2" />
    <enum name="three" value="3" />
</attr>

<!-- declare myflags attribute -->
<attr name="myflags">
    <flag name="one" value="1" />
    <flag name="two" value="2" />
    <flag name="four" value="4" />
    <flag name="eight" value="8" />
</attr>

<!-- declare our custom widget to be styleable by these attributes -->
<declare-styleable name="com.example.MyWidget">
    <attr name="myenum" />
    <attr name="myflags" />
</declare-styleable>

Trong res/layout/mylayout.xmlchúng tôi bây giờ có thể làm

<com.example.MyWidget
    myenum="two"
    myflags="one|two"
    ... />

Vì vậy, một enum chọn một trong các giá trị có thể có của nó, trong khi các cờ có thể được kết hợp. Các giá trị số phải phản ánh sự khác biệt này, thông thường bạn sẽ muốn chuỗi đi 0,1,2,3,...cho enum (được sử dụng làm chỉ số mảng, chẳng hạn) và cờ để đi 1,2,4,8,...để chúng có thể được thêm vào hoặc xóa một cách độc lập, sử dụng bitwise HOẶC |để kết hợp các cờ.

Chúng tôi có thể xác định rõ ràng "cờ meta" với các giá trị không phải là lũy thừa của 2, và do đó giới thiệu một loại tốc ký cho các kết hợp phổ biến. Ví dụ: nếu chúng tôi đã đưa điều này vào myflagskhai báo của mình

<flag name="three" value="3" />

thì chúng tôi có thể viết myflags="three"thay thế myflags="one|two", cho kết quả hoàn toàn giống nhau 3 == 1|2.

Cá nhân tôi muốn luôn bao gồm

<flag name="none" value="0" /> <!-- or "normal, "regular", and so on -->
<flag name="all" value="15" /> <!-- 15 == 1|2|4|8 -->

điều này sẽ cho phép tôi bỏ đặt hoặc đặt tất cả các cờ cùng một lúc.

Tinh tế hơn, có thể là trường hợp một lá cờ này được ám chỉ bởi một lá cờ khác. Vì vậy, trong ví dụ của chúng tôi, giả sử rằng eightcờ đang được đặt nên buộc fourcờ phải được đặt (nếu nó chưa được đặt). Sau đó, chúng tôi có thể xác định eightlại để đưa vào trước, vì nó vốn là fourcờ,

<flag name="eight" value="12" /> <!-- 12 == 8|4 -->

Cuối cùng, nếu bạn đang khai báo các thuộc tính trong một dự án thư viện nhưng muốn áp dụng chúng trong các bố cục của một dự án khác (phụ thuộc vào lib), bạn sẽ cần sử dụng tiền tố không gian tên mà bạn phải ràng buộc trong phần tử gốc XML. Ví dụ,

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:auto="http://schemas.android.com/apk/res-auto"
    ... >

    <com.example.MyWidget
        auto:myenum="two"
        auto:myflags="one|two"
        ... />

</RelativeLayout>

Điểm tốt. Tôi cho rằng đối với một người quen thuộc với enum từ các ngôn ngữ lập trình, sự khác biệt giữa cờ và enum sẽ là bản chất thứ hai. (Tôi thậm chí còn không nhận ra rằng điểm bạn đang đưa ra bị thiếu trong câu trả lời của Aleadam.) Đây chắc chắn là thông tin bổ sung hữu ích. +1
Ted Hopp

Câu trả lời này rõ ràng chỉ ra cách phân biệt các enum với các cờ trong khi xác định các thuộc tính tùy chỉnh. Nó giúp rõ ràng tôi sử dụng cờ :) 1
ptitvinou
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.