NullPointerException truy cập dạng xem trong onCreate ()


114

Đây là một câu hỏi chính tắc cho một vấn đề thường xuyên được đăng trên StackOverflow.

Tôi đang làm theo một hướng dẫn. Tôi đã tạo một hoạt động mới bằng trình hướng dẫn. Tôi nhận được NullPointerExceptionkhi cố gắng gọi một phương thức Viewcó được findViewById()trong hoạt động của mình onCreate().

Hoạt động onCreate():

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    View something = findViewById(R.id.something);
    something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE

    if (savedInstanceState == null) {
        getSupportFragmentManager().beginTransaction()
                .add(R.id.container, new PlaceholderFragment()).commit();
    }
}

Bố cục XML ( fragment_main.xml):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="packagename.MainActivity$PlaceholderFragment" >

    <View
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/something" />

</RelativeLayout>

Câu trả lời:


70

Hướng dẫn này có lẽ đã lỗi thời, đang cố gắng tạo một giao diện người dùng dựa trên hoạt động thay vì giao diện người dùng dựa trên phân đoạn được ưu tiên bởi mã do trình hướng dẫn tạo.

Chế độ xem nằm trong bố cục phân đoạn ( fragment_main.xml) chứ không phải trong bố cục hoạt động ( activity_main.xml). onCreate()quá sớm trong vòng đời để tìm thấy nó trong phân cấp chế độ xem hoạt động và a nullđược trả về. Gọi một phương pháp về nullnguyên nhân NPE.

Giải pháp ưu tiên là di chuyển mã đến phân đoạn onCreateView(), gọi findViewById()bố cục phân mảnh tăng cao rootView:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
  View rootView = inflater.inflate(R.layout.fragment_main, container,
      false);

  View something = rootView.findViewById(R.id.something); // not activity findViewById()
  something.setOnClickListener(new View.OnClickListener() { ... });

  return rootView;
}

Lưu ý thêm, bố cục phân mảnh cuối cùng sẽ là một phần của hệ thống phân cấp chế độ xem hoạt động và có thể phát hiện được với hoạt động findViewById()nhưng chỉ sau khi giao dịch phân đoạn đã được chạy. Các giao dịch phân mảnh đang chờ xử lý sẽ được thực hiện super.onStart()sau đó onCreate().


Phần findViewById phải nằm trong onActivityCreate.
Zar E Ahmer

@Nepster Có thể nhưng không nhất thiết phải như vậy.
laalto

Đôi khi hoạt động vẫn chưa được đính kèm vào phân mảnh.
Zar E Ahmer

1
@Nepster Và đó lý do tại sao gọi findViewById()trên rootViewvà không phải trên hoạt động.
laalto

10

Hãy thử OnStart()phương pháp và chỉ sử dụng

View view = getView().findViewById(R.id.something);

hoặc Khai báo bất kỳ Chế độ xem nào bằng getView().findViewByIdphương pháp trongonStart()

Khai báo trình nghe nhấp chuột trên chế độ xem của anyView.setOnClickListener(this);


Đối với tôi, điều này rất hữu ích vì tôi đang cố gắng truy cập một dạng xem anh em bên trong onCreateView của một phân đoạn, nơi phân đoạn được khai báo trong xml. Những quan điểm anh chị em vẫn còn null trong onCreateView vì phụ huynh đã không hoàn thành gia tăng, nhưng trong onStart họ đã :)
Daniel Wilson

3

Cố gắng chuyển các dạng xem truy cập của bạn sang phương thức onViewCreate của phân đoạn vì đôi khi khi bạn cố gắng truy cập các dạng xem trong phương thức onCreate, chúng không được hiển thị tại thời điểm dẫn đến ngoại lệ con trỏ rỗng.

 @Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
     View something = findViewById(R.id.something);
     something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE

     if (savedInstanceState == null) {
           getSupportFragmentManager().beginTransaction()
            .add(R.id.container, new PlaceholderFragment()).commit();
    }
 }

2

Đồng ý, đây là một lỗi điển hình vì mọi người thường không thực sự hiểu cách hoạt động của Fragment khi họ bắt đầu phát triển Android. Để giảm bớt sự nhầm lẫn, tôi đã tạo một mã ví dụ đơn giản mà tôi đã đăng ban đầu trên Ứng dụng bị dừng trong trình giả lập Android , nhưng tôi cũng đăng nó ở đây.

