Làm cách nào để khai báo phần tử giao diện người dùng Android bằng XML?
Làm cách nào để khai báo phần tử giao diện người dùng Android bằng XML?
Câu trả lời:
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:
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-styleable
thẻ. Các thuộc tính Android không chuẩn như extraInformation
cầ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.
Vì có hai hàm tạo sử dụng một AttributeSet
khở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.MyCustomView
là 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_text
chứa android_text
thuộc tính cho MyCustomView
. Các thuộc tính sau đó có thể được lấy từ TypedArray
các get
chứ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.attr
khi 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.
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.
Nếu bạn muốn có một ví dụ hoàn chỉnh, hãy xem mẫu xem nhãn Android.
TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView);
CharSequences=a.getString(R.styleable.LabelView_text);
<declare-styleable name="LabelView">
<attr name="text"format="string"/>
<attr name="textColor"format="color"/>
<attr name="textSize"format="dimension"/>
</declare-styleable>
<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 LinearLayout
thuộc tính không gian tên:xmlns:app="http://schemas.android.com/apk/res-auto"
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" />
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
Suspicious namespace: Did you mean http://schemas.android.com/apk/res-auto
res-auto
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/[your package name]
. tức làhttp://schemas.android.com/apk/lib/[your package name]
Ngoài ra hầu hết các câu trả lời bỏ phiếu.
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.
Đồ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.
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
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!
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.