Khai báo một phần tử giao diện người dùng Android tùy chỉnh bằng XML


473

Làm cách nào để khai báo phần tử giao diện người dùng Android bằng XML?


15
Nếu bất cứ ai tìm kiếm danh sách các định dạng thuộc tính tích hợp, được hỗ trợ, nó có thể được tìm thấy tức là ở đây .
Marcin Orleansowski

1
Hướng dẫn tốt để bắt đầu với -> Tạo chế độ xem hợp chất trên Android
Gayan Weerakutti

Câu trả lời:


840

Hướng dẫn dành cho nhà phát triển Android có một phần gọi là Xây dựng thành phần tùy chỉnh . Thật không may, việc thảo luận về các thuộc tính XML chỉ bao gồm việc khai báo điều khiển bên trong tệp bố cục và không thực sự xử lý các giá trị bên trong khởi tạo lớp. Các bước thực hiện như sau:

1. Khai báo các thuộc tính trong values\attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyCustomView">
        <attr name="android:text"/>
        <attr name="android:textColor"/>            
        <attr name="extraInformation" format="string" />
    </declare-styleable>
</resources>

Lưu ý việc sử dụng tên không đủ tiêu chuẩn trong declare-styleablethẻ. Các thuộc tính Android không chuẩn như extraInformationcần phải khai báo kiểu của chúng. Các thẻ được khai báo trong lớp cha sẽ có sẵn trong các lớp con mà không cần phải được khai báo lại.

2. Tạo các nhà xây dựng

Vì có hai hàm tạo sử dụng một AttributeSetkhởi tạo, nên thuận tiện để tạo một phương thức khởi tạo riêng cho các hàm tạo để gọi.

private void init(AttributeSet attrs) { 
    TypedArray a=getContext().obtainStyledAttributes(
         attrs,
         R.styleable.MyCustomView);

    //Use a
    Log.i("test",a.getString(
         R.styleable.MyCustomView_android_text));
    Log.i("test",""+a.getColor(
         R.styleable.MyCustomView_android_textColor, Color.BLACK));
    Log.i("test",a.getString(
         R.styleable.MyCustomView_extraInformation));

    //Don't forget this
    a.recycle();
}

R.styleable.MyCustomViewlà một int[]tài nguyên được tạo tự động trong đó mỗi phần tử là ID của một thuộc tính. Các thuộc tính được tạo cho mỗi thuộc tính trong XML bằng cách nối thêm tên thuộc tính vào tên thành phần. Ví dụ, R.styleable.MyCustomView_android_textchứa android_textthuộc tính cho MyCustomView. Các thuộc tính sau đó có thể được lấy từ TypedArraycác getchức năng khác nhau . Nếu thuộc tính không được định nghĩa trong định nghĩa trong XML, thì nullđược trả về. Tất nhiên, ngoại trừ, nếu kiểu trả về là nguyên thủy, trong trường hợp đó, đối số thứ hai được trả về.

Nếu bạn không muốn truy xuất tất cả các thuộc tính, có thể tạo mảng này theo cách thủ công. ID cho các thuộc tính Android tiêu chuẩn được bao gồm trong android.R.attrkhi các thuộc tính cho dự án này được đưa vào R.attr.

int attrsWanted[]=new int[]{android.R.attr.text, R.attr.textColor};

Xin lưu ý rằng bạn không nên sử dụng bất cứ thứ gì trong android.R.styleable, vì theo chủ đề này, nó có thể thay đổi trong tương lai. Nó vẫn còn trong tài liệu là để xem tất cả các hằng số này ở một nơi là hữu ích.

3. Sử dụng nó trong một tập tin bố trí như layout\main.xml

Bao gồm khai báo không gian tên xmlns:app="http://schemas.android.com/apk/res-auto"trong phần tử xml cấp cao nhất. Không gian tên cung cấp một phương pháp để tránh các xung đột đôi khi xảy ra khi các lược đồ khác nhau sử dụng cùng tên các thành phần (xem bài viết này để biết thêm thông tin). URL chỉ đơn giản là một cách xác định các lược đồ duy nhất - không có gì thực sự cần được lưu trữ tại URL đó . Nếu điều này dường như không làm gì cả, đó là vì bạn thực sự không cần thêm tiền tố không gian tên trừ khi bạn cần giải quyết xung đột.

