View not attached to window manager!! Maldita AsyncTask, y maldito ProgressDialog!!!


Error:

java.lang.IllegalArgumentException: View not attached to window manager

Causa:

La actividad finaliza antes de que la AsyncTask haya finalizado, con un ProgressDialog de carga.

Solución:

La solución pasa por forzar que el ProgressDialog sea nulo al salir de la actividad, es decir, cuando se llama a su método onStop().

@Override
public void onStop() {
    super.onStop();
    mDialog = null;
}

Y ya en la AsyncTask de la actividad, hacer lo siguiente:

protected void onPreExecute() {
    mDialog = ProgressDialog.show(mContext, "", "Saving changes...",
            true);
}

protected void onPostExecute(Object result) {
   if (mDialog != null) { 
        mDialog.dismiss();
   }
}

Con esto, sólo se forzará que desaparezca cuando realmente haya ProgressDialog, y evidentemente, desaparecerá la excepción…

🙂

http://www.android.es/


http://www.android.es/

Otra página «hermana»… 🙂

Tab Host: Task before!!


He visto algunas maneras de buscar solución al problema de cambiar una actividad dentro de una tab, antes de que la tab cambie, y ninguna me convence.

Hay una forma más sencilla, sabiendo cómo funciona el ciclo de vida de las acividades teniendo en cuenta que aunque no sean visibles, todas las actividades de las tabs ya han pasado por onCreate() cuando se entra por primera vez en la actividad que contiene al tabHost.

 

 

 

 

 

 

 

 

 

La idea es que al cambiar, la actividad al irse a mostrar, pasa por el método onPause(), por lo que podemos apoyarnos en él para hacer los cambios convenientes antes de mostrar. Por ejemplo, cambiar los datos de un ListView:

  @Override
    public void onPause() {
	super.onPause();

	String[] miListaNueva = { "2", "3" };

        if(myCondicion) {

	    list.setAdapter(new ArrayAdapter(this,
		android.R.layout.simple_list_item_1, miListaNueva));
        }
    }

🙂

Search Interface para ListView


A veces es interesante darle opción al usuario, cuando hay muchas entradas mostradas en forma de ListView, a que pueda navegar por ellas, especialmente cuando busca una en concreto, aunque no sepa exactamente cuál de ellas es. Una forma básica de hacer esto, es por coincidencia.

Supongamos el siguiente main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/LinearLayout01"
                android:layout_width="fill_parent"
android:layout_height="fill_parent"
                xmlns:android="http://schemas.android.com/apk/res/android"
                android:orientation="vertical">
<EditText android:id="@+id/EditText01"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:hint="Search">
</EditText>

<ListView android:id="@+id/ListView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>

Tal como se puede ver, es sencillo, un EditText y un ListView.

Ahora supongamos que la actividad principal tiene un String array constante, de la siguiente forma:

package com.ListViewSearchExample;

import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;

public class ListViewSearchExample extends Activity
{
private ListView lv;
private EditText et;
private String listview_array[] = { "ONE", "TWO", "THREE", "FOUR", "FIVE",
"SIX", "SEVEN", "EIGHT", "NINE", "TEN" };
private ArrayList<String> array_sort= new ArrayList<String>();
int textlength=0;

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

lv = (ListView) findViewById(R.id.ListView01);
et = (EditText) findViewById(R.id.EditText01);
lv.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, listview_array));

et.addTextChangedListener(new TextWatcher()
{
public void afterTextChanged(Editable s)
{
                                                                // Abstract Method of TextWatcher Interface.
}
public void beforeTextChanged(CharSequence s,
int start, int count, int after)
{
// Abstract Method of TextWatcher Interface.
}
public void onTextChanged(CharSequence s,
int start, int before, int count)
{
textlength = et.getText().length();
array_sort.clear();
for (int i = 0; i < listview_array.length; i++)
{
if (textlength <= listview_array[i].length())
{
if(et.getText().toString().equalsIgnoreCase(
(String)
listview_array[i].subSequence(0,
textlength)))
{
                                                                                                                array_sort.add(listview_array[i]);
                                                                                                }
                                                                                }
                                                                }
lv.setAdapter(new ArrayAdapter<String>
(ListViewSearchExample.this,
android.R.layout.simple_list_item_1, array_sort));
}
});
}
}

La idea es asociarle un listener de cambio al EditText, de forma que antes de que se produzca el cambio, se limpie el array, y se vuelva a recrear con las opciones por coincidencia (ver método beforeTextChanged()).

De esta forma tan sencilla, se emularía tal comportamiento, y puede ser un buen punto de partida para complicarlo tanto como se quiera.

🙂

Talkyou


Talkyou es un cliente SIP que permite hacer llamadas VOIP desde cualquier teléfono Android sin necesidad de llamar por la linea de teléfono móvil, y así reducir costes de llamadas, con total soporte en redes WIFI y 3G. Además, tiene compatibilidad 100% con centralitas Asterisk y proveedores de voz IP.

Otras características son:

  • La calidad de sonido es excelente, incluye codecs G.711 (ulaw y alaw)
  • Permite configurar con facilidad los códecs de audio y vídeo
  • Integración con los contactos del teléfono Android
  • Historial de llamadas