Một ví dụ như sau:

public class ContainerActivity extends FragmentActivity implements ExampleFragment.Callback
{
    @Override
    public void onCreate(Bundle saveInstanceState)
    {
        super.onCreate(saveInstanceState);
        this.setContentView(R.layout.activity_container);
        if (saveInstanceState == null)
        {               
             getSupportFragmentManager().beginTransaction()
                .add(R.id.activity_container_container, new ExampleFragment())
                .addToBackStack(null)
             .commit();
        }
        getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener()
        {
            public void onBackStackChanged()
            {
                int backCount = getSupportFragmentManager().getBackStackEntryCount();
                if (backCount == 0)
                {
                    finish();
                }
            }
        });
    }

    @Override
    public void exampleFragmentCallback()
    {
        Toast.makeText(this, "Hello!", Toast.LENGTH_LONG).show();
    }
}

activity_container.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <FrameLayout
        android:id="@+id/activity_container_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

ExampleFragment:

public class ExampleFragment extends Fragment implements View.OnClickListener
{
    public static interface Callback
    {
        void exampleFragmentCallback();
    }

    private Button btnOne;
    private Button btnTwo;
    private Button btnThree;

    private Callback callback;

    @Override
    public void onAttach(Activity activity)
    {
        super.onAttach(activity);
        try
        {
            this.callback = (Callback) activity;
        }
        catch (ClassCastException e)
        {
            Log.e(this.getClass().getSimpleName(), "Activity must implement Callback interface.", e);
            throw e;
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View rootView = inflater.inflate(R.layout.fragment_example, container, false);

        btnOne = (Button) rootView.findViewById(R.id.example_button_one);
        btnTwo = (Button) rootView.findViewById(R.id.example_button_two);
        btnThree = (Button) rootView.findViewById(R.id.example_button_three);

        btnOne.setOnClickListener(this);
        btnTwo.setOnClickListener(this);
        btnThree.setOnClickListener(this);
        return rootView;
    }

    @Override
    public void onClick(View v)
    {
        if (btnOne == v)
        {
            Toast.makeText(getActivity(), "One.", Toast.LENGTH_LONG).show();
        }
        else if (btnTwo == v)
        {
            Toast.makeText(getActivity(), "Two.", Toast.LENGTH_LONG).show();
        }
        else if (btnThree == v)
        {
            callback.exampleFragmentCallback();
        }
    }
}

gment_example.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <Button
            android:id="@+id/example_button_one"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="30dp"
            android:text="@string/hello" 
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"/>

        <Button
            android:id="@+id/example_button_two"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@+id/example_button_one"
            android:layout_alignRight="@+id/example_button_one"
            android:layout_below="@+id/example_button_one"
            android:layout_marginTop="30dp"
            android:text="@string/hello" />

        <Button
            android:id="@+id/example_button_three"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@+id/example_button_two"
            android:layout_alignRight="@+id/example_button_two"
            android:layout_below="@+id/example_button_two"
            android:layout_marginTop="30dp"
            android:text="@string/hello" />

</RelativeLayout>

Và đó phải là một ví dụ hợp lệ, nó chỉ ra cách bạn có thể sử dụng Hoạt động để hiển thị một Phân đoạn và xử lý các sự kiện trong Phân đoạn đó. Và cả cách giao tiếp với Activity chứa.


1
Ví dụ này sử dụng thư viện android-support-v4 cho FragmentActivity và trình quản lý phân đoạn hỗ trợ.
EpicPandaForce

1
một ví dụ thậm chí hoàn chỉnh hơn là có sẵn tại stackoverflow.com/questions/24840509/...
EpicPandaForce

1
Mặc dù bạn nên sử dụng Ottođể liên lạc thay vì gọi lại và sử dụng Butterknifeđể tiêm lượt xem.
EpicPandaForce

2

Chế độ xem "cái gì đó" nằm trong phân mảnh và không hoạt động, vì vậy thay vì truy cập nó trong hoạt động, bạn phải truy cập nó trong lớp phân mảnh như

Trong PlaceholderFragment.class

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_main, container,
  false);

