2

Haciendo un código para que simplemente se muestre Hola, mundo en un TextView llamado st, al que se le referencia por la variable stat, sale el error:

java.lang.RuntimeException: Unable to start activity ComponentInfo      {com.aapps.vyber.clicker/com.aapps.vyber.app.MainActivity}: java.lang.NullPointerException:

Haciendo referencia a la última línea de instrucciones de onCreate(), en la que intento colocar el texto por medio de setText().

MainActivity.xml -> Sólo tiene la barra de opciones abajo

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/fragment_container"
    android:layout_above="@+id/navigation"/>

<android.support.design.widget.BottomNavigationView
    android:id="@+id/navigation"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginEnd="0dp"
    android:layout_marginStart="0dp"
    android:background="?android:attr/windowBackground"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:menu="@menu/navigation"
    android:layout_alignParentBottom="true"/>

</RelativeLayout>

MainActivity.java -> Con esta clase, quiero cambiar el texto de un TextView de Fragment.xml

package com.aapps.vyber.app;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;

import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    public TextView stat;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        stat = (TextView) findViewById(R.id.st);
        stat.setText("Hola, mundo");
    }

}

También definí un nuevo método por el que se pone el texto tipo,

void do_it(){
    stat.setText("Hola, mundo");
}

Para invocarlo desde onCreate() en vez de poner el setText() directamente, pero sigue sin funcionar.

He aquí el XML del Fragment. El MainActivity está completamente vacío, por lo que no veo importante su inclusión

Fragment.xml -> Tiene el TextView al que quiero referenciar

<?xml version="1.0" encoding="utf-8"?>
<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">

<TextView
    android:id="@+id/st"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="403dp"
    android:text="Quiero cambiarme"
    android:textAlignment="center"
    android:textSize="18sp" />

</RelativeLayout>

FragmentActivity.java -> Sólo tiene el código para cambiar según la barra de herramientas

package com.aapps.vyber.app;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FragmentActivity extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.Fragment, container, false);
    }
}

Algún error garrafal por ahí? Gracias por su dedicación y ayuda de antemano.

NOTA

st existe y está en una layout, solo que no en la MainActivity. Está en un fragmento. Si es motivo del problema, por supuesto, me encantaría que me notificaran de cómo solucionarlo manteniendo al TextView en el fragmento. Sin embargo, el IDE no me señala el texto como imposible de alcanzar. Además, todas las variables y valores que uso están definidos y he intentado no usar métodos import que puedan devolver valores null

Néstor Llop
  • 147
  • 1
  • 1
  • 10
  • 1
    Vybr, por favor publica tu xml del activity. – Einer Jul 30 '18 at 12:44
  • Muchísimas gracias por la petición, hecho – Néstor Llop Jul 30 '18 at 12:48
  • 2
    Posible duplicado de [¿Cuál es la solución a todos los errores NullPointerException presentes, pasados y futuros?](https://es.stackoverflow.com/questions/42977/cu%c3%a1l-es-la-soluci%c3%b3n-a-todos-los-errores-nullpointerexception-presentes-pasados) – gbianchi Jul 30 '18 at 13:32
  • Gracias por la observación. La diferencia está en que no es uso de ningún valor nulo o todavía no creado, o de algún método que no devuelva información. He hecho rigurosos exámenes y pruebas para dar seguridad de que toda la información empleada o pedida en el código existe – Néstor Llop Jul 30 '18 at 13:39
  • @gbianchi Considero que posiblemente sea duplicado, pero no de esa pregunta: el tema de los layouts en Android suele dar este problema y la solución no es obvia. Ejemplo: https://es.stackoverflow.com/questions/130441/android-nullpointerexception-en-getintent-getextras – Pablo Lozano Jul 30 '18 at 13:45
  • @PabloLozano tu eres el experto. Necesitamos adaptar esa respuesta o estos casos si hay que tratarlos uno a uno? – gbianchi Jul 30 '18 at 13:46
  • El experto en este caso sería @JorgeSys, mis conocimientos de Android son muy limitados – Pablo Lozano Jul 30 '18 at 13:50

1 Answers1

4

Si intentas realizar una búsqueda de referencia de un id no existente en tu layout, instanciarlo e invocar algún método en la instancia, siempre te causara NullPointerException.

Solucion a tu problema

Primero que todo, elimina el codigo de tu Activity que pertenece al Fragment:

stat = (TextView) findViewById(R.id.st);
stat.setText("Hola, mundo");

y muevelo al onCreateView de tu Fragment:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    // Referencia del Layout, esto es mas o menos lo mismo que hacer setContentView en un Activity
    View view = inflater.inflate(R.layout.Fragment, container, false);
    stat = (TextView) view.findViewById(R.id.st); // Aqui no devuelve null, porque st esta en el layout Fragment.axml que carga el inflate
    stat.setText("Hola, mundo");
    return view;
}

Fijate que el root donde vas a hacer findViewById debe hacer referencia a la layout cargado. Para poder referenciar las vistas en un Fragment, debes hacerlo en su clase y con el layout del mismo, en este caso su referencia guardada en la variable local view de tipo View.

Ejemplo:

TextView tv = (TextView) view.findViewById(R.id.***)...
Button btn = (Button) view.findViewById(R.id.***)...

etc...

¿Como comunicar acciones entre Activiy-Fragment y viceversa?

