Eventos personalizados en Android

Leunamal

Hola, os cuento un poco por encima que es lo que me sucede ya que llevo todo este finde intentando arreglar este problema y nada... No sé exactamente que puedo estar haciendo mal.

Tengo una actividad principal que la he llamado AlfaActivity, esta actividad simplemente tiene 2 botones: "login" y "register". Para el click del login hago lo siguiente:

btnLogin.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Intent intent = new Intent(v.getContext(), Login.class);
				startActivity(intent);
			}
		});

En login tengo lo siguiente:

package com.droid;

import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONObject;

import com.access.ControlLogin;
import com.access.OnLoginListener;
import com.access.Post;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

public class Login extends Activity {
	private ControlLogin ctlLogin;

public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.control_login);

	ctlLogin = (ControlLogin) findViewById(R.id.CtlLogin);

	try {
		ctlLogin.setOnLoginListener(new OnLoginListener() {

			@Override
			public void test() {
				Toast.makeText(getBaseContext(), "Test. ",
						Toast.LENGTH_SHORT).show();

			}
		});

	} catch (Exception ex) {
		Log.e("Ex_Login", "Error: " + ex.getMessage());
		Toast.makeText(getApplicationContext(),
				ex.getClass().getName() + " " + ex.getMessage(),
				Toast.LENGTH_LONG).show();
	}
}
}

Si quito el try y catch, me sale en el LogCat un monton de excepciones originados por un java.lang.NullPointerException o si quito el OnLoginListener (es el causante del problema). El código del contrologin es el siguiente:

package com.access;

import com.droid.R;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.access.OnLoginListener;



public class ControlLogin extends LinearLayout {
	private EditText txtUsuario;
	private EditText txtPassword;
	private Button btnLogin;
	private TextView lblMensaje;

public OnLoginListener listener;

public ControlLogin(Context context) {
	super(context);
	inicializar();
}

public ControlLogin(Context context, AttributeSet attrs) {
	super(context, attrs);
	inicializar();
}

private void inicializar() {
	// Utilizamos el layout 'control_login' como interfaz del control
	String infService = Context.LAYOUT_INFLATER_SERVICE;
	LayoutInflater li = (LayoutInflater) getContext().getSystemService(
			infService);
	li.inflate(R.layout.control_login, this, true);

	// Obtenemoslas referencias a los distintos control
	txtUsuario = (EditText) findViewById(R.id.TxtUsuario);
	txtPassword = (EditText) findViewById(R.id.TxtPassword);
	btnLogin = (Button) findViewById(R.id.BtnAceptar);
	lblMensaje = (TextView) findViewById(R.id.LblMensaje);

	// Asociamos los eventos necesarios
	asignarEventos();
}

public void setOnLoginListener(OnLoginListener l) {
	listener = l;
}

private void asignarEventos() {
	btnLogin.setOnClickListener(new OnClickListener() {
		@Override
		public void onClick(View v) {
			listener.test();
		}
	});
}

public void setMensaje(String msg) {
	lblMensaje.setText(msg);
}
}

Y la interfaz (OnLoginListener.java):

package com.access;

public interface OnLoginListener 
{
	void test();
}

Es super raro que no me detecte alguno de los elementos. En el AndroidManifiest creo tener todo lo que hace falta:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.droid"
    android:versionCode="1"
    android:versionName="1.0" >

<uses-sdk android:minSdkVersion="15" />

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name" >
    <activity
        android:name=".AlfaActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:name=".Login">
        <intent-filter>
            <action android:name="Login" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>
</application>

</manifest>

¿Alguno podría tener alguna idea de que me podría estar sucediendo? Resultante bastante frustante el estar constantemente probando cosas y nada... que siga produciéndose el mismo error de ejecución.

Un saludo y gracias.

Tig

Explica mejor lo que quieres hacer, porque no me ha quedado nada claro.

Varias cosas:

Explica qué buscas usando una vista personalizada
Enseña el XML que define la pantalla donde usas la vista personalizada
Pega el log del Logcat o ni sabemos donde da el error...

1 respuesta
Leunamal

#2 ok, gracias antes que nada por responder.

He subido lo que tenia hecho aquel día en mediafire:

http://www.mediafire.com/?84yprgb7mn44hyy

Aunque he vuelto a cambiar el código de estructura el problema me sigue sucediendo. Lo que quería hacer era que a partir de un control personalizado pudiese manejar los eventos del mismo. Decidí usar un control o elemento de Android simplemente para aprender. Pero no consigo que funcione correctamente o me da error o simplemente no detecta el evento click.

El xml donde uso el componente personalizado es el siguiente:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:sgo="http://schemas.android.com/apk/res/com.test.droidtest"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:padding="10dip" >

<com.test.droidtest.ControlLogin
    android:id="@+id/CtlLogin"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#0000AA" />

</LinearLayout>

El log que obtengo con el LogCat es el siguiente:

03-19 15:32:18.336: E/Ex_Login(642): Error: null
03-19 15:32:51.935: D/gralloc_goldfish(696): Emulator without GPU emulation detected.
03-19 15:32:53.395: E/Ex_Login(696): Error: null
03-19 15:32:53.415: D/AndroidRuntime(696): Shutting down VM
03-19 15:32:53.415: W/dalvikvm(696): threadid=1: thread exiting with uncaught exception (group=0x409c01f8)
03-19 15:32:53.445: E/AndroidRuntime(696): FATAL EXCEPTION: main
03-19 15:32:53.445: E/AndroidRuntime(696): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.droid/com.droid.Login}: java.lang.RuntimeException: java.lang.NullPointerException
03-19 15:32:53.445: E/AndroidRuntime(696): 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1956)
03-19 15:32:53.445: E/AndroidRuntime(696): 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1981)
03-19 15:32:53.445: E/AndroidRuntime(696): 	at android.app.ActivityThread.access$600(ActivityThread.java:123)
03-19 15:32:53.445: E/AndroidRuntime(696): 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1147)
03-19 15:32:53.445: E/AndroidRuntime(696): 	at android.os.Handler.dispatchMessage(Handler.java:99)
03-19 15:32:53.445: E/AndroidRuntime(696): 	at android.os.Looper.loop(Looper.java:137)
03-19 15:32:53.445: E/AndroidRuntime(696): 	at android.app.ActivityThread.main(ActivityThread.java:4424)
03-19 15:32:53.445: E/AndroidRuntime(696): 	at java.lang.reflect.Method.invokeNative(Native Method)
03-19 15:32:53.445: E/AndroidRuntime(696): 	at java.lang.reflect.Method.invoke(Method.java:511)
03-19 15:32:53.445: E/AndroidRuntime(696): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
03-19 15:32:53.445: E/AndroidRuntime(696): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
03-19 15:32:53.445: E/AndroidRuntime(696): 	at dalvik.system.NativeStart.main(Native Method)
03-19 15:32:53.445: E/AndroidRuntime(696): Caused by: java.lang.RuntimeException: java.lang.NullPointerException
03-19 15:32:53.445: E/AndroidRuntime(696): 	at com.droid.Login.onCreate(Login.java:98)
03-19 15:32:53.445: E/AndroidRuntime(696): 	at android.app.Activity.performCreate(Activity.java:4465)
03-19 15:32:53.445: E/AndroidRuntime(696): 	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049)
03-19 15:32:53.445: E/AndroidRuntime(696): 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1920)
03-19 15:32:53.445: E/AndroidRuntime(696): 	... 11 more
03-19 15:32:53.445: E/AndroidRuntime(696): Caused by: java.lang.NullPointerException
03-19 15:32:53.445: E/AndroidRuntime(696): 	at com.droid.Login.onCreate(Login.java:29)
03-19 15:32:53.445: E/AndroidRuntime(696): 	... 14 more

Tig

No tiene ningún sentido que tu activity y tu vista inflen el mismo xml

En Login.java

setContentView(R.layout.control_login);

En ControlLogin

li.inflate(R.layout.control_login, this, true);

así que me parece que ahí está el error y que estás fijando la vista incorrecta en la actividad, de ahí que ctlLogin será null, porque no lo encuentra

ctlLogin = (ControlLogin) findViewById(R.id.CtlLogin);

No sé si esto soluciona tus dudas, pero para otra vez intenta ser más específico y no pastees tanto código, que marea y quita las ganas de mirarlo. Lo primero y más importante, qué quieres conseguir, el log logcat y la línea donde da el error. El resto de primeras sobra.

2 respuestas
Leunamal

#4

El cambio de Login.java lo hice el otro día y me seguia dando problemas.

setContentView(R.layout.login);

Me daba el siguiente log:

03-19 16:51:49.976: W/dalvikvm(949): threadid=1: thread exiting with uncaught exception (group=0x409c01f8)
03-19 16:51:50.036: E/AndroidRuntime(949): FATAL EXCEPTION: main
03-19 16:51:50.036: E/AndroidRuntime(949): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.droid/com.droid.Login}: android.view.InflateException: Binary XML file line #9: Error inflating class com.test.droidtest.ControlLogin
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1956)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1981)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.app.ActivityThread.access$600(ActivityThread.java:123)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1147)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.os.Handler.dispatchMessage(Handler.java:99)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.os.Looper.loop(Looper.java:137)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.app.ActivityThread.main(ActivityThread.java:4424)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at java.lang.reflect.Method.invokeNative(Native Method)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at java.lang.reflect.Method.invoke(Method.java:511)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at dalvik.system.NativeStart.main(Native Method)
03-19 16:51:50.036: E/AndroidRuntime(949): Caused by: android.view.InflateException: Binary XML file line #9: Error inflating class com.test.droidtest.ControlLogin
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:691)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.view.LayoutInflater.rInflate(LayoutInflater.java:739)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:251)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.app.Activity.setContentView(Activity.java:1835)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at com.droid.Login.onCreate(Login.java:24)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.app.Activity.performCreate(Activity.java:4465)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1920)
03-19 16:51:50.036: E/AndroidRuntime(949): 	... 11 more
03-19 16:51:50.036: E/AndroidRuntime(949): Caused by: java.lang.ClassNotFoundException: com.test.droidtest.ControlLogin
03-19 16:51:50.036: E/AndroidRuntime(949): 	at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:61)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.view.LayoutInflater.createView(LayoutInflater.java:552)
03-19 16:51:50.036: E/AndroidRuntime(949): 	at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:680)
03-19 16:51:50.036: E/AndroidRuntime(949): 	... 21 more