View something = root .findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... });

return root;
}

2

Bạn đang cố gắng truy cập các phần tử giao diện người dùng trong onCreate()nhưng còn quá sớm để truy cập chúng, vì trong onCreateView()phương thức có thể tạo các khung nhìn phân mảnh . Và onActivityCreated()phương thức đáng tin cậy để xử lý bất kỳ hành động nào trên chúng, vì hoạt động được tải đầy đủ ở trạng thái này.


1

Thêm phần sau vào activity_main.xml của bạn

<fragment
    android:id="@+id/myFragment"
    android:name="packagename.MainActivity$PlaceholderFragment"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >
</fragment>

0

Vì bạn đã khai báo Chế độ xem của mình trong fragment_main.xml, hãy di chuyển đoạn mã đó đến nơi bạn lấy NPE trong onCreateView()phương thức của đoạn. Điều này sẽ giải quyết vấn đề.


0

trong đoạn mã đã đăng ở trên trong câu hỏi, có sự cố: bạn đang sử dụng R.layout.activity_main trong phương thức oncreate, nhưng tên tệp xml là "gment_main.xml ", có nghĩa là bạn đang cố lấy chế độ xem của tệp pixel_main.xml không được hiển thị nên nó đưa ra ngoại lệ con trỏ null. thay đổi mã như:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.fragment_main);// your xml layout ,where the views are

    View something = findViewById(R.id.something);
    something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE

    if (savedInstanceState == null) {
        getSupportFragmentManager().beginTransaction()
                .add(R.id.container, new PlaceholderFragment()).commit();
    }
}

0

Bạn phải nhớ điều quan trọng là: NullPointerException xảy ra khi bạn đã khai báo biến của mình và cố gắng truy xuất giá trị của nó trước khi gán giá trị cho nó.


0

Sử dụng phương thức onViewCreate () bất cứ khi nào sử dụng hoặc gọi các khung nhìn từ các phân đoạn.

override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
      View v = view.findViewById(R.id.whatever)
}

0

Tôi đã có cùng một NullPointerExceptionkhởi tạo trình nghe sau khi gọi findViewById() onCreate()onCreateView()các phương thức.

Nhưng khi tôi sử dụng, onActivityCreated(Bundle savedInstanceState) {...}nó hoạt động. Vì vậy, tôi có thể truy cập GroupViewvà thiết lập trình nghe của mình.

Tôi hy vọng nó sẽ hữu ích.


0

Thư viện phổ biến nhất để tìm các chế độ xem được hầu hết mọi nhà phát triển sử dụng.

ButterKnife

Như tôi có thể, họ là đủ câu trả lời giải thích việc tìm kiếm quan điểm với phương pháp luận phù hợp. Nhưng nếu bạn là nhà phát triển Android và viết mã thường xuyên hàng ngày thì bạn có thể sử dụng dao cắt bơ giúp tiết kiệm rất nhiều thời gian trong việc tìm kiếm lượt xem và bạn không phải viết mã cho nó, chỉ với 2-3 bước bạn có thể tìm thấy lượt xem trong mili giây .

Thêm phụ thuộc vào gradle cấp ứng dụng:

implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

Thêm plugin cho dao cắt bơ:

File -> Settings -> plugins-> 

Sau đó, tìm kiếm Android ButterKnife Zelezny và cài đặt plugin và khởi động lại studio của bạn và bạn đã hoàn tất.

Bây giờ, chỉ cần chuyển đến phương thức Oncreate của hoạt động của bạn và nhấp chuột phải vào layout_name của bạn và nhấn vào nút tạo và chọn tùy chọn tiêm butterknife và các tham chiếu chế độ xem của bạn sẽ được tự động tạo như đề cập bên dưới:

    @BindView(R.id.rv_featured_artist)
    ViewPager rvFeaturedArtist;
    @BindView(R.id.indicator)
    PageIndicator indicator;
    @BindView(R.id.rv_artist)
    RecyclerView rvArtist;
    @BindView(R.id.nsv)
    NestedScrollingView nsv;
    @BindView(R.id.btn_filter)
    Button btnFilter;
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.