Android - การเขียนคอมโพเนนต์ (สารประกอบ) ที่กำหนดเอง


132

แอป Android ที่ฉันกำลังพัฒนาอยู่มีกิจกรรมหลักที่เติบโตขึ้นค่อนข้างมาก ส่วนใหญ่เป็นเพราะมีTabWidget3 แท็บ แต่ละแท็บมีส่วนประกอบค่อนข้างน้อย กิจกรรมต้องควบคุมส่วนประกอบเหล่านั้นทั้งหมดพร้อมกัน ดังนั้นฉันคิดว่าคุณคงนึกออกว่ากิจกรรมนี้มี 20 ช่อง (ช่องสำหรับเกือบทุกองค์ประกอบ) นอกจากนี้ยังมีตรรกะมากมาย (ตัวฟังคลิกตรรกะเพื่อเติมรายการ ฯลฯ )

โดยปกติสิ่งที่ฉันทำในเฟรมเวิร์กที่ใช้ส่วนประกอบคือการแยกทุกอย่างออกเป็นส่วนประกอบที่กำหนดเอง ส่วนประกอบที่กำหนดเองแต่ละส่วนจะมีความรับผิดชอบที่ชัดเจน มันจะมีชุดส่วนประกอบของตัวเองและตรรกะอื่น ๆ ทั้งหมดที่เกี่ยวข้องกับส่วนประกอบนั้น

ฉันพยายามหาวิธีที่จะทำได้และพบบางอย่างในเอกสารของ Android ที่พวกเขาชอบเรียกว่า "Compound Control" (ดูhttp://developer.android.com/guide/topics/ui/custom-components.html#compoundและเลื่อนไปที่ส่วน "Compound Controls") ฉันต้องการสร้างส่วนประกอบดังกล่าวโดยใช้ไฟล์ XML ที่กำหนด ดูโครงสร้าง

ในเอกสารระบุว่า:

โปรดทราบว่าเช่นเดียวกับกิจกรรมคุณสามารถใช้วิธีการประกาศ (ตาม XML) ในการสร้างส่วนประกอบที่มีอยู่หรือคุณสามารถซ้อนกันโดยทางโปรแกรมจากโค้ดของคุณ

นั่นเป็นข่าวดี! วิธีการที่ใช้ XML เป็นสิ่งที่ฉันต้องการ! แต่มันไม่ได้บอกว่าต้องทำอย่างไรยกเว้นว่า "ชอบด้วยกิจกรรม" ... แต่สิ่งที่ฉันทำในกิจกรรมคือการเรียกร้องsetContentView(...)ให้ขยายมุมมองจาก XML LinearLayoutวิธีการที่ไม่สามารถใช้ได้ถ้าคุณตัวอย่างเช่นประเภทรอง

ดังนั้นฉันจึงพยายามขยาย XML ด้วยตนเองดังนี้:

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);
    }
}

สิ่งนี้ใช้งานได้ยกเว้นข้อเท็จจริงที่ว่า XML ที่ฉันกำลังโหลดได้LinearLayoutประกาศเป็นองค์ประกอบราก ส่งผลให้พองLinearLayoutเป็นเด็กMyCompoundComponentซึ่งตัวเองอยู่แล้วLinearLayout!! ตอนนี้เรามี LinearLayout ที่ซ้ำซ้อนอยู่ระหว่างMyCompoundComponentและมุมมองที่ต้องการจริงๆ

ใครช่วยบอกวิธีที่ดีกว่านี้ให้ฉันได้โดยไม่ต้องมีการLinearLayoutสร้างอินสแตนซ์ซ้ำซ้อน


14
ฉันชอบคำถามที่ฉันเรียนรู้บางสิ่งจาก ขอบคุณ.
Jeremy Logan

5
ฉันเพิ่งเขียนรายการบล็อกเกี่ยวกับเรื่องนี้: blog.jteam.nl/2009/10/08/exploring-the-world-of-android-part-3
Tom van Zummeren

คำตอบ:


101

ใช้แท็กผสานเป็นรูท XML ของคุณ

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

ตรวจสอบบทความนี้


10
ขอบคุณมาก! นั่นคือสิ่งที่ฉันกำลังมองหา น่าทึ่งที่คำถามยาว ๆ แบบนี้สามารถตอบสั้น ๆ ได้ ยอดเยี่ยม!
Tom van Zummeren

แล้วการรวมเค้าโครงผสานนี้ในแนวนอนล่ะ
Kostadin

2
@Timmmm ฮ่า ๆ ๆ ฉันถามคำถามนี้มานานก่อนที่โปรแกรมแก้ไขภาพจะมีอยู่ :)
Tom van Zummeren

0

ฉันคิดว่าวิธีที่คุณควรทำคือใช้ชื่อคลาสของคุณเป็นองค์ประกอบราก XML:

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

จากนั้นให้คลาสของคุณได้มาจากเลย์เอาต์ที่คุณต้องการใช้ โปรดทราบว่าหากคุณกำลังใช้วิธีนี้คุณไม่ได้ใช้ตัวพองโครงร่างที่นี่

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);
    }
}

จากนั้นคุณสามารถใช้มุมมองของคุณในเค้าโครง XML ได้ตามปกติ หากคุณต้องการสร้างมุมมองโดยใช้โปรแกรมคุณต้องขยายด้วยตัวเอง:

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

น่าเสียดายที่สิ่งนี้ไม่ยอมให้คุณทำv = new MyView(context)เพราะดูเหมือนจะไม่มีวิธีแก้ไขปัญหาเลย์เอาต์ที่ซ้อนกันดังนั้นนี่จึงไม่ใช่วิธีแก้ปัญหาที่สมบูรณ์ คุณสามารถเพิ่มวิธีการเช่นนี้MyViewเพื่อให้ดีขึ้นเล็กน้อย:

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

คำเตือน: ฉันอาจกำลังพูดถึง bollocks ที่สมบูรณ์


ขอบคุณ! ฉันคิดว่าคำตอบนี้ก็ถูกต้องเช่นกัน :) แต่เมื่อสามปีที่แล้วเมื่อฉันถามคำถามนี้ "ผสาน" ก็มีเคล็ดลับเช่นกัน!
Tom van Zummeren

8
จากนั้นก็มีคนเข้ามาและพยายามใช้มุมมองที่กำหนดเองของคุณในเลย์เอาต์ที่ใดที่หนึ่งโดยมีเพียงแค่และการโทร<com.example.views.MyView />ของคุณก็เริ่มขว้าง NPE และคุณไม่รู้ว่าทำไม setDataonFinishInflate
Christopher Perry

เคล็ดลับในที่นี้คือการใช้มุมมองแบบกำหนดเองของคุณจากนั้นในตัวสร้างขยายเค้าโครงที่ใช้แท็กผสานเป็นรูท ตอนนี้คุณสามารถใช้ใน XML หรือเพียงแค่สร้างขึ้นใหม่ ครอบคลุมฐานทั้งหมดซึ่งเป็นสิ่งที่คำถาม / คำตอบที่ยอมรับทำร่วมกัน อย่างไรก็ตามสิ่งที่คุณไม่สามารถทำได้คืออ้างถึงเค้าโครงโดยตรงอีกต่อไป ตอนนี้ 'เป็นเจ้าของ' โดยการควบคุมแบบกำหนดเองและควรใช้โดยสิ่งนั้นในตัวสร้างเท่านั้น แต่ถ้าคุณใช้แนวทางนี้ทำไมคุณถึงต้องใช้วิธีนี้ที่อื่นล่ะ? คุณจะไม่
Mark A. Donohoe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.