Se puede descargar TalkYou por 1,99 € en el Android Market.

Fuente http://www.android.es/talkyou-sofphone-para-android.html#ixzz1lKoK1ahL

🙂

Android adb “Unable to open sync connection!”


Error:

java.io.IOException: Unable to open sync connection!

Causa:

Aparentemente, ninguna, el smartphone parece estar correctamente conectado, y justo se iba a subir mi aplicación al dispositivo. Jum!! ¿Se habrá vuelto loco? 😦

Solución:

Hay dos posibles soluciones, que en realidad tratan de lo mismo: depuración. La primera opción, más intuitiva, consiste en deshabilitar y habilitar el USB Debugger del dispositivo, y todo volverá a ir bien. La segunda, más experta, es «matar» por comando al Android Debug Bridge: adb kill-server

🙂

:) Developer Tools :)


Herramientas útiles para desarrolladores

Pues eso, herramientas útiles, la mayoría con UI bastante notable, así como otras utlidades, para cualquier desarrollador que se digne.

🙂

Exit Button??


Uno de los problemas más usualeses a la hora de plantear cómo abandonar una aplicación Android, y muchos optan por poner el típico botón EXIT.

Button btnexit = (Button)findviewbyId(btn_exit);

btnexit.setOnClicklistenr(new onClicklister(){

     @override
     public void onClick(View v){
            finish();
});

Sin embargo, esto que puede parecer lógico hasta cierto punto, está poco aconsejado, ya que la gracia de Android es su ciclo de vida de las actividades: lo interesante es dejar que sea el SO quien decida cuándo matar la actividad de nuestra aplicación, recurriendo en todo caso a «jugar» con las fases del ciclo si nos fuese útil. Es decir, dejarle al sistema que sea «inteligente» (claro, es verdad, en la tienda me dijeron que era un móvil inteligente!!!,)

🙂

De hecho, aplicando lo anterior, sólo conseguiríamos cerrar la actividad actual, pero no las que estuvieran debajo en la pila correspondientes a la aplicación.

La idea pues es, ir redirigiendo a la pantalla Home, y entonces allí, dejar que sea el SO quien decida, al pulsar el Back Button, y volver al escritorio del sistema. Algunos prefieren directamente, en ese caso, forzar a lo bruto el cierre como cualquier aplicación Java:

finish();
System.exit(0);

Esto se puede ver si lo aplicamos a una actividad intermedia de la aplicación: muy posíblemente aparecería un «parpadeo» negro, hasta volver a la actividad anterior. Ese «parpadeo» es el que delata que la actividad fue eliminada «en combate».

Como solución de compromiso, propongo lo siguiente: fingir que se abandona la aplicación. Es decir, vamos redirigiendo a la actividad principal, y entonce sobreescribimos el método onBackPressed(), de la siguiente forma:

 @Override
    public void onBackPressed() {
	if (this.lastBackPressTime < System.currentTimeMillis() - 4000) {
	    this.toast = Toast.makeText(this,
		    "Pulsa atrás otra vez para cerrar la aplicación", 4000);
	    this.toast.show();

	    this.lastBackPressTime = System.currentTimeMillis();
	} else {
	    if (this.toast != null) {
		this.toast.cancel();
	    }
	    super.onBackPressed();

//	    finish();
//	    System.exit(0);
	}
    }

La idea es que al pulsar el botón BACK, nos pregunte por si deseamos cerrar, y se pulsa rápido a continuación, en realidad haga la acción de back, dándole la sensación al usuario de que se sale de la misma.

Si descomentáramos las líneas de finish y System, sería otra historia…

🙂

Refrescar un ListView :)


Me acaba de pasar, y no es la primera vez, que quiero refrescar algún elemento en un ListView, y no sé cómo…

El problema es que, cuando un elemento aparece en pantalla, se carga su vista desde el método getView() del adaptador correspondiente, y de la misma forma perdiéndose esa vista cuando desaparece de la misma.

En primera instancia pensé en una maravillosa AsynTask que cogiera la vista desde una lista de vistas interna en el adaptador, y aparentemente iba bien. Pero claro, en el momento que aparecían y desaparecían dinámicamente, se perdían y aparecían nuevas referencias, y cuando te querías dar cuenta… ArrayOutOfBoundsException!!!

Y buscando, encontré la solución:

myListView.invalidateViews();

Es decir, al invalidar todas las vistas, estamos forzando que se vuelvan a representar las que estén visibles, ya con los cambios que habíamos realizado previamente. Es una solución tosca, he de reconocerlo, pero se sale del paso si el ListView es sencillito, en el sentido de que sólo aparezcan unos pocos elementos en pantalla.

Otra forma de actuar es usar métodos específicos del ListView:

int visiblePosition = yourListView.getFirstVisiblePosition();
    View v = yourListView.getChildAt(visiblePosition);

En este caso, accederíamos a la vista «inflada» que ocupa todo el primer elemento visible en pantalla, pudiendo acceder a su vez a cualquiera de sus vistas internas.

🙂