<com.mycompany.projectname.MyCustomView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@android:color/transparent"
    android:text="Test text"
    android:textColor="#FFFFFF"
    app:extraInformation="My extra information"
/> 

Tham chiếu chế độ xem tùy chỉnh bằng cách sử dụng tên đủ điều kiện.

Mẫu nhãn AndroidView

Nếu bạn muốn có một ví dụ hoàn chỉnh, hãy xem mẫu xem nhãn Android.

LabelView.java

 TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView);
 CharSequences=a.getString(R.styleable.LabelView_text);

attrs.xml

<declare-styleable name="LabelView">
    <attr name="text"format="string"/>
    <attr name="textColor"format="color"/>
    <attr name="textSize"format="dimension"/>
</declare-styleable>

custom_view_1.xml

<com.example.android.apis.view.LabelView
    android:background="@drawable/blue"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    app:text="Blue" app:textSize="20dp"/>

Điều này được chứa trong một LinearLayoutthuộc tính không gian tên:xmlns:app="http://schemas.android.com/apk/res-auto"

Liên kết


14
Tôi muốn thêm rằng nếu phần tử gốc của bạn yêu cầu không gian tên tùy chỉnh của bạn, bạn sẽ phải thêm cả không gian tên Android tiêu chuẩn và tùy chỉnh của riêng bạn hoặc nếu không bạn có thể gặp lỗi xây dựng.
Đuổi theo

11
Câu trả lời này là tài nguyên rõ ràng nhất trên Internet về các thông số XML tùy chỉnh mà tôi có thể tìm thấy. Cảm ơn bạn, Casebash.
Artem Russakovskii

2
vì một số lý do, trình soạn thảo trực quan từ chối sử dụng giá trị văn bản viết cho android: text, tuy nhiên thiết bị sử dụng nó rất tốt. làm thế nào mà ?
nhà phát triển Android

2
@androiddeveloper Dường như trình soạn thảo Eclipse từ chối sử dụng các giá trị cho tất cả các thuộc tính Android :. Tôi muốn biết nếu đó là một tính năng hoặc lỗi
deej

4
Mục đích của xmlns: không gian tên ứng dụng và res-auto là gì?
IgorGanapolsky

91

Tài liệu tham khảo tuyệt vời. Cảm ơn! Một bổ sung cho nó:

Nếu bạn tình cờ có một dự án thư viện bao gồm các thuộc tính tùy chỉnh cho chế độ xem tùy chỉnh, bạn phải khai báo không gian tên dự án của bạn, không phải thư viện. Ví dụ:

Cho rằng thư viện có gói "com.example.l Library.customview" và dự án làm việc có gói "com.example.customview", sau đó:

Sẽ không hoạt động (hiển thị lỗi "lỗi: Không tìm thấy định danh tài nguyên cho thuộc tính 'newAttr' trong gói 'com.example.l Library.customview'"):

<com.library.CustomView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res/com.example.library.customview"
        android:id="@+id/myView"
        app:newAttr="value" />

Sẽ làm việc:

<com.library.CustomView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res/com.example.customview"
        android:id="@+id/myView"
        app:newAttr="value" />

47
Điều này đã được ~ cố định trong bản xem trước ADT 17. Để sử dụng không gian tên của ứng dụng từ thư viện, hãy xmlns:app="http://schemas.android.com/apk/res-auto"xem bình luận 57 trong code.google.com/p/android/issues/detail?id=9656
nmr

2
Bao gồm không gian tên tùy chỉnh của bạn bây giờ trả về một lỗiSuspicious namespace: Did you mean http://schemas.android.com/apk/res-auto
Ben Wilkinson

không gian tên tùy chỉnh kết thúc ở chế độ tự động lại vì chúng tôi đang sử dụng Android Studio và Gradle. Mặt khác (ví dụ: một số phiên bản Eclipse), nó thường sẽ kết thúc bằng lib / [tên gói của bạn]
Universe

