findViewById () คืนค่า null สำหรับคอมโพเนนต์ที่กำหนดเองใน XML เค้าโครงไม่ใช่สำหรับคอมโพเนนต์อื่น ๆ


92

ฉันมีres/layout/main.xmlองค์ประกอบเหล่านี้และอื่น ๆ :

<some.package.MyCustomView android:id="@+id/foo" (some other params) />
<TextView android:id="@+id/boring" (some other params) />

ในกิจกรรม onCreate ของฉันฉันทำสิ่งนี้:

setContentView(R.layout.main);
TextView boring = (TextView) findViewById(R.id.boring);
// ...find other elements...
MyCustomView foo = (MyCustomView) findViewById(R.id.foo);
if (foo == null) { Log.d(TAG, "epic fail"); }

พบองค์ประกอบอื่น ๆ สำเร็จ แต่fooกลับมาเป็นโมฆะ MyCustomView มีตัวสร้างMyCustomView(Context c, AttributeSet a)และLog.d(...)ที่ส่วนท้ายของตัวสร้างนั้นจะปรากฏสำเร็จใน logcat ก่อนที่ "มหากาพย์ล้มเหลว"

เหตุใดจึงเป็นfooโมฆะ

คำตอบ:


183

เพราะในตัวสร้างฉันมีsuper(context)แทนsuper(context, attrs).

สมเหตุสมผลถ้าคุณไม่ส่งผ่านในแอตทริบิวต์เช่น id มุมมองจะไม่มี id ดังนั้นจึงไม่สามารถค้นหาได้โดยใช้ id นั้น :-)


1
ยินดีเสมอที่สามารถตอบคำถามของคุณเอง :) อย่าลืมทำเครื่องหมายว่าเป็นคำตอบที่ยอมรับด้วย
MattC

แน่นอน. จะทำเช่นนั้นเมื่อ SO ให้ฉัน ("คุณสามารถยอมรับคำตอบของคุณเองได้ใน 2 วัน")
Chris Boyle

4
นอกจากนี้คุณไม่ควรบรรทัดเช่น(MyCustomView) foo = findViewById(R.id.foo);be MyCustomView foo = (MyCustomView) findViewById(R.id.foo);?
Jeremy Logan

3
มีปัญหาเดียวกันในกรณีของฉันฉันลืม setContentView () .. XD
Tom Brito

มีตัวอย่างที่ดีในการทำสิ่งเหล่านี้บน vogella.com: vogella.com/articles/AndroidCustomViews/article.html
Wolkenjaeger

27

ฉันมีปัญหาเดียวกันเพราะในมุมมองที่กำหนดเองของฉันฉันได้ลบล้างตัวสร้าง แต่เรียกใช้ตัวสร้างขั้นสูงด้วยตัวกำหนดค่าพารามิเตอร์ นั่นคือการคัดลอกวาง)

เวอร์ชันตัวสร้างก่อนหน้าของฉัน:

    public TabsAndFilterRelativeLayout(Context context, AttributeSet attrs) {
            super(context);
}

ตอนนี้ฉันมี:

    public TabsAndFilterRelativeLayout(Context context, AttributeSet attrs) {
            super(context, attrs);}

และได้ผล!


ปัญหาเดียวกันที่นี่ บังเอิญมันเป็นข้อผิดพลาดในการคัดลอกวางสำหรับฉันด้วย
KurtCobain

สิ่งเดียวกันเกิดขึ้นกับฉัน คำตอบที่ถูกต้องที่สุดใน Stackoverflow เมื่อเพิ่ม AttributeSet attrs กลับทุกอย่างเรียบร้อยดี
spikeyang

ฉันเดาว่าเราทุกคนค้นหาบทช่วยสอนเดียวกันด้วยมุมมองที่กำหนดเอง) ข้อผิดพลาดนี้ทำให้ฉันใช้เวลา 0,5 ชั่วโมงในการดีบักแบบไม่มีจุดหมาย ...
KrwawyKefir

