NullPointerException z dostępem do widoków w onCreate ()


114

To jest kanoniczne pytanie dotyczące problemu często publikowanego w witrynie StackOverflow.

Postępuję zgodnie z tutorialem. Utworzyłem nową aktywność za pomocą kreatora. Otrzymuję NullPointerException, próbując wywołać metodę na Views uzyskanych findViewById()w mojej aktywności onCreate().

Aktywność 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();
    }
}

Układ 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>

Odpowiedzi:


70

Samouczek jest prawdopodobnie przestarzały i zawiera próbę utworzenia interfejsu użytkownika opartego na aktywności zamiast interfejsu użytkownika opartego na fragmentach preferowanego przez kod generowany przez kreatora.

Widok znajduje się w układzie fragmentów ( fragment_main.xml), a nie w układzie działania ( activity_main.xml). onCreate()znajduje się na zbyt wczesnym etapie cyklu życia, aby znaleźć go w hierarchii widoku działań, i nulljest zwracany a. Wywołanie metody na nullpowoduje NPE.

Preferowanym rozwiązaniem jest przeniesienie kodu do fragmentu onCreateView(), wywołując findViewById()zawyżony układ fragmentu 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;
}

Na marginesie, układ fragmentów będzie ostatecznie częścią hierarchii widoku działania i będzie wykrywalny za pomocą działań, findViewById()ale dopiero po uruchomieniu transakcji fragmentu. Oczekujące transakcje fragmentowe są wykonywane super.onStart()po onCreate().


Część findViewById powinna znajdować się w onActivityCreated.
Zar E Ahmer

@Nepster Może, ale nie musi.
laalto

Czasami aktywność nie jest jeszcze przywiązana do fragmentu.
Zar E Ahmer

1
@Nepster i dlatego wzywające findViewById()na rootViewi nie na aktywności.
laalto

10

Wypróbuj OnStart()metodę i po prostu użyj

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

lub zadeklaruj dowolny widok przy użyciu getView().findViewByIdmetody w programieonStart()

Zadeklaruj, że odbiornik kliknięć ma być wyświetlany według anyView.setOnClickListener(this);


Dla mnie było to przydatne, ponieważ próbowałem uzyskać dostęp do widoku rodzeństwa w onCreateView fragmentu, gdzie fragment został zadeklarowany w formacie xml. Widoki rodzeństwo nadal zerowy w onCreateView bo rodzic nie miał gotowego napełniające, ale w onStart mieli :)
Daniel Wilson

3

Spróbuj przenieść dostęp do widoków do metody onViewCreated fragmentu, ponieważ czasami, gdy próbujesz uzyskać dostęp do widoków w metodzie onCreate, nie są one renderowane w czasie wynikającym z wyjątku wskaźnika o wartości null.

 @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

Zgoda, jest to typowy błąd, ponieważ ludzie często nie rozumieją, jak działają fragmenty, gdy rozpoczynają pracę nad programowaniem na Androida. Aby uniknąć nieporozumień, stworzyłem prosty przykładowy kod, który pierwotnie opublikowałem w aplikacji jest zatrzymany w emulatorze Androida , ale opublikowałem go również tutaj.

Oto przykład:

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>

Przykładowy fragment:

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

fragment_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>

Powinien to być prawidłowy przykład, pokazujący, jak można użyć działania do wyświetlenia fragmentu i obsługi zdarzeń w tym fragmencie. A także jak komunikować się z działaniem zawierającym.


1
W tym przykładzie użyto biblioteki Android-support-v4 dla FragmentActivity i menedżera fragmentów pomocy.
EpicPandaForce

1
jeszcze bardziej kompletny przykład jest dostępny na stackoverflow.com/questions/24840509/ ...
EpicPandaForce

1
Chociaż powinieneś używać Ottodo komunikacji zamiast wywołań zwrotnych i używać Butterknifedo wstrzykiwania widoków.
EpicPandaForce

2

Widok „coś” jest fragmentaryczny, a nie aktywny, więc zamiast uzyskać do niego dostęp w działaniu, należy uzyskać do niego dostęp w klasie fragmentu, takiej jak

W 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

Próbujesz uzyskać dostęp do elementów UI w, onCreate()ale jest za wcześnie, aby uzyskać do nich dostęp, ponieważ widoki fragmentów można tworzyć w onCreateView()metodzie. A onActivityCreated()metoda jest niezawodna w obsłudze wszelkich działań na nich, ponieważ aktywność jest w tym stanie w pełni załadowana.


1

Dodaj następujące elementy w swoim pliku activity_main.xml

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

0

Ponieważ zadeklarowałeś swój widok w fragment_main.xml, przenieś ten fragment kodu, w którym otrzymujesz NPE w onCreateView()metodzie fragmentu. To powinno rozwiązać problem.


0

w zamieszczonym kodzie powyżej w pytaniu jest problem: używasz R.layout.activity_main w metodzie oncreate, ale nazwa pliku xml to "fragment_main.xml", co oznacza, że ​​próbujesz uzyskać widok pliku fragment_main.xml który nie jest wyświetlany, więc daje wyjątek zerowego wskaźnika. zmień kod na przykład:

@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

Musisz pamiętać, że ważne jest to, że: NullPointerException występuje, gdy zadeklarowałeś swoją zmienną i próbujesz odzyskać jej wartość przed przypisaniem jej wartości.


0

Użyj metody onViewCreated () zawsze, gdy używasz lub wywołujesz widoki z fragmentów.

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

0

Mam to samo NullPointerExceptioninicjowanie odbiornika po wywołaniu findViewById() onCreate()i onCreateView()metodach.

Ale kiedy używam, onActivityCreated(Bundle savedInstanceState) {...}to działa. Więc mogłem uzyskać dostęp do GroupViewi ustawić mojego słuchacza.

Mam nadzieję, że będzie to pomocne.


0

Najpopularniejsza biblioteka do wyszukiwania widoków, z której korzysta prawie każdy programista.

Nóż do masła

Jak mogę, ich odpowiedzi jest wystarczających, wyjaśniających znalezienie poglądów za pomocą odpowiedniej metodologii. Ale jeśli jesteś programistą Androida i często kodujesz, możesz użyć noża do masła, który oszczędza dużo czasu na znajdowaniu widoków i nie musisz pisać do niego kodu. W 2-3 krokach możesz znaleźć widoki w milisekundach .

Dodaj zależność na poziomie aplikacji:

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

Dodaj wtyczkę do noża do masła:

File -> Settings -> plugins-> 

Następnie wyszukaj Android ButterKnife Zelezny i zainstaluj wtyczkę, zrestartuj swoje studio i gotowe.

Teraz po prostu przejdź do metody Oncreate swojej aktywności i kliknij prawym przyciskiem myszy swoją nazwę_układu i dotknij przycisku generowania i wybierz opcję wstrzyknięcia Butterknife, a odniesienia do widoków zostaną automatycznie utworzone, jak wspomniano poniżej:

    @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;
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.