¿Sería incorrecto poner lo siguiente tras haber puesto el layout.login?

ctlLogin = (ControlLogin) findViewById(R.id.CtlLogin);

En teoría debería encontrar ese elemento ya que en el xml de login.xml esta puesto el componente con ese ID y la clase es la ControlLogin.

La vista en el ControlLogin.java debería dejarla tal como esta,¿no?

Un saludo y gracias de nuevo.

Leunamal

#4 Solucionado. Simplemente interpretando con sentido común el LogCat se puede solucionar esto. No sé como antes no pude darme cuenta de alguno de los errores del fichero .xml :S

Muchísimas gracias.

1 respuesta
m0rG

Me he bajado tu proyecto y tenías dos errores relacionados con la carga de layouts desde XML.El primero ya te lo han comentado. En la actividad Login.java estás cargando el layout que no es (cargas control_login cuando debes cargar login). Es decir en Login.java en el método onCreate() debe ir:

	
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.login);

El segundo error (relacionado con el error que te da de que no puede inflarse el layout desde XML) se debe a que has puesto mal el nombre del paquete en login.xml. En el fichero login.xml debe aparecer lo siguiente:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:sgo="http://schemas.android.com/apk/res/com.test.droidtest"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:padding="10dip" >

<com.access.ControlLogin
    android:id="@+id/CtlLogin"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#0000AA" />
</LinearLayout>

En tu caso tenías en vez de com.access.ControlLogin otro nombre de paquete (com.test.droidtest o algo parecido). Por esa razón al intentar cargar el layout desde ese XML te da un error (por que no encuentra ControlLogin en el paquete que le has puesto).

1 respuesta
Tig

#6 Me alegra haberte ayudado, y sí, la mayor parte de las veces basta con mirar el logcat y pensar que casi 100% seguro el error está en ti y no en la máquina :)

En este caso, era evidente que si ctlLogin era null es porque no existía en la vista que estabas cargando en la Activity. Pero vaya, que es un error que me ha pasado bastantes veces, simplemente la experiencia te hace detectarlos más rápido.

Suerte!

Leunamal

#7 Pues sí, exactamente eso era lo que había que tocar para solucionarlo.

Un saludo y gracias.

--
Ahora que tengo un hueco libre adaptare la nueva versión del proyecto. No creo que tenga ya el mismo problema :).

1 respuesta
Tig

#9 de todas formas, sigo sin entender para qué quieres esa vista personalizada, no has llegado a explicar tus motivos. Si quieres una caja de login reusable en muchas pantallas, crea un Fragment y reúsalo.

1 respuesta
Leunamal

#10 Pues tal como comente antes era para aprender como hacer elementos personalizados. Mirando manuales me dio por hacer una app cambiando cosillas. Voy a echar un ojo al Fragment, desconozco ahora mismo de que va.

Antes tambien me encontré problemillas con la android.os.NetworkOnMainThreadException. Al parecer tengo que crear un hilo distinto cuando se hacen conexiones externas para evitar que la interfaz se quede bloqueada esperando respuesta. No he profundizado mucho, pero al parecer con lo siguiente me podría valer:

runOnUiThread(new Runnable() { 
public void run() { 
//codigo
} 
}); 
1 respuesta
Tig

#11 ten en cuenta que en Android, a no ser que indiques lo contrario, todo se ejecuta en el hilo de pintado, el que ve el usuario. También ten en cuenta que en Java la ejecución del código es secuencial, es decir que si haces

int i = operacionCostosa();

guardarResultado(i);

hasta que i no tenga valor, no se guarda el resultado o, lo que es lo mismo, el hilo con el que el usuario tiene que interactuar está bloqueado a la espera de que se termine esa operación costosa. Si el bloqueo dura 4-5 segundos, Android lanza una ANR (Application Not Responding) y cierra tu aplicación para que dejes de tener bloqueado al usuario.

Así, toda operación costosa o asíncrona (como leer un fichero o llamar a una web) tiene que ir en un hilo diferente del principal. Leete esto: http://developer.android.com/guide/practices/design/responsiveness.html

Depende de para lo que quieras conectar con internet es mejor que te mires los Loaders (http://developer.android.com/guide/topics/fundamentals/loaders.html), que te facilitan leer datos de internet en un hilo aparte y te devuelven el resultado cuando han terminado. Los Threads vienen bien para muchas cosas, pero hay clases específicas para algunas tareas asíncronas.

1 respuesta
Leunamal

#12 Me leeré detenidamente los enlaces. Me van a venir bien.

Gracias.

Usuarios habituales

  • Leunamal
  • Tig
  • m0rG