2009-09-25 31 views
129

Ứng dụng Android mà tôi hiện đang phát triển có một hoạt động chính đã phát triển khá lớn. Điều này chủ yếu là vì nó chứa một TabWidget với 3 tab. Mỗi tab có khá nhiều thành phần. Hoạt động này phải kiểm soát tất cả các thành phần đó cùng một lúc. Vì vậy, tôi nghĩ bạn có thể tưởng tượng rằng Hoạt động này có 20 trường (một trường cho hầu hết mọi thành phần). Ngoài ra nó có chứa rất nhiều logic (click listeners, logic để điền vào danh sách, vv).Android - Viết một thành phần tùy chỉnh (hợp chất)

Những gì tôi thường làm trong khung thành phần dựa trên là chia mọi thứ thành các thành phần tùy chỉnh. Mỗi thành phần tùy chỉnh sau đó sẽ có trách nhiệm rõ ràng. Nó sẽ chứa tập hợp các thành phần riêng của nó và tất cả các logic khác liên quan đến thành phần đó.

Tôi đã cố gắng tìm hiểu cách thực hiện điều này và tôi đã tìm thấy nội dung nào đó trong tài liệu Android mà họ muốn gọi là "Kiểm soát hợp chất". (Xem http://developer.android.com/guide/topics/ui/custom-components.html#compound và cuộn đến phần "Điều khiển Hợp chất") Tôi muốn tạo một thành phần như vậy dựa trên một tệp XML xác định cấu trúc khung nhìn.

Trong tài liệu hướng dẫn nó nói:

Lưu ý rằng giống như với một hoạt động, bạn có thể sử dụng một trong hai cách tiếp cận (dựa trên XML) khai báo để tạo ra chứa các thành phần, hoặc bạn có thể làm tổ chúng lập trình từ mã của bạn.

Vâng, đó là tin tốt! Cách tiếp cận dựa trên XML là chính xác những gì tôi muốn! Nhưng nó không nói làm thế nào để làm điều đó, ngoại trừ rằng nó là "giống như với một hoạt động" ... Nhưng những gì tôi làm trong một hoạt động được gọi setContentView(...) để thổi phồng các quan điểm từ XML. Phương thức đó không có sẵn nếu bạn ví dụ lớp con LinearLayout.

Vì vậy, tôi đã cố gắng để thổi phồng XML bằng tay như thế này:

public class MyCompoundComponent extends LinearLayout { 

    public MyCompoundComponent(Context context, AttributeSet attributeSet) { 
     super(context, attributeSet); 
     LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     inflater.inflate(R.layout.my_layout, this); 
    } 
} 

này hoạt động, ngoại trừ một thực tế rằng XML Tôi tải có LinearLayout khai báo là phần tử gốc. Điều này dẫn đến con số LinearLayout tăng cao là con của MyCompoundComponent mà chính nó đã là LinearLayout !! Vì vậy, bây giờ chúng ta có một LinearLayout dư thừa ở giữa MyCompoundComponent và các khung nhìn mà nó thực sự cần.

Ai đó có thể vui lòng cung cấp cho tôi một cách tốt hơn để tiếp cận điều này, tránh việc có một số dư thừa LinearLayout được khởi tạo không?

+13

Tôi yêu các câu hỏi mà tôi học được điều gì đó từ đó. Cảm ơn. –

+4

Gần đây tôi đã viết một bài viết blog về điều này: http://blog.jteam.nl/2009/10/08/exploring-the-world-of-android-part-3/ –

Trả lời

98

Sử dụng merge thẻ như là người chủ XML của bạn

<merge xmlns:android="http://schemas.android.com/apk/res/android"> 
<!-- Your Layout --> 
</merge> 

Check this article.

+10

Cảm ơn bạn rất nhiều! Đó là chính xác những gì tôi đang tìm kiếm. Tuyệt vời như thế nào một câu hỏi dài như vậy có thể có một câu trả lời ngắn như vậy. Xuất sắc! –

+0

Điều gì về việc loại bỏ bố cục hợp nhất này trong cảnh quan? – Kostadin

+0

Nhưng điều này có nghĩa là bạn không thể sử dụng trình chỉnh sửa hình ảnh ... – Timmmm

0

Tôi nghĩ rằng cách bạn phải làm điều đó, là sử dụng tên lớp của bạn như là phần tử gốc XML:

<com.example.views.MyView xmlns:.... 
     android:orientation="vertical" etc.> 
    <TextView android:id="@+id/text" ... /> 
</com.example.views.MyView> 

Sau đó, lớp học của bạn bắt nguồn từ bất kỳ bố cục nào bạn muốn sử dụng. Lưu ý rằng nếu bạn đang sử dụng phương pháp này, bạn không hãy sử dụng công cụ tạo bố cục tại đây.

public class MyView extends LinearLayout 
{ 
    public ConversationListItem(Context context, AttributeSet attrs) 
    { 
     super(context, attrs); 
    } 
    public ConversationListItem(Context context, AttributeSet attrs, int defStyle) 
    { 
     super(context, attrs, defStyle); 
    } 


    public void setData(String text) 
    { 
     mTextView.setText(text); 
    } 

    private TextView mTextView; 

    @Override 
    protected void onFinishInflate() 
    { 
     super.onFinishInflate(); 

     mTextView = (TextView)findViewById(R.id.text); 
    } 
} 

Và sau đó bạn có thể sử dụng chế độ xem của mình trong bố cục XML như bình thường.Nếu bạn muốn thực hiện quan điểm lập trình bạn phải thổi phồng nó tự hỏi:

MyView v = (MyView)inflater.inflate(R.layout.my_view, parent, false); 

Thật không may điều này không cho phép bạn làm v = new MyView(context) vì dường như không phải là một con đường xung quanh vấn đề bố trí lồng nhau, vì vậy isn này 't thực sự là một giải pháp đầy đủ. Bạn có thể thêm một phương pháp như thế này để MyView để làm cho nó đẹp hơn một chút:

public static MyView create(Context context) 
{ 
    return (MyView)LayoutInflater.fromContext(context).inflate(R.layout.my_view, null, false); 
} 

Disclaimer: Tôi có thể nói bollocks hoàn tất.

+0

Cảm ơn! Tôi nghĩ rằng câu trả lời này cũng đúng :) Nhưng ba năm trước, khi tôi hỏi câu hỏi này, "hợp nhất" cũng đã làm các trick! –

+8

Và sau đó ai đó đến và cố gắng sử dụng chế độ xem tùy chỉnh của bạn trong bố cục ở đâu đó chỉ với '' '' '' và các cuộc gọi 'setData' và' onFinishInflate' của bạn bắt đầu ném NPE, và bạn không biết tại sao. –

+0

Bí quyết ở đây là sử dụng chế độ xem tùy chỉnh của bạn, sau đó trong hàm tạo, tăng bố cục sử dụng thẻ hợp nhất làm gốc. Bây giờ bạn có thể sử dụng nó trong XML, hoặc chỉ bằng cách làm mới nó. Tất cả các cơ sở đều được đề cập, đó chính xác là câu hỏi/câu trả lời được chấp nhận. Tuy nhiên, điều bạn không thể làm là tham khảo bố cục trực tiếp. Nó bây giờ là 'thuộc sở hữu' bởi điều khiển tùy chỉnh và chỉ nên được sử dụng bởi điều đó trong constructor. Nhưng nếu bạn đang sử dụng cách tiếp cận này, tại sao bạn sẽ cần phải sử dụng nó ở bất cứ nơi nào khác? Bạn sẽ không. – MarqueIV

Các vấn đề liên quan