สิ่งนี้ได้ผลสำหรับฉันเช่นกัน! ฉันต่อสู้กับมันมาระยะหนึ่งแล้ว ขอบคุณ!
us_david

18

ผมมีปัญหาเหมือนกัน. ความผิดพลาดของฉันคือ: ฉันเขียน

        LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View layout=inflater.inflate(R.layout.dlg_show_info, null);
        alertDlgShowInfo.setView(layout);
        TableRow trowDesc=(TableRow)findViewById(R.id.trowDesc);

และเมื่อฉันใช้ Inflater เพื่อ "โหลด" มุมมองจากไฟล์ XML บรรทัดสุดท้ายก็ผิด เพื่อแก้ปัญหานี้ฉันต้องเขียน:

TableRow trowDesc=(TableRow)layout.findViewById(R.id.trowDesc);

ฉันเขียนวิธีแก้ปัญหาของฉันในกรณีที่มีคนมีปัญหาเดียวกัน


เพื่อนคุณเป็นคนใจดี นั่นเป็นทางออกเดียวที่ได้ผลสำหรับฉันจากสิ่งต่างๆมากมายที่ฉันอ่านใน SO
IgorGanapolsky

นี่เป็นปัญหาสำหรับฉันเช่นกัน ว้าวช่างเป็นอะไรที่ ... ฉันต้องใช้เวลาหลายวันในการคิดออกด้วยตัวเอง ขอบคุณ!
poshaughnessy

18

ดูเหมือนจะมีหลายสาเหตุ ฉันเพิ่งใช้ "Clean ... " ใน Eclipse เพื่อแก้ปัญหาที่คล้ายกัน (FindViewByID เคยทำงานมาก่อนและด้วยเหตุผลบางอย่างจึงเริ่มคืนค่าว่าง)


1
เห็นได้ชัดว่าปัญหาพื้นฐานคือรหัส R.java เสียหรืออาจไม่ได้รับการอัปเดต ฉันสังเกตเห็นสิ่งนี้ไม่เพียง แต่กับ ID เท่านั้น แต่ในกรณีอื่น ๆ เช่นสตริงผิดที่แสดงใน TextView เป็นต้นไม่รู้ว่าทำไมถึงเกิดเหตุการณ์นี้ขึ้น
แมงกะพรุน

สิ่งนี้ทำให้ฉันเศร้าโศกนานเกินไป - ความสะอาดช่วยแก้ไขให้ฉันได้อย่างแท้จริง
Nicholas MT Elliott

1
ล้างข้อมูลแน่นอน ว้าว!
Tim Büthe

11

ปัญหาเดียวกัน แต่วิธีแก้ไขต่างกัน: ฉันไม่ได้โทร

setContentView(R.layout.main)

ก่อนที่ฉันจะพยายามค้นหามุมมองตามที่ระบุไว้ที่นี่


ฉันคิดว่านี่เป็นวิธีแก้ปัญหาหากคุณได้รับองค์ประกอบในมุมมองอื่นแทนที่จะเป็นมุมมองที่เกี่ยวข้องในปัจจุบัน
StarCub

4

หากคุณมีเลย์เอาต์หลายเวอร์ชัน (ขึ้นอยู่กับความหนาแน่นของหน้าจอเวอร์ชัน SDK) ตรวจสอบให้แน่ใจว่าเวอร์ชันทั้งหมดมีองค์ประกอบที่คุณกำลังมองหา


2

ในกรณีของฉัน findViewById ส่งคืนค่าว่างเนื่องจากมุมมองที่กำหนดเองของฉันมีลักษณะเช่นนี้ใน XML หลัก:

        <com.gerfmarquez.seekbar.VerticalSeekBar  
            android:id="@+id/verticalSeekBar"
            android:layout_width="wrap_content" 
            android:layout_height="fill_parent" 
            />

และฉันพบว่าเมื่อฉันเพิ่มสิ่ง xmlns มันทำงานเช่นนี้:

        <com.gerfmarquez.seekbar.VerticalSeekBar  
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/verticalSeekBar"
            android:layout_width="wrap_content" 
            android:layout_height="fill_parent" 
            />


1

สำหรับฉันปัญหาได้รับการแก้ไขเมื่อฉันเพิ่มโฟลเดอร์ res ไปยัง Source ใน Java Build Path ในการตั้งค่าโปรเจ็กต์


1

ฉันพบปัญหาเดียวกันในขณะที่ฉันเพิ่มมุมมองที่กำหนดเองผ่าน XML เค้าโครงแล้วพยายามแนบการโทรกลับที่อื่นในแอปพลิเคชัน ...

ฉันสร้างมุมมองที่กำหนดเองและเพิ่มลงใน "layout_main.xml" ของฉัน

public class MUIComponent extends SurfaceView implements SurfaceHolder.Callback {
    public MUIComponent (Context context, AttributeSet attrs ) {
        super ( context, attrs );
    }
    // ..
}

และในกิจกรรมหลักฉันต้องการแนบการเรียกกลับและรับการอ้างอิงถึงองค์ประกอบ UI จาก XML

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // ...

        MUIInitializer muiInit = new MUIInitializer();
        muiInit.setupCallbacks(this);
        muiInit.intializeFields(this);
    }       
}

ตัวเริ่มต้นไม่ได้ทำอะไรแปลก ๆ แต่การเปลี่ยนแปลงใด ๆ ที่พยายามทำกับมุมมองแบบกำหนดเอง (MUIComponent) หรือองค์ประกอบ UI ที่ไม่ได้กำหนดเองอื่น ๆก็ไม่ปรากฏในแอปพลิเคชัน

public class MUIInitializer {

    // ...

    public void setupCallbacks ( Activity mainAct ) {


        // This does NOT work properly
        // - The object instance returned is technically an instance of my "MUICompnent" view
        //   but it is a *different* instance than the instance created and shown in the UI screen
        // - Callbacks never get triggered, changes don't appear on UI, etc.
        MUIComponent badInst = (MUIComponent) mainAct.findViewById(R.id.MUIComponent_TESTSURF);


        // ...
        // This works properly

        LayoutInflater inflater = (LayoutInflater) mainAct.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View inflatedLayout = inflater.inflate ( R.layout.activity_main, null );

        MUIComponent goodInst = (MUIComponent) inflatedLayout.findViewById(R.id.MUIComponent_TESTSURF);


        // Add callbacks
        // ...
    }

}

ความแตกต่างระหว่าง "badInst" และ "goodInst" คือ:

  • badInst ใช้ findViewByID ของกิจกรรม
  • goodInst ขยายเค้าโครงและใช้เค้าโครงที่สูงเกินจริงเพื่อทำการค้นหา

สังเกตเห็น Vincent มีวิธีแก้ปัญหาเหมือนกัน ... และคำตอบของเขาสั้นลง ... +1 แทน :)
DevByStarlight

1

สิ่งนี้เกิดขึ้นกับฉันด้วยส่วนประกอบที่กำหนดเองสำหรับ Wear แต่เป็นคำแนะนำทั่วไป หากคุณกำลังใช้ Stub (เช่นที่ฉันใช้WatchViewStub) คุณจะไม่สามารถโทรไปfindViewById()ที่ไหนก็ได้ ทุกอย่างที่อยู่ในต้นขั้วจะต้องพองตัวก่อนซึ่งจะไม่เกิดขึ้นหลังจากsetContentView()นั้น ดังนั้นคุณควรเขียนสิ่งนี้เพื่อรอให้สิ่งนั้นเกิดขึ้น:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_wear);
    final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
    stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
        @Override
        public void onLayoutInflated(WatchViewStub stub) {
            myCustomViewInst = (MyCustomView) findViewById(R.id.my_custom_view);
            ...

0

ปัญหาของฉันคือการพิมพ์ผิด ฉันเขียนandroid.id(dot) แทนandroid:id. : ป

เห็นได้ชัดว่าไม่มีการตรวจสอบไวยากรณ์ภายใน xml คอมโพเนนต์ที่กำหนดเองของฉัน :(


0

มีปัญหาเดียวกัน

ฉันมีเค้าโครงกับเด็กไม่กี่คน จากคอนสตรัคเตอร์ของสิ่งหนึ่งฉันพยายามขอการอ้างอิง (โดยใช้ context.findViewById) ไปยังเด็กคนอื่น ไม่ทำงานเนื่องจากลูกคนที่สองถูกกำหนดเพิ่มเติมในเค้าโครง

ฉันได้แก้ไขแล้วดังนี้:

setContentView(R.layout.main);
MyView first = findViewById(R.layout.first_child);
first.setSecondView(findViewById(R.layout.second_child));

มันจะได้ผลเช่นกันถ้าคำสั่งของเด็ก ๆ อยู่ตรงข้ามกัน แต่ฉันเดาว่าโดยทั่วไปควรทำเหมือนข้างบน


1
โดยทั่วไปคุณไม่ควรใช้findViewByIdในตัวสร้างของ a Viewแต่ควรใส่รหัสเริ่มต้นในOnFinishInflate?
Sanjay Manohar

0

findViewById()วิธีการบางครั้งผลตอบแทนnullเมื่อรากของรูปแบบไม่มีandroid:idแอตทริบิวต์ วิซาร์ด Eclipse สำหรับการสร้างไฟล์ xml เลย์เอาต์ไม่สร้างandroid:idแอ็ตทริบิวต์สำหรับอิลิเมนต์รูทโดยอัตโนมัติ


0

ในกรณีของฉันมุมมองอยู่ในพาเรนต์ไม่ได้อยู่ในมุมมองที่ฉันพยายามเรียกมันเข้ามาดังนั้นในมุมมองเด็กฉันต้องเรียก:

RelativeLayout relLayout = (RelativeLayout) this.getParent();
View view1 = relLayout.findViewById(R.id.id_relative_layout_search);

0

ตัวเลือก 'สะอาด' ใช้ได้ผลสำหรับฉัน

ในกรณีของฉันสาเหตุที่แท้จริงคือซอร์สโค้ดอยู่บนเครือข่ายที่ใช้ร่วมกันและเวิร์กสเตชันและเซิร์ฟเวอร์ไฟล์ของฉันไม่ได้รับการซิงโครไนซ์อย่างถูกต้องและลอยไป 5 วินาที การประทับเวลาบนไฟล์ที่สร้างโดย Eclipse เป็นอดีต (เนื่องจากถูกกำหนดโดยเซิร์ฟเวอร์ไฟล์) เขียนนาฬิกาของเวิร์กสเตชันทำให้ Eclipse แก้ไขการอ้างอิงระหว่างไฟล์ที่สร้างและไฟล์ต้นทางไม่ถูกต้อง ในกรณีนี้คำว่า 'สะอาด' ดูเหมือนจะใช้งานได้เนื่องจากบังคับให้สร้างใหม่ทั้งหมดแทนที่จะเป็นการสร้างแบบเพิ่มหน่วยซึ่งขึ้นอยู่กับการประทับเวลาที่ไม่ถูกต้อง

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


ควรเพิ่มสิ่งนี้ในความคิดเห็นของคำตอบด้านบน
Trung Nguyen

0

หากต้องการเพิ่มข้อผิดพลาดเล็กน้อยให้กับคำตอบที่ต้องระวัง:

ตรวจสอบว่าคุณกำลังแก้ไขไฟล์ XML เค้าโครงที่ถูกต้องจริงๆ ...


0

ฉันมีปัญหาเดียวกันเพราะฉันลืมอัปเดต id มุมมองในโฟลเดอร์เลย์เอาต์ทั้งหมดของฉัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.