Android - Viết một thành phần (hợp chất) tùy chỉnh


132

Ứng dụng Android tôi đ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 TabWidgetvới 3 tab. Mỗi tab có khá nhiều thành phần. Các hoạt động 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ĩ rằng 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ó chứa rất nhiều logic (nhấp vào trình nghe, logic để điền vào danh sách, v.v.).

Những gì tôi thường làm trong các khung dựa trên thành phầ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ó một trách nhiệm rõ ràng. Nó sẽ chứa tập hợp các thành phần của riêng 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 làm thế nào điều này có thể được thực hiện và tôi đã tìm thấy một cái gì đó trong tài liệu Android mà họ muốn gọi là "Điều khiển 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 tệp XML xác định xem cấu trúc.

Trong tài liệu có ghi:

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

Vâng, đó là tin tốt! Cách tiếp cận dựa trên XML chính xác là 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ừ việc nó "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 là kêu gọi setContentView(...)làm tăng các khung nhìn từ XML. Phương thức đó không khả dụng nếu bạn ví dụ lớp con LinearLayout.

Vì vậy, tôi đã cố gắng thổi phồng XML theo cách thủ công 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);
    }
}

Điều này hoạt động, ngoại trừ thực tế là XML tôi đang tải đã LinearLayoutkhai báo là phần tử gốc. Điều này dẫn đến việc bị thổi phồng LinearLayoutlà một đứa trẻ MyCompoundComponentmà chính nó đã là một LinearLayout!! Vì vậy, bây giờ chúng ta có một linearLayout dự phòng ở giữa MyCompoundComponentvà 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ó ngay lập tức LinearLayoutkhông?


14
Tôi thích những câu hỏi mà tôi học được điều gì đó từ. Cảm ơn.
Jeremy Logan

5
Gần đây tôi đã viết một mục blog về điều này: blog.jteam.nl/2009/10/08/exploring-the-world-of-android-part-3
Tom van Zummeren

Câu trả lời:


101

Sử dụng thẻ hợp nhất làm gốc XML của bạn

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

Kiểm tra bài viết này.


10
Cảm ơn rât nhiều! Đó chính xác là những gì tôi đang tìm kiếm. Thật ngạc nhiên khi 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. Thông minh!
Tom van Zummeren

Điều gì về việc bao gồm bố trí hợp nhất này trong cảnh quan?
Kostadin

2
@Timmmm hahahaha Tôi đã hỏi câu hỏi này rất lâu trước khi trình soạn thảo trực quan thậm chí còn tồn tại :)
Tom van Zummeren

0

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

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

Và sau đó có lớp của bạn bắt nguồn từ bất kỳ bố trí 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 sử dụng công cụ bố trí ở đâ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 các bố cục XML như bình thường. Nếu bạn muốn thực hiện chế độ xem theo chương trình, bạn phải tự thổi phồng nó:

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 có cách nào giải quyết vấn đề bố cục lồng nhau, vì vậy đây không thực sự là một giải pháp đầy đủ. Bạn có thể thêm một phương thức 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);
}

Tuyên bố từ chối trách nhiệm: Tôi có thể đang nói bollocks hoàn chỉnh.


Cảm ơn! Tôi nghĩ 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 điều đó!
Tom van Zummeren

8
Và rồi ai đó xuất hiện và cố gắng để sử dụng giao diện tùy chỉnh của bạn trong một nơi bố trí với chỉ <com.example.views.MyView />và bạn setDataonFinishInflatecác cuộc gọi bắt đầu ném NPEs, và bạn không có ý tưởng tại sao.
Christopher Perry

Mẹo ở đây là sử dụng chế độ xem tùy chỉnh của bạn, sau đó trong hàm tạo, thổi phồ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ở được bảo hiểm, đó chính xác là những gì câu hỏi / câu trả lời được chấp nhận làm cùng nhau. Tuy nhiên, những gì bạn không thể làm là tham khảo bố cục trực tiếp nữa. Bây giờ nó đượ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 hàm tạo. Nhưng nếu bạn đang sử dụng phương pháp này, tại sao bạn vẫn cần sử dụng nó ở bất kỳ nơi nào khác? Bạn sẽ không.
Đánh dấu A. Donohoe
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.