findViewById () trả về null cho thành phần tùy chỉnh trong XML bố cục, không cho các thành phần khác


91

Tôi có một res/layout/main.xmlbao gồm các yếu tố này và những yếu tố khác:

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

Trong onCreate của Activity của tôi, tôi thực hiện điều này:

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

Các phần tử khác được tìm thấy thành công, nhưng footrả về giá trị rỗng. MyCustomView có một hàm tạo MyCustomView(Context c, AttributeSet a)Log.d(...)ở cuối hàm tạo đó xuất hiện thành công trong logcat ngay trước khi "lỗi sử thi".

Tại sao là foonull?

Câu trả lời:


182

Bởi vì trong hàm tạo, tôi đã có super(context)thay vì super(context, attrs).

Có lý, nếu bạn không chuyển các thuộc tính, chẳng hạn như id, thì chế độ xem sẽ không có id và do đó không thể tìm thấy bằng id đó. :-)


1
Luôn rất vui khi có thể trả lời các câu hỏi của chính bạn :) Hãy nhớ đánh dấu câu trả lời của bạn là câu trả lời được chấp nhận.
MattC

Thật. Sẽ làm như vậy khi SO phép tôi ( "Bạn có thể chấp nhận câu trả lời của riêng bạn trong 2 ngày.")
Chris Boyle

4
Ngoài ra, không nên bạn dòng thích (MyCustomView) foo = findViewById(R.id.foo);được MyCustomView foo = (MyCustomView) findViewById(R.id.foo);?
Jeremy Logan

3
Đã cùng một vấn đề, trong trường hợp của tôi, tôi đã quên setContentView () .. XD
Tom Brito

Có một ví dụ tốt đẹp để làm những việc như vậy trên vogella.com: vogella.com/articles/AndroidCustomViews/article.html
Wolkenjaeger

27

Tôi gặp vấn đề tương tự vì trong chế độ xem tùy chỉnh của mình, tôi đã ghi đè hàm tạo nhưng đã gọi siêu hàm tạo không có tham số attrs. Đó là copy paste)

Phiên bản hàm tạo trước đây của tôi:

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

Bây giờ tôi có:

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

Và điều đó hoạt động!


Vấn đề tương tự ở đây. Thật tình cờ, đó là một lỗi sao chép dán đối với tôi.
KurtCobain

Điều tương tự đã xảy ra với tôi. Câu trả lời đúng nhất trên Stackoverflow. Khi thêm phần đính kèm AttributeSet trở lại, mọi thứ đều ổn.
spikeyang

Tôi đoán tất cả chúng ta nhìn lên cùng một hướng dẫn với giao diện tùy chỉnh;) lỗi này đưa tôi 0,5h gỡ lỗi vô nghĩa ...
KrwawyKefir

Điều này cũng làm việc cho tôi! Tôi đã chiến đấu với điều này trong một thời gian khá dài. Cảm ơn!
us_david

18

Tôi đã từng gặp vấn đề tương tự. Sai lầm của tôi là: Tôi đã viết

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

và khi tôi sử dụng một bộ khuếch đại để "tải" chế độ xem từ tệp XML, dòng cuối cùng bị sai. Để giải quyết nó, tôi phải viết:

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

Tôi đã viết giải pháp của mình, trong trường hợp ai đó gặp vấn đề tương tự.


Dude, bạn là một người tài giỏi. Đó là giải pháp duy nhất làm việc cho tôi ra khỏi tất cả những người vô tôi đọc trên SO
IgorGanapolsky

Đây cũng là vấn đề đối với tôi. Chà, thật là một phần của ... Tôi sẽ mất nhiều ngày để tự mình tìm ra điều này. Cảm ơn bạn!
poshaughnessy

18

Có vẻ như có nhiều lý do. Tôi vừa sử dụng "Clean ..." trong Eclipse để giải quyết vấn đề tương tự. (FindViewByID đã hoạt động trước đó và vì một số lý do bắt đầu trả về null.)


1
rõ ràng, vấn đề cơ bản là ID R.java bằng cách nào đó bị hỏng hoặc có thể không được cập nhật. Tôi đã nhận thấy điều này không chỉ với ID mà còn trong các trường hợp khác, ví dụ như chuỗi sai được hiển thị trong TextView, v.v. Tôi không thực sự biết tại sao điều này lại xảy ra.
sứa

Điều này đã khiến tôi đau buồn quá lâu - sự sạch sẽ thực sự đã sửa chữa nó cho tôi.
Nicholas MT Elliott

1
Dọn dẹp, thực sự. Chà, thật tệ!
Tim Büthe

11

Cùng một vấn đề, nhưng giải pháp khác nhau: Tôi đã không gọi

setContentView(R.layout.main)

TRƯỚC KHI tôi cố gắng tìm chế độ xem như đã nêu ở đây


Tôi nghĩ đây là giải pháp nếu bạn đang nhận được một phần tử trên chế độ xem khác thay vì chế độ xem liên quan hiện tại.
StarCub

4

Nếu bạn có nhiều phiên bản bố cục (tùy thuộc vào mật độ màn hình, phiên bản SDK), hãy đảm bảo rằng tất cả chúng đều bao gồm phần tử bạn đang tìm kiếm.


2

Trong trường hợp của tôi, findViewById trả về null vì chế độ xem tùy chỉnh của tôi trông giống như thế này trong XML chính:

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