Digamos que ahora quieres modificar algun valor de tu Fragment desde el Activity. Para ello, este debe estar activo en el Fragment backstack, y segun la documentacion oficial, la comunicacion entre la actividad y el Fragment debe hacerse de la siguiente manera:

Desde el Fragment al Activity:

Digamos que tienes un Fragment llamado MyFragment. Creas una variable privada que sera el listener a invocar:

private OnExampleListener listener;

Debes crear una interface de comunicacion entre el Fragment y el Activity. Primero declara una interface que pertenezca al espacio del Fragment, esto para poder identificarla como tal.

// La actividad que contiene al Fragment debe implementar esta interfaz
public interface OnExampleListener {
    public void onExample(); // Aqui creas uno o mas metodos que implementaras
}

una vez hecho esto, debes suscribir el listener, la mejor manera es mientras el fragment este adjunto a la actividad, es decir, cuando se haya creado y este en el stack (onAttach):

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        // Si la instancia de tu Actividad, implementa la interfaz, esta sera a quien se va a invocar en la interacción
        try {
            listener = (OnExampleListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " esta actividad no implementa OnExampleListener");
        }
    }

Una vez hecho esto, en tu Activity, implementas la interfaz del Fragment:

public class MainActivity extends Activity
        implements MyFragment.OnExampleListener{
    ...

    // Este método pertenece a la interfaz y se llamara al ser invocado
    public void onExample() {
         // Aqui haces lo que desees al ser invocado el metodo de la interfaz
    }
}

y ya para invocar la accion en cualquier parte del Fragment ya sea en un onClick o cualquier parte, lo llamas asi:

if(listener != null)
   listener.onExample();

Desde el Activity al Fragment:

Si es una accion como el onBackPressed puedes hacer el mismo truco de las interfaces pero de manera inversa, sin embargo, si solo quieres invocar un metodo desde la Actividad al Fragment, debes hacer lo siguiente:

MyFragment mFragment = (MyFragment)
                getSupportFragmentManager().findFragmentById(R.id./*id del contenedor del Fragment (FrameLayout) en tu activity*/);

        if (mFragment != null) {
            // Si el fragment esta en el contenedor, aqui invocas su accion.

            // llama un metodo del Fragment
            mFragment.exampleMethod("Hola mundo desde el Activity");
        }

y ya en tu Fragment, decidiras que hacer en tu metodo, cuando sea invocado:

public void exampleMethod(String text){
    // Aqui haces lo que desees, en tu ejemplo, hacer setText desde el Activity
    if(stat != null && text != null)
       stat.setText(text);
}
Andrespengineer
  • 3,002
  • 1
  • 8
  • 18
  • Muchísimas gracias, y perdona por la confusión que ofrecí poniendo ese código. Esa parte que nombras está resuelta tanto en otros archivos como en directorios. Mi verdadero problema es por qué no puedo usar el setText(), aun referenciándolo correctamente y de la manera más completa para mi parecer. Si has respondido a esto, y no lo he entendido como querías, me encantaría que me perdonaras, pues no llevo demasiado tiempo con esto del Android – Néstor Llop Jul 30 '18 at 14:27
  • @Vyber90 No puedes referenciarlo desde otro archivo. `findViewById`, solo buscara referencias que estén adjuntas al layout propuesto en setContentView o un `root` de una vista inflada. Si ese es el xml que manejas para tu Activity, debes eliminar dichas lineas de codigo. – Andrespengineer Jul 30 '18 at 14:31
  • Y puedo poner una función que haga el setText en la clase del Fragment para llamarla desde el MainActivity.java usando un import? – Néstor Llop Jul 30 '18 at 14:33
  • Tambien veo que estas diciendo que ese layout pertenece a tu fragment. Quieres decir que ese id del TextView pertenece a fragment_container.axml ? esto no se debe, ya que si ese es el caso, deberias tener una clase Fragment donde el root de la vista sea la inflada. No puedes referenciar un TextView perteneciente a un Fragment en tu Activity. – Andrespengineer Jul 30 '18 at 14:34
  • @Vyber90 para la comunicacion entre un Fragment y un Activity debes hacerlo [mediante interfaces](https://developer.android.com/training/basics/fragments/communicating) puedes ver mas en el link adjunto. – Andrespengineer Jul 30 '18 at 14:36
  • Fragment_container es un FrameLayout que puse en el MainActivity.xml, ajeno al código que puse en la pregunta. Mi intención era, con la transacción, poner en éste el fragment.xml que correspondiera dependiendo del botón pulsado en el BottomNavigationView. Perdona que no sepa lo que quieres decir exactamente – Néstor Llop Jul 30 '18 at 14:37
  • @Vyber90 muestra el codigo completo tanto de tu Activity, Fragment (que debes tener una clase que herede de Fragment) y tu xml, para editarte mi respuesta y postearte la solucion que funcionaria. Trata de editar tu pregunta. – Andrespengineer Jul 30 '18 at 14:38
  • Ya lo he hecho. Si quieres ver algo más, sin problema, pero creo que esto ya es suficiente. Encima de cada bloque de texto, que representa un archivo, tienes el nombre que uso para referenciarlo y una breve descripción. Espero que te sea útil, y perdona por ser tan tonto a la hora de explicar y dar información – Néstor Llop Jul 30 '18 at 15:45
  • [Continuemos el debate en el chat](https://chat.stackexchange.com/rooms/80868/discussion-between-vyber90-and-andrespengineer). – Néstor Llop Jul 30 '18 at 15:49