CountDown for Android! La cuenta atrás ha comenzado…


Resulta que para la aplicación que ando perfilando necesitaba una serie de contadores de tiempo hacia atrás simultáneos, y se puede hacer de varias formas.

Para ilustrarlo un poco, vamos a ver un ejemplo sencillo basado en un Timer eespecífico: CountDownTimer. Aunque no son clases para tiempos muy recomendadas para cuando se requiera restricciones altas, ya que puede sufrir los retrasos propios de los procesos, en este caso podemos usarlas sin problemas.

En concreto, vamos a crear un servicio que cuenta atrás para cualquier fecha que se elija, como podría ser de cumpleados 🙂 En caso de poner una fecha del pasado, indicaremos cuánto tiempo se ha vivido.

En primer lugar tenemos el main.xml como layout principal:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="30dp">
<TextView
android:id="@+id/day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Day "
android:textSize="36sp" />
<TextView  	android:id="@+id/hour"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00"
android:textSize="36sp"    />
<TextView  	android:id="@+id/min"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=":00"
android:textSize="36sp"    />
<TextView  	android:id="@+id/sec"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text=":00"
android:textSize="36sp"    />
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5dp"    >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter End Time :   "    />
<EditText    	android:id="@+id/end"
android:layout_width="fill_parent"
android:layout_height="wrap_content"     />
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5dp"    >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter Start Time : "    />
<EditText    	android:id="@+id/start"
android:layout_width="fill_parent"
android:layout_height="wrap_content"     />
</LinearLayout>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="40dp"    >
<Button    	android:id="@+id/btnstart"
android:layout_width="wrap_content"
 android:layout_height="wrap_content"
android:text="Start Counter"    />
<Button    	android:id="@+id/btnstop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stop"    />
</LinearLayout>
</LinearLayout>

A continuación, el código:

public class MActivity extends Activity {

TextView day,hour,min,sec;
int iDay,iHour,iMin,iSec;
MyCount counter;
Date endDate = null;
Date startDate = null;
NumberFormat myFormat = NumberFormat.getInstance();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

final SimpleDateFormat outputFormat = new SimpleDateFormat("yyyy-MM-dd   HH:mm:ss");
day=(TextView)findViewById(R.id.day);
hour=(TextView)findViewById(R.id.hour);
min=(TextView)findViewById(R.id.min);
sec=(TextView)findViewById(R.id.sec);
myFormat.setMinimumIntegerDigits(2);
final EditText end=(EditText)findViewById(R.id.end);
final EditText start=(EditText)findViewById(R.id.start);

Button btnStart=(Button)findViewById(R.id.btnstart);
btnStart.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View arg0) {
try {
endDate = outputFormat.parse(end.getText().toString());
startDate=outputFormat.parse(start.getText().toString());

long diffInMis= endDate.getTime() - startDate.getTime();
if(diffInMis<0){
 Toast.makeText(getBaseContext(), "Please, Enter valid Time..."
     ,Toast.LENGTH_SHORT).show();
}
else{
long diff = TimeUnit.MILLISECONDS.toSeconds(diffInMis);
iDay=(int) (diff/(60*60*24));
long lday= (diff%(60*60*24));
iHour=(int)(lday/3600);
long lhour= (lday%(60*60));
iMin=(int)(lhour/60);
long lmin= (lhour%(60));
iSec=(int)(lmin);
day.setText(String.valueOf(iDay).toString()+" Day ");
hour.setText(String.valueOf(myFormat.format(iHour)).toString());
min.setText(":"+String.valueOf(myFormat.format(iMin)).toString());
sec.setText(":"+String.valueOf(myFormat.format(iSec)).toString());
counter = new MyCount(iSec*1000,1000);
counter.start();}
} catch (ParseException e) {
e.printStackTrace();
} 	};     });
Button btnStop=(Button)findViewById(R.id.btnstop);
btnStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
counter.cancel();
}         }); }
public class MyCount extends CountDownTimer{
public MyCount(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
@Override
public void onFinish() {
counter = new MyCount(60000,1000);
counter.start();
iMin-=1;
if(iMin>-1)
min.setText(":"+String.valueOf(myFormat.format(iMin)).toString());
else{
iMin=59;
min.setText(":"+String.valueOf(myFormat.format(iMin)).toString());
iHour-=1;
if(iHour>-1)
hour.setText(String.valueOf(myFormat.format(iHour)).toString());
else{
iHour=11;
hour.setText(String.valueOf(myFormat.format(iHour)).toString());
iDay-=1;
if(iDay>-1)
day.setText(" "+String.valueOf(iDay).toString());
else{
day.setText("Time is over");
hour.setText("");
min.setText("");
sec.setText("");
counter.cancel();
}}}}
@Override
public void onTick(long millisUntilFinished) {
sec.setText(":"+String.valueOf(myFormat.format(millisUntilFinished/1000)));
}}}

Tal como se puede ver, la idea es tener dos objetos Date, startDate y endDate, correspondiente con la fecha inicial y final, respectivamente, en el formato yyyy-MM-dd HH:mm:ss, y a partir de ahi realizar los cálculos.

🙂