Tìm kiếm điểm chính xác trong mã nguồn của Android nơi trẻ em được thêm vào.
Chúng ta có thể xem những gì setContentView(R.layout.some_id)
đang hoạt động dưới mui xe.
setContentView(int)
gọi PhoneWindow#setContentView(int)
-PhoneWindow
Link là một inplementation cụ thể của Window
:
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
Phương pháp LayoutInflater#inflate(layoutResID, mContentParent)
cuối cùng gọi ViewGroup#addView(View, LayoutParams)
trên mContentParent
. Ở giữa, chế độ xem con
Tôi muốn biết điều gì xảy ra chính xác sau khi tôi đặt chế độ xem nội dung thành tệp XML chứa chế độ xem tùy chỉnh. Afer constructor có phải là một phần trong đoạn mã nơi khung nhìn tùy chỉnh "parse/read/inflate/convert" các khung nhìn con được khai báo XML thành các khung nhìn thực tế!(Comment bởi JohnTube)
Ambiquity: Từ bình luận JohnTube của, có vẻ như ông là quan tâm nhiều hơn trong biết làm thế nào một cái nhìn tùy chỉnh là thổi phồng. Để biết điều này, chúng ta sẽ phải xem xét các hoạt động của LayoutInflater
Link.
Vì vậy, câu trả lời cho Which method of xLayout or ViewGroup should I override ?
là ViewGroup#addView(View, LayoutParams)
. Lưu ý rằng, tại thời điểm này, lạm phát của tất cả các Chế độ xem thông thường/tùy chỉnh đã diễn ra.
Lạm phát của quan điểm tùy chỉnh:
Các phương pháp sau đây trong LayoutInflater
là nơi addView(View, LayoutParams)
được kêu gọi phụ huynh/root:
Lưu ý: Các cuộc gọi mLayoutInflater.inflate(layoutResID, mContentParent);
trong PhoneWindow#setContentView(int)
chuỗi này. Ở đây mContentParent
là DecorView
: chế độ xem có thể truy cập qua getWindow().getDecorView()
.
// Inflate a new view hierarchy from the specified XML node.
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
// Recursive method used to descend down the xml hierarchy and instantiate views,
// instantiate their children, and then call onFinishInflate().
void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException
Cuộc gọi quan tâm trong phương pháp này (và trong đệ quy rInflate(XmlPullParser, View, AttributeSet, boolean)
) là:
temp = createViewFromTag(root, name, attrs);
Hãy xem những gì createViewFromTag(...)
được thực hiện:
View createViewFromTag(View parent, String name, AttributeSet attrs) {
....
....
if (view == null) {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null, attrs);
}
}
....
}
Các period(.)
quyết định liệu onCreateView(...)
hoặc createView(...)
được gọi.
Tại sao lại chọn séc này? Bởi vì View
được xác định trong các gói android.view
, android.widget
hoặc android.webkit
được truy cập thông qua tên lớp của nó. Ví dụ:
android.widget: Button, TextView etc.
android.view: ViewStub. SurfaceView, TextureView etc.
android.webkit: WebView
Khi gặp phải những quan điểm này, onCreateView(parent, name, attrs)
được gọi. Phương pháp này thực sự chuỗi để createView(...)
:
protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
return createView(name, "android.view.", attrs);
}
này sẽ đối phó với SurfaceView
, TextureView
và quan điểm khác quy định tại android.view
gói. Nếu bạn muốn biết cách TextView, Button etc.
được xử lý, hãy xem PhoneLayoutInflater
Link - nó mở rộng LayoutInflater
và ghi đè onCreateView(...)
để kiểm tra xem android.widget
và android.webkit
là tên gói dự kiến hay không. Thực tế, cuộc gọi getLayoutInflater()
giúp bạn có một phiên bản PhoneLayoutInflater
. Đây là lý do tại sao nếu bạn đã phân loại LayoutInflater
, bạn thậm chí không thể thổi phồng bố cục đơn giản nhất - bởi vì LayoutInflater
chỉ có thể xử lý lượt xem từ gói android.view
.
Dù sao thì tôi cũng đang đi tiêu. Bit phụ này xảy ra cho Chế độ xem thông thường - không có số period(.)
trong định nghĩa của chúng. Chế độ xem tùy chỉnh do có một khoảng thời gian trong tên của chúng - com.my.package.CustomView
. Đây là cách số LayoutInflater
phân biệt giữa hai số.
Vì vậy, trong trường hợp có chế độ xem thông thường (ví dụ: Nút), prefix
chẳng hạn như android.widget
sẽ được chuyển làm đối số thứ hai - cho chế độ xem tùy chỉnh, điều này sẽ là null
. Sau đó, prefix
được sử dụng cùng với name
để có được hàm tạo cho lớp của chế độ xem cụ thể đó. Chế độ xem tùy chỉnh không cần điều này vì name
của chúng tôi đã đủ điều kiện. Tôi đoán điều này đã được thực hiện để thuận tiện. Khác, bạn có thể đã được xác định bố trí theo cách này:
<android.widget.LinearLayout
...
... />
(pháp lý của nó mặc dù ...)
Ngoài ra, đây là lý do tại sao lượt xem đến từ một thư viện hỗ trợ (ví dụ < android.support. .v4.widget.DrawerLayout ... />) phải sử dụng tên đầy đủ.
Bằng cách này, nếu bạn đã muốn viết bố trí của bạn như:
<MyCustomView ../>
tất cả các bạn phải làm là để mở rộng LayoutInflater và thêm tên gói của bạn com.my.package.
vào danh sách các chuỗi được kiểm tra trong thời lạm phát . Kiểm tra PhoneLayoutInflater
để được trợ giúp về vấn đề này.
Hãy xem điều gì xảy ra trong giai đoạn cuối cùng cho xem cả hai tùy chỉnh và thường xuyên - createView(...)
:
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
// Try looking for the constructor in cache
Constructor<? extends View> constructor = sConstructorMap.get(name);
Class<? extends View> clazz = null;
try {
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
....
// Get constructor
constructor = clazz.getConstructor(mConstructorSignature);
sConstructorMap.put(name, constructor);
} else {
....
}
Object[] args = mConstructorArgs;
args[1] = attrs;
// Obtain an instance
final View view = constructor.newInstance(args);
....
// We finally have a view!
return view;
}
// A bunch of catch blocks:
- if the only constructor defined is `CustomView(Context)` - NoSuchMethodException
- if `com.my.package.CustomView` doesn't extend View - ClassCastException
- if `com.my.package.CustomView` is not found - ClassNotFoundException
// All these catch blocks throw the often seen `InflateException`.
}
... một View
được sinh ra.
Câu trả lời hay! Tôi đã tìm kiếm tập quán của 'addView (View)', nhưng không tìm thấy bất cứ điều gì có liên quan. Tham chiếu đến 'rInflate' trong' LayoutInflater' là một trợ giúp lớn! Cuộc gọi bên phải mà tôi đang tìm kiếm là 'addView (View, int, LayoutParams)'. – nhaarman
@NiekHaarman Cảm ơn, Niek. 'addView (View, int, LayoutParams)' thực sự là cuộc gọi đúng. Tất cả các chuỗi phương thức 'addView' đã bị quá tải khác. – Vikram