NullPointerException accediendo a vistas en onCreate ()

114

Esta es una pregunta canónica para un problema que se publica con frecuencia en StackOverflow.

Estoy siguiendo un tutorial. He creado una nueva actividad con un asistente. Obtengo NullPointerExceptioncuando intento llamar a un método en Views obtenido con findViewById()en mi actividad onCreate().

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

Diseño 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>
laalto
fuente

Respuestas:

70

El tutorial probablemente esté desactualizado, ya que intenta crear una interfaz de usuario basada en actividades en lugar de la interfaz de usuario basada en fragmentos preferida por el código generado por el asistente.

La vista está en el diseño de fragmentos ( fragment_main.xml) y no en el diseño de actividades ( activity_main.xml). onCreate()es demasiado temprano en el ciclo de vida para encontrarlo en la jerarquía de vistas de actividad y nullse devuelve a. Invocar un método ennull provoca el NPE.

La solución preferida es mover el código al fragmento onCreateView(), invocando findViewById()el diseño del fragmento inflado 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;
}

Como nota al margen, el diseño del fragmento eventualmente será parte de la jerarquía de la vista de actividad y se podrá descubrir con la actividad, findViewById()pero solo después de que se haya ejecutado la transacción del fragmento. Las transacciones de fragmentos pendientes se ejecutan super.onStart()después onCreate().

laalto
fuente
La parte findViewById debe estar en onActivityCreated.
Zar E Ahmer
@Nepster Puede ser, pero no tiene por qué serlo.
laalto
En algún momento, la actividad aún no se adjunta al fragmento.
Zar E Ahmer
1
@Nepster y es por eso que llaman findViewById()en rootViewy no en la actividad.
laalto
10

Prueba el OnStart()método y solo usa

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

o declarar cualquier vista usando el getView().findViewByIdmétodo enonStart()

Declarar oyente de clics en la vista por anyView.setOnClickListener(this);

Tarun Sharma
fuente
Para mí, esto fue útil porque estaba tratando de acceder a una vista de hermanos dentro de onCreateView de un fragmento, donde el fragmento se declaró en xml. Las vistas de los hermanos seguían siendo nulas en onCreateView porque el padre no había terminado de inflar, pero en onStart sí :)
Daniel Wilson
3

Intente cambiar sus vistas de acceso al método de fragmento onViewCreated porque a veces, cuando intenta acceder a las vistas en el método onCreate, no se procesan en el momento, lo que resulta en una excepción de puntero nulo.

 @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();
    }
 }
Sarthak Sharma
fuente
2

De acuerdo, este es un error típico porque las personas a menudo no comprenden realmente cómo funcionan los Fragmentos cuando comienzan a trabajar en el desarrollo de Android. Para aliviar la confusión, creé un código de ejemplo simple que publiqué originalmente en La aplicación está detenida en el emulador de Android , pero también lo publiqué aquí.

Un ejemplo es el siguiente:

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>

Y ese debería ser un ejemplo válido, muestra cómo puede usar una Actividad para mostrar un Fragmento y manejar eventos en ese Fragmento. Y también cómo comunicarse con la actividad que lo contiene.

EpicPandaForce
fuente
1
Este ejemplo usa la biblioteca android-support-v4 para FragmentActivity y el administrador de fragmentos de soporte.
EpicPandaForce
1
un ejemplo aún más completo está disponible en stackoverflow.com/questions/24840509/…
EpicPandaForce
1
Aunque debería usarlo Ottopara la comunicación en lugar de devoluciones de llamada, y usarlo Butterknifepara inyectar vistas.
EpicPandaForce
2

La vista "algo" está en fragmento y no en actividad, por lo que en lugar de acceder a ella en actividad, debe acceder a ella en la clase de fragmento como

En 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;
}
Shubham Goel
fuente
2

Está intentando acceder a los elementos de la interfaz de usuario en el onCreate(), pero es demasiado pronto para acceder a ellos, ya que las vistas en fragmentos se pueden crear en el onCreateView()método. Y el onActivityCreated()método es confiable para manejar cualquier acción sobre ellos, ya que la actividad está completamente cargada en este estado.

Krishna
fuente
1

Agregue lo siguiente en su activity_main.xml

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

Dado que ha declarado su Vista en el fragment_main.xml, mueva ese fragmento de código donde obtiene el NPE en el onCreateView()método del fragmento. Esto debería resolver el problema.

Rohit Goyal
fuente
0

en el código publicado arriba en la pregunta hay un problema: está utilizando R.layout.activity_main en el método oncreate, pero el nombre del archivo xml es "fragment_main.xml", significa que está tratando de obtener la vista del archivo fragment_main.xml que no se muestra, por lo que da una excepción de puntero nulo. cambiar el código como:

@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();
    }
}
Akshay Paliwal
fuente
0

Debes recordar que lo importante es: NullPointerException ocurre cuando has declarado tu variable e intentas recuperar su valor antes de asignarle un valor.

Mohan Munisifreddy
fuente
0

Utilice el método onViewCreated () siempre que utilice o llame a vistas desde fragmentos.

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

Tengo lo mismo NullPointerExceptioninicializando un oyente después de llamar findViewById() onCreate()yonCreateView() métodos.

Pero cuando lo he usado onActivityCreated(Bundle savedInstanceState) {...}funciona. Entonces, pude acceder alGroupView y configurar mi oyente.

Espero te sea de ayuda.

clebertx
fuente
0

La biblioteca más popular para buscar vistas que utilizan casi todos los desarrolladores.

Cuchillo de mantequilla

Como puedo, hay suficientes respuestas para explicar la búsqueda de puntos de vista con la metodología adecuada. Pero si es un desarrollador de Android y codifica con frecuencia a diario, puede usar un cuchillo de mantequilla, lo que le ahorra mucho tiempo en la búsqueda de vistas y no tiene código de escritura para ello. Con 2-3 pasos puede encontrar vistas en milisegundos. .

Agregue dependencia en el nivel de aplicación gradle:

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

Agregar complemento para cuchillo de mantequilla:

File -> Settings -> plugins-> 

Luego busque Android ButterKnife Zelezny e instale el complemento y reinicie su estudio y terminado.

Ahora simplemente vaya al método Oncreate de su actividad y haga clic derecho en su layout_name y toque el botón generar y seleccione la opción de inyección de cuchillo de mantequilla y sus referencias de vistas se crearán automáticamente como se menciona a continuación:

    @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;
Rishabh Saxena
fuente