không gian tên tùy chỉnh kết thúc res-autovì chúng tôi đang sử dụng Android Studio và Gradle. Mặt khác (ví dụ một số phiên bản Eclipse) nó thường sẽ kết thúc bằng lib/[your package name]. tức làhttp://schemas.android.com/apk/lib/[your package name]
Vũ trụ

27

Ngoài ra hầu hết các câu trả lời bỏ phiếu.

gotStyledAttribut ()

Tôi muốn thêm một số từ về cách sử dụng gotStyledAttribut (), khi chúng tôi tạo chế độ xem tùy chỉnh bằng các thuộc tính được xác định trước android: xxx. Đặc biệt là khi chúng tôi sử dụng TextAppurdy.
Như đã đề cập trong "2. Tạo các nhà xây dựng", chế độ xem tùy chỉnh sẽ có Attributionset khi tạo. Sử dụng chính chúng ta có thể thấy trong mã nguồn TextView (API 16).

final Resources.Theme theme = context.getTheme();

// TextAppearance is inspected first, but let observe it later

TypedArray a = theme.obtainStyledAttributes(
            attrs, com.android.internal.R.styleable.TextView, defStyle, 0);

int n = a.getIndexCount();
for (int i = 0; i < n; i++) 
{
    int attr = a.getIndex(i);
    // huge switch with pattern value=a.getXXX(attr) <=> a.getXXX(a.getIndex(i))
}
a.recycle();

Những gì chúng ta có thể thấy ở đây?
obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
Bộ thuộc tính được xử lý theo chủ đề theo tài liệu. Các giá trị thuộc tính được biên dịch từng bước. Các thuộc tính đầu tiên được điền từ chủ đề, sau đó các giá trị được thay thế bằng các giá trị từ kiểu và cuối cùng là các giá trị chính xác từ XML cho trường hợp xem đặc biệt thay thế các giá trị khác.
Mảng thuộc tính được yêu cầu - com.android.internal.R.styleable.TextView
Đây là một mảng thông thường của hằng số. Nếu chúng tôi yêu cầu các thuộc tính tiêu chuẩn, chúng tôi có thể xây dựng mảng này bằng tay.

Những gì không được đề cập trong tài liệu - thứ tự các yếu tố TypedArray kết quả.
Khi chế độ xem tùy chỉnh được khai báo trong attrs.xml, các hằng số đặc biệt cho các chỉ mục thuộc tính được tạo. Và chúng ta có thể trích xuất các giá trị theo cách này : a.getString(R.styleable.MyCustomView_android_text). Nhưng đối với hướng dẫn sử dụng int[]không có hằng số. Tôi cho rằng, getXXXValue (mảng Index) sẽ hoạt động tốt.

Và câu hỏi khác là: "Làm thế nào chúng ta có thể thay thế các hằng số nội bộ và yêu cầu các thuộc tính tiêu chuẩn?" Chúng tôi có thể sử dụng các giá trị android.R.attr. *.

Vì vậy, nếu chúng ta muốn sử dụng thuộc tính TextAppparent tiêu chuẩn trong chế độ xem tùy chỉnh và đọc các giá trị của nó trong hàm tạo, chúng ta có thể sửa đổi mã từ TextView theo cách này:

ColorStateList textColorApp = null;
int textSize = 15;
int typefaceIndex = -1;
int styleIndex = -1;

Resources.Theme theme = context.getTheme();

TypedArray a = theme.obtainStyledAttributes(attrs, R.styleable.CustomLabel, defStyle, 0);
TypedArray appearance = null;
int apResourceId = a.getResourceId(R.styleable.CustomLabel_android_textAppearance, -1);
a.recycle();
if (apResourceId != -1)
{
    appearance = 
        theme.obtainStyledAttributes(apResourceId, new int[] { android.R.attr.textColor, android.R.attr.textSize, 
            android.R.attr.typeface, android.R.attr.textStyle });
}
if (appearance != null)
{
    textColorApp = appearance.getColorStateList(0);
    textSize = appearance.getDimensionPixelSize(1, textSize);
    typefaceIndex = appearance.getInt(2, -1);
    styleIndex = appearance.getInt(3, -1);

    appearance.recycle();
}

Trong đó CustomLabel được xác định:

<declare-styleable name="CustomLabel">
    <!-- Label text. -->
    <attr name="android:text" />
    <!-- Label text color. -->
    <attr name="android:textColor" />
    <!-- Combined text appearance properties. -->
    <attr name="android:textAppearance" />
</declare-styleable>

Có thể, tôi đã nhầm, nhưng tài liệu Android về gotStyledAttribut () rất kém.

Mở rộng thành phần UI tiêu chuẩn

Đồng thời chúng ta chỉ có thể mở rộng thành phần UI tiêu chuẩn, sử dụng tất cả các thuộc tính được khai báo của nó. Cách tiếp cận này không tốt lắm, ví dụ như TextView khai báo rất nhiều thuộc tính. Và sẽ không thể thực hiện đầy đủ chức năng trong overriden onMeasure () và onDraw ().

Nhưng chúng ta có thể hy sinh sự tái sử dụng rộng rãi về mặt lý thuyết của thành phần tùy chỉnh. Nói "Tôi biết chính xác những tính năng tôi sẽ sử dụng" và không chia sẻ mã với bất kỳ ai.

Sau đó chúng ta có thể thực hiện constructor CustomComponent(Context, AttributeSet, defStyle). Sau khi gọi, super(...)chúng ta sẽ có tất cả các thuộc tính được phân tích cú pháp và có sẵn thông qua các phương thức getter.


làm android: xxx thuộc tính được xác định trước làm việc trong thiết kế gui nhật thực?
deej

Các thuộc tính như vậy được công nhận bởi plugin ADT của Eclipse trong trình soạn thảo thuộc tính. Tôi có thể thấy mặc định từ phong cách của mình, nếu một số giá trị không được xác định. Và đừng quên thêm chú thích @RemoteView vào lớp của bạn.
yuriy.weiss

Không thể làm cho nó hoạt động. Eclipse tiếp tục tải null cho getText và ném android.content.res.Resource $ NotFoundException cho getResourceId, mặc dù ứng dụng chạy tốt trên thiết bị.
deej

Xin lỗi, tôi không thể giúp bạn. Tôi đã chỉ tạo dự án demo để kiểm tra các khả năng và không gặp phải các lỗi như vậy.
yuriy.weiss

Điều này tốt hơn nhiều so với việc ánh xạ các thuộc tính tùy chỉnh của chế độ xem tùy chỉnh sang thuộc tính tích hợp của chế độ xem tích hợp có trong.
samis

13

Có vẻ như Google đã cập nhật trang nhà phát triển của mình và thêm các khóa đào tạo khác nhau ở đó.

Một trong số đó liên quan đến việc tạo các chế độ xem tùy chỉnh và có thể được tìm thấy ở đây


5

Cảm ơn rất nhiều cho câu trả lời đầu tiên.

Đối với tôi, tôi chỉ có một vấn đề với nó. Khi thổi phồng quan điểm của tôi, tôi đã gặp một lỗi: java.lang.NoSuchMethodException: MyView (Ngữ cảnh, Thuộc tính)

Tôi đã giải quyết nó bằng cách tạo một hàm tạo mới:

public MyView(Context context, AttributeSet attrs) {
     super(context, attrs);
     // some code
}

Hy vọng điều này sẽ giúp!


0

Bạn có thể bao gồm bất kỳ tệp bố cục nào trong tệp bố cục khác như-

             <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="30dp" >

                <include
                    android:id="@+id/frnd_img_file"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    layout="@layout/include_imagefile"/>

                <include
                    android:id="@+id/frnd_video_file"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    layout="@layout/include_video_lay" />

                <ImageView
                    android:id="@+id/downloadbtn"
                    android:layout_width="30dp"
                    android:layout_height="30dp"
                    android:layout_centerInParent="true"
                    android:src="@drawable/plus"/>
            </RelativeLayout>

ở đây các tệp bố cục trong thẻ bao gồm là các tệp bố cục .xml khác trong cùng thư mục res.


Tôi đã thử điều này, vấn đề tôi gặp phải là bố cục đi kèm không thể 'thích nghi', không thể tạo ra tổng quát. Ví dụ: khi tôi bao gồm một nút theo cách tương tự, nếu tôi cố gắng đặt văn bản trong xml thì nó hoạt động.
cfl
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.