NullPointerException доступ к представлениям в onCreate ()


114

Это канонический вопрос для проблемы, часто публикуемой на StackOverflow.

Я слежу за учебником. Я создал новое занятие с помощью мастера. Я получаю NullPointerExceptionпри попытке вызвать метод для Views, полученного findViewById()в моей деятельности onCreate().

Активность 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();
    }
}

Макет 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>

Ответы:


70

Учебник, вероятно, устарел, в нем делается попытка создать пользовательский интерфейс на основе действий вместо пользовательского интерфейса на основе фрагментов, предпочитаемого кодом, созданным мастером.

Представление находится в макете фрагмента ( fragment_main.xml), а не в макете действия ( activity_main.xml). onCreate()находится слишком рано в жизненном цикле, чтобы найти его в иерархии представления действий, и nullвозвращается. Вызов метода nullвызывает NPE.

Предпочтительное решение - переместить код во фрагмент onCreateView(), вызывая findViewById()расширенный макет фрагмента 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;
}

В качестве побочного примечания, макет фрагмента в конечном итоге станет частью иерархии представления активности и будет обнаружен с помощью действия, findViewById()но только после того, как транзакция фрагмента будет запущена. Ожидающие транзакции фрагментов выполняются через super.onStart()after onCreate().


Часть findViewById должна находиться в onActivityCreated.
Zar E Ahmer

@Nepster Может быть, но не обязательно.
laalto

Иногда активность еще не прикреплена к фрагменту.
Zar E Ahmer

1
@Nepster и поэтому призывают findViewById()на rootViewи не от деятельности.
laalto

10

Попробуйте OnStart()метод и просто используйте

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

или объявить любое представление с использованием getView().findViewByIdметода вonStart()

Объявить прослушиватель кликов для просмотра anyView.setOnClickListener(this);


Для меня это было полезно, потому что я пытался получить доступ к одноуровневому представлению внутри onCreateView фрагмента, где фрагмент был объявлен в xml. Родственные представления все еще были нулевыми в onCreateView, потому что родительский элемент не закончил надувание, но в onStart они были :)
Дэниел Уилсон,

3

Попробуйте переключить ваши представления доступа на метод фрагмента onViewCreated, потому что иногда, когда вы пытаетесь получить доступ к представлениям в методе onCreate, они не отображаются в то время, когда возникает исключение нулевого указателя.

 @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

Согласен, это типичная ошибка, потому что люди часто не понимают, как работают фрагменты, когда начинают работать над разработкой для Android. Чтобы избежать путаницы, я создал простой пример кода, который изначально был опубликован в разделе «Приложение остановлено в эмуляторе Android» , но я разместил его и здесь.

Пример такой:

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

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>

И это должен быть действительный пример, он показывает, как вы можете использовать Activity для отображения фрагмента и обрабатывать события в этом фрагменте. А также как общаться с содержащим Activity.


1
В этом примере используется библиотека android-support-v4 для FragmentActivity и диспетчер фрагментов поддержки.
EpicPandaForce 02

1
еще более полный пример доступен на stackoverflow.com/questions/24840509/…
EpicPandaForce

1
Хотя вы должны использовать Ottoдля связи вместо обратных вызовов и использовать Butterknifeдля внедрения представлений.
EpicPandaForce

2

Представление «что-то» находится во фрагменте, а не в действии, поэтому вместо доступа к нему в действии вы должны получить доступ к нему в классе фрагмента, например

В 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

Вы пытаетесь получить доступ к элементам пользовательского интерфейса в onCreate()но, еще слишком рано для доступа к ним, поскольку представления фрагментов могут быть созданы в onCreateView()методе. И onActivityCreated()метод надежен для обработки любых действий с ними, поскольку в этом состоянии активность полностью загружена.


1

Добавьте следующее в свой 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

Поскольку вы объявили свое представление в файле fragment_main.xml, переместите этот фрагмент кода, в который вы получаете NPE, в onCreateView()методе фрагмента. Это должно решить проблему.


0

в опубликованном коде выше в вопросе есть проблема: вы используете R.layout.activity_main в методе oncreate, но имя файла xml - «fragment_main.xml», означает, что вы пытаетесь получить представление файла fragment_main.xml который не отображается, поэтому он дает исключение нулевого указателя. измените код, например:

@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

Вы должны помнить, что важно следующее: исключение NullPointerException возникает, когда вы объявили свою переменную и пытаетесь получить ее значение перед присвоением ей значения.


0

Используйте метод onViewCreated () при использовании или вызове представлений из фрагментов.

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

0

У меня такая же NullPointerExceptionинициализация слушателя после вызова findViewById() onCreate()и onCreateView()методов.

Но когда я использовал onActivityCreated(Bundle savedInstanceState) {...}его, он работает. Итак, я мог получить доступ GroupViewи установить свой слушатель.

Надеюсь, это будет полезно.


0

Самая популярная библиотека для поиска представлений, которую использует практически каждый разработчик.

Нож для масла

Насколько я могу, их ответов достаточно, чтобы объяснить поиск точек зрения с помощью надлежащей методологии. Но если вы разработчик Android и часто кодите ежедневно, вы можете использовать нож для масла, который экономит много времени при поиске представлений, и у вас нет кода для него. С помощью 2-3 шагов вы можете найти представления за миллисекунды. ,

Добавить зависимость в gradle на уровне приложения:

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

Добавьте плагин для ножа для масла:

File -> Settings -> plugins-> 

Затем найдите Android ButterKnife Zelezny, установите плагин и перезапустите студию, и все готово.

Теперь просто перейдите к методу Oncreate вашей активности и щелкните правой кнопкой мыши свое layout_name, нажмите кнопку создания и выберите вариант впрыска масляного ножа, и ваши ссылки на представления будут автоматически созданы, как указано ниже:

    @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;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.