findViewById () mengembalikan null untuk komponen kustom dalam XML layout, bukan untuk komponen lain

93

Saya memiliki res/layout/main.xmltermasuk elemen ini dan lainnya:

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

Di onCreate Aktivitas saya, saya melakukan ini:

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

Unsur-unsur lain berhasil ditemukan, tetapi fookembali nol. MyCustomView memiliki konstruktor MyCustomView(Context c, AttributeSet a)dan Log.d(...)di akhir konstruktor tersebut berhasil muncul di logcat tepat sebelum "epik gagal".

Mengapa foonull?

Chris Boyle
sumber

Jawaban:

184

Karena di konstruktor, saya super(context)malah super(context, attrs).

Masuk akal, jika Anda tidak meneruskan atribut, seperti id, maka tampilan tidak akan memiliki id dan oleh karena itu tidak dapat ditemukan menggunakan id itu. :-)

Chris Boyle
sumber
1
Selalu senang bisa menjawab pertanyaan Anda sendiri :) Pastikan untuk menandai milik Anda sebagai jawaban yang diterima juga.
MattC
Memang. Akan melakukannya jika SO mengizinkan saya ("Anda dapat menerima jawaban Anda sendiri dalam 2 hari.")
Chris Boyle
4
Juga, bukankah seharusnya Anda berbaris seperti (MyCustomView) foo = findViewById(R.id.foo);itu MyCustomView foo = (MyCustomView) findViewById(R.id.foo);?
Jeremy Logan
3
Punya masalah yang sama, dalam kasus saya, saya lupa setContentView () .. XD
Tom Brito
Ada contoh yang bagus untuk melakukan hal-hal seperti itu di vogella.com: vogella.com/articles/AndroidCustomViews/article.html
Wolkenjaeger
27

Saya memiliki masalah yang sama karena dalam tampilan kustom saya, saya mengganti konstruktor tetapi memanggil super contrs dengan paramete attrs. Itu adalah copy paste)

Versi konstruktor saya sebelumnya:

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

Sekarang saya punya:

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

Dan itu berhasil!

Alexander Korolchuk
sumber
Masalah yang sama di sini. Kebetulan itu juga kesalahan copy paste untuk saya.
KurtCobain
Hal yang sama terjadi pada saya. Jawaban paling benar di Stackoverflow. Saat menambahkan atribut AttributeSet kembali, semuanya baik-baik saja.
spikeyang
Saya kira kita semua mencari tutorial yang sama dengan tampilan kustom;) kesalahan ini membawa saya 0,5 jam debugging tidak berguna ...
KrwawyKefir
Ini berhasil juga untuk saya! Saya berjuang melawan ini untuk beberapa waktu. Terima kasih!
us_david
18

Saya memiliki masalah yang sama. Kesalahan saya adalah: Saya menulis

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

dan karena saya menggunakan inflater untuk "memuat" tampilan dari file XML, baris terakhir salah. Untuk mengatasinya, saya harus menulis:

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

Saya menulis solusi saya, jika seseorang memiliki masalah yang sama.

Vincent
sumber
Bung, Anda seorang yang jenius. Itulah satu-satunya solusi yang berhasil bagi saya dari semua yang saya baca di SO
IgorGanapolsky
Ini juga masalah saya. Wow, betapa hebatnya ... Aku butuh waktu berhari-hari untuk memikirkannya sendiri. Terima kasih!
poshaughnessy
18

Sepertinya ada berbagai alasan. Saya baru saja menggunakan "Bersihkan ..." di Eclipse untuk memecahkan masalah serupa. (FindViewByID telah bekerja sebelumnya dan karena alasan tertentu mulai mengembalikan null.)

ubur-ubur
sumber
1
tampaknya, masalah yang mendasarinya adalah bahwa ID R.java entah bagaimana bisa rusak atau mungkin tidak diperbarui. Saya telah memperhatikan ini tidak hanya dengan ID, tetapi juga dalam kasus lain, misalnya string yang salah ditampilkan di TextView dll. Tidak benar-benar tahu mengapa hal ini terjadi.
ubur
Ini membuat saya berduka terlalu lama - pembersihan memang memperbaikinya untuk saya.
Nicholas MT Elliott
1
Pembersihan, memang. Wow, menyebalkan!
Tim Büthe
11

Masalah yang sama, tetapi solusi yang berbeda: Saya tidak menelepon

setContentView(R.layout.main)

SEBELUM saya mencoba mencari pemandangan seperti yang tertera di sini

Daniel
sumber
Saya rasa ini adalah solusi jika Anda mendapatkan elemen di tampilan lain, bukan tampilan terkait saat ini.
StarCub
4

Jika Anda memiliki beberapa versi tata letak (bergantung pada kepadatan layar, versi SDK), pastikan semuanya menyertakan elemen yang Anda cari.

MQ
sumber
2

Dalam kasus saya findViewById mengembalikan null karena tampilan kustom saya terlihat seperti ini di XML utama:

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

dan saya menemukan bahwa ketika saya menambahkan hal-hal xmlns itu bekerja seperti ini:

        <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" 
            />
gerfmarquez
sumber
2

Pastikan bahwa setContentView(R.layout.main)pernyataan memanggil sebelum findViewById(...)pernyataan;

Tukang batu
sumber
1

Bagi saya, masalah terpecahkan ketika saya menambahkan folder res ke Source di Java Build Path di Project Settings.

Jiwon Park
sumber
1

Saya mengalami masalah yang sama beberapa waktu yang lalu ketika saya menambahkan Tampilan kustom melalui XML tata letak dan kemudian mencoba memasang callback di tempat lain dalam aplikasi ...

Saya membuat tampilan kustom dan menambahkannya ke "layout_main.xml" saya

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

Dan di Aktivitas utama saya ingin melampirkan beberapa callback dan mendapatkan referensi ke elemen UI dari 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);
    }       
}

Initilizer tidak melakukan sesuatu yang mewah tetapi perubahan apa pun yang dicoba dilakukan pada tampilan kustom (MUIComponent) atau elemen UI non-kustom lainnya tidak muncul dalam aplikasi.

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
        // ...
    }

}

Perbedaan antara "badInst" dan "goodInst" adalah:

  • badInst menggunakan findViewByID Aktivitas
  • goodInst memekarkan tata letak dan menggunakan tata letak yang diperbesar untuk melakukan pencarian
DevByStarlight
sumber
Melihat Vincent memiliki solusi yang sama ... dan jawabannya lebih pendek ... +1 sebagai gantinya :)
DevByStarlight
1

Ini terjadi pada saya dengan komponen khusus untuk Wear, tetapi merupakan saran umum. Jika Anda menggunakan Stub (seperti yang saya gunakan WatchViewStub), Anda tidak bisa begitu saja menelepon ke findViewById()mana pun. Segala sesuatu di dalam rintisan harus digelembungkan terlebih dahulu, yang tidak terjadi begitu saja setContentView(). Jadi, Anda harus menulis sesuatu seperti ini untuk menunggu itu terjadi:

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);
            ...
Stephen Wylie
sumber
0

Masalah saya adalah salah ketik. Saya telah menulis android.id(titik), bukan android:id. : P

Rupanya tidak ada pemeriksaan sintaks dalam komponen kustom saya xml. :(

JOG
sumber
0

Punya masalah yang sama.

Saya memiliki tata letak dengan sedikit anak. Dari konstruktor salah satunya saya mencoba mendapatkan referensi (dengan menggunakan context.findViewById) ke anak lain. Itu tidak berfungsi karena anak kedua didefinisikan lebih jauh dalam tata letak.

Saya telah menyelesaikannya seperti ini:

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

Ini akan berhasil juga jika urutan anak-anak berlawanan, tapi saya rasa umumnya harus dilakukan seperti di atas.

Kangur
sumber
1
Secara umum Anda tidak boleh menggunakan findViewByIddalam konstruktor a View, melainkan meletakkan kode inisialisasi di OnFinishInflate?
Sanjay Manohar
0

The findViewById()Metode kadang-kadang kembali nullketika akar tata letak tidak memiliki android:idatribut. Wizard Eclipse untuk membuat file xml layout tidak secara otomatis menghasilkan android:idatribut untuk elemen root.

Santosh
sumber
0

Dalam kasus saya, tampilan berada di induk BUKAN dalam tampilan yang saya coba panggil. Jadi dalam tampilan anak saya harus memanggil:

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

Opsi 'bersih' berhasil untuk saya.

Dalam kasus saya, akar penyebabnya adalah bahwa kode sumber berada di jaringan bersama, dan workstation serta server file saya tidak disinkronkan dengan benar, dan telah melayang selama 5 detik. Stempel waktu pada file yang dibuat oleh Eclipse berada di masa lalu (karena mereka ditetapkan oleh server file) membuat jam stasiun kerja, menyebabkan Eclipse menyelesaikan dependensi antara file yang dibuat dan file sumber secara tidak benar. Dalam kasus ini, 'clean' tampaknya berfungsi, karena memaksa build ulang lengkap, bukan build inkremental yang bergantung pada stempel waktu yang salah.

Setelah saya memperbaiki pengaturan NTP di workstation saya, masalah tidak pernah terjadi lagi. Tanpa pengaturan NTP yang tepat, itu akan terjadi setiap beberapa jam, karena jam bergerak cepat.

diter
sumber
Harus menambahkan ini ke komentar jawaban di atas
Trung Nguyen
0

Untuk menambahkan kesalahan sepele lainnya ke jawaban yang harus diperhatikan:

Periksa apakah Anda benar-benar mengedit file XML tata letak yang benar ...

blub
sumber
0

saya memiliki masalah yang sama karena saya lupa memperbarui id tampilan di semua folder tata letak saya.

Shanij PS
sumber