và tôi phát hiện ra rằng khi tôi thêm nội dung xmlns, nó hoạt động như thế này:

        <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" 
            />

2

Hãy chắc chắn rằng setContentView(R.layout.main)câu lệnh gọi trước findViewById(...)câu lệnh;


1

Đối với tôi, vấn đề đã được giải quyết khi tôi thêm thư mục res vào Nguồn trong Đường dẫn xây dựng Java trong Cài đặt dự án.


1

Tôi đã gặp phải vấn đề tương tự một lúc trước khi tôi thêm Chế độ xem tùy chỉnh qua XML bố cục và sau đó cố gắng đính kèm một lệnh gọi lại ở nơi khác trong ứng dụng ...

Tôi đã tạo một chế độ xem tùy chỉnh và thêm nó vào "layout_main.xml" của mình

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

Và trong Hoạt động chính, tôi muốn đính kèm một số lệnh gọi lại và nhận tham chiếu đến các phần tử giao diện người dùng từ 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);
    }       
}

Trình khởi tạo không làm bất cứ điều gì lạ mắt nhưng bất kỳ thay đổi nào mà nó cố gắng thực hiện đối với chế độ xem tùy chỉnh (MUIComponent) hoặc các phần tử giao diện người dùng không tùy chỉnh khác chỉ đơn giản là không xuất hiện trong ứng dụng.

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

}

Sự khác biệt giữa "badInst" và "goodInst" là:

  • badInst sử dụng findViewByID của Activity
  • goodInst làm phồng bố cục và sử dụng bố cục phồng để thực hiện tra cứu

Chú ý Vincent có cùng một giải pháp ... và câu trả lời của mình ngắn hơn ... 1 thay vì mình :)
DevByStarlight

1

Điều này đã xảy ra với tôi với một thành phần tùy chỉnh cho Wear, nhưng là một lời khuyên chung. Nếu bạn đang sử dụng Stub (chẳng hạn như tôi đang sử dụng WatchViewStub), bạn không thể chỉ thực hiện cuộc gọi đến findViewById()bất cứ đâu. Mọi thứ bên trong sơ khai phải được thổi phồng trước, điều này không chỉ xảy ra sau đó setContentView(). Vì vậy, bạn nên viết một cái gì đó như thế này để chờ điều đó xảy ra:

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

Vấn đề của tôi là một lỗi đánh máy. Tôi đã viết android.id(dấu chấm) thay vìandroid:id . : P

Rõ ràng không có kiểm tra cú pháp trong xml thành phần tùy chỉnh của tôi. :(


0

Có cùng một vấn đề.

Tôi đã bố trí với ít trẻ em. Từ phương thức khởi tạo của một trong số chúng, tôi đang cố gắng lấy tham chiếu (bằng cách sử dụng context.findViewById) đến con khác. Nó không hoạt động vì con thứ hai được xác định thêm trong bố cục.

Tôi đã giải quyết nó như thế này:

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

Nó cũng sẽ hoạt động nếu thứ tự của trẻ em ngược lại, nhưng tôi đoán nó thường được thực hiện như trên.


1
Nói chung, bạn không nên sử dụng findViewByIdhàm tạo của a View, mà hãy đặt mã khởi tạo vào OnFinishInflate?
Sanjay Manohar

0

Các findViewById()phương pháp đôi khi trả về nullkhi gốc rễ của cách bố trí không có android:idthuộc tính. Trình hướng dẫn Eclipse để tạo tệp xml bố cục không tự động tạo android:idthuộc tính cho phần tử gốc.


0

Trong trường hợp của tôi, chế độ xem ở dạng chính KHÔNG ở dạng xem mà tôi đang cố gọi nó vào. Vì vậy, trong dạng xem con, tôi phải gọi:

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

0

Tùy chọn 'sạch' phù hợp với tôi.

Trong trường hợp của tôi, nguyên nhân gốc rễ là do mã nguồn nằm trên một mạng chia sẻ và máy trạm và máy chủ tệp của tôi không được đồng bộ hóa chính xác và đã bị trôi đi 5 giây. Dấu thời gian trên các tệp được tạo bởi Eclipse là trong quá khứ (vì chúng được chỉ định bởi máy chủ tệp) làm giảm đồng hồ của máy trạm, khiến Eclipse giải quyết không chính xác sự phụ thuộc giữa tệp nguồn và tệp được tạo. Trong trường hợp này, phiên bản 'sạch' có vẻ hoạt động vì nó buộc phải xây dựng lại hoàn toàn thay vì một bản dựng tăng dần phụ thuộc vào dấu thời gian sai.

Sau khi tôi sửa cài đặt NTP trên máy trạm của mình, sự cố không bao giờ xảy ra nữa. Nếu không có cài đặt NTP thích hợp, nó sẽ xảy ra vài giờ một lần, vì đồng hồ trôi nhanh.


Nên thêm cái này vào bình luận của câu trả lời ở trên
Trung Nguyen

0

Để thêm một lỗi nhỏ khác vào các câu trả lời cần tìm:

Kiểm tra xem bạn có thực sự đang chỉnh sửa tệp XML có bố cục phù hợp không ...


0

tôi đã gặp vấn đề tương tự vì tôi quên cập nhật id chế độ xem trong tất cả các thư mục bố cục của mình.

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.