Crear una pantalla de bienvenida real

¿Cómo puedo crear una verdadera pantalla de bienvenida en Android? No quiero temporizadores ni demoras. Sólo una pantalla de bienvenida que se muestra hasta que se haya cargado la aplicación.

Algo como

public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.splash); handler = new Handler(); new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { //Do some heavy stuff return null; } @Override public void onPostExecute(Void result){ handler.post(new Runnable(){ @Override public void run(){ setContentView(R.layout.main); } }); } }.execute(); } 

Una solución, con código, aparece a continuación que parece haber funcionado perfectamente en una serie de dispositivos de prueba durante las últimas seis semanas.

Sin embargo, hay algunos preliminares que deben ser considerados antes de sumergirse en una pantalla de bienvenida completa.

En primer lugar, si puede evitar la necesidad de una pantalla de bienvenida mediante la presentación de la vista principal de su aplicación de inmediato, dando al usuario acceso inmediato a su aplicación, que es su mejor opción.

A menudo puede lograr esto al mostrar inmediatamente los gráficos de la pantalla principal y, a continuación, crear un hilo de trabajo para realizar tareas de inicialización que consumen mucho tiempo, como cargar una tabla que siempre utiliza la aplicación.

Sin embargo, es posible que los gráficos de su vista principal tarden mucho tiempo en configurar y mostrar, y desea que se vea otra cosa durante esa inicialización.

Ahora, si su actividad principal tiene un fondo simple (por ejemplo, predeterminado), claro o negro, no transparente, que proporcionará una confirmación inmediata de que al menos algo está ocurriendo cuando el usuario inicia su aplicación. Los temas de fondo que personalmente he encontrado para funcionar como pantallas primitivas "splash" incluyen lo siguiente (que se agregará a la etiqueta de actividad de su actividad principal en su archivo de manifiesto):

 android:theme="@style/Theme.Light.NoTitleBar" android:theme="@style/Theme.Black.NoTitleBar" 

Me gustaría señalar a este respecto que si su actividad de fondo requiere cualquier tipo de mapa de bits, o animación, u otro dibujable más allá de un tema por defecto (o un simple tema claro o negro como se muestra arriba), mi experiencia es que el fondo de la actividad no se mostrará Hasta que su vista principal se ha mostrado de todos modos, y tan simplemente cambiando el fondo de su actividad a sí mismo su pantalla de bienvenida no (en mi experiencia) lograr una respuesta más inmediata de lo que su pantalla principal ya proporciona.

Aunque los temas sencillos anteriores funcionarán como "salpicaduras" primitivas, tal vez pienses que una simple luz o fondo de actividad negro es demasiado desagradable como señal de que tu aplicación se ha iniciado y deseas algo que muestre el nombre o el logotipo de tu aplicación mientras Usuario está esperando. O, quizás su fondo de actividad debe ser transparente , porque desea poder superponer alguna otra aplicación con las vistas de su propia aplicación (un fondo transparente, por supuesto, es invisible durante el inicio y, por lo tanto, no indicará al usuario que su aplicación ha comenzado).

Si, después de considerar todas las alternativas presentadas anteriormente, usted todavía piensa que usted necesita una pantalla de bienvenida, aquí es un enfoque que personalmente he encontrado para trabajar muy bien.

Para este enfoque, necesitará definir una nueva clase que extienda LinearLayout. La razón por la que necesitas tu propia clase es porque necesitas recibir una confirmación positiva de que tu pantalla de bienvenida se ha mostrado en realidad, de modo que puedas pasar inmediatamente a mostrar tu vista principal sin un poco de cronómetro que sólo puede adivinar cuánto tiempo tomará Su pantalla de bienvenida para que aparezca. Me gustaría señalar a este respecto que si se inicia la visualización de su vista principal demasiado rápido después de mostrar su vista de bienvenida, la vista de bienvenida nunca se verá; Utilizando este enfoque se evita esa posibilidad.

He aquí un ejemplo de tal clase:

 public class SplashView extends LinearLayout { public interface SplashEvents { //This event is signaled after the splash and all of its child views, // if any, have been drawn. // As currently implemented, it will trigger BEFORE any scrollbars are drawn. // We are assuming that there will BE no scrollbars on a SplashView. public void onSplashDrawComplete(); } private SplashEvents splashEventHandler = null; public void setSplashEventHandler(SplashEvents splashEventHandler) { this.splashEventHandler = splashEventHandler; } private void fireSplashDrawCompleteEvent() { if(this.splashEventHandler != null) { this.splashEventHandler.onSplashDrawComplete(); } } public SplashView(Context context) { super(context); //This is set by default for a LinearLayout, but just making sure! this.setWillNotDraw(true); //If the cache is not enabled, then I think that helps to ensure that // dispatchDraw override WILL // get called. Because if caching were enabled, then the //drawing might not occur. this.setDrawingCacheEnabled(false); setGravity(Gravity.CENTER); //This splices in your XML definition (see below) to the SplashView layout LayoutInflater.from(context).inflate(R.layout.splashscreen, this, true); } @Override protected void dispatchDraw(Canvas canvas) { //Draw any child views super.dispatchDraw(canvas); //Signal client objects (in this case, your main activity) that // we have finished initializing and drawing the view. fireSplashDrawCompleteEvent(); } } 

Debido a que estamos cargando nuestro XML desde dentro de la vista, necesitamos definirlo en XML usando una etiqueta <merge> para "empalmar" en los elementos definidos por XML como hijos de nuestra clase SplashView. Aquí hay un ejemplo (que se puede colocar en la carpeta res / layout de tu aplicación), que puedes adaptar a tus propias necesidades:

 <merge xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center_horizontal" > <TextView android:id="@+id/tvHeading" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:textSize="30dp" android:textStyle="bold" android:text="Loading..." android:layout_weight="1.0" android:textColor="#00ff00" android:background="#AA000000" /> </merge> 

Tenga en cuenta que el TextView se define con un fondo negro translúcido, por lo que causará un oscurecimiento de la pantalla del lanzador, con el texto "Cargando …" superpuesto en la parte superior en verde.

Todo lo que queda es editar algo como lo siguiente en (y encima) el método onCreate () de su actividad principal:

 private Handler uiThreadHandler = new Handler(); @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); //Create an instance of the splash view, and perform a setContentView() SplashView splashView = new SplashView(this); //Doing this allows you to access the "this" pointer of the main // activity inside the Runnable below. final main mainThis = this; // Set an event handler on the SplashView object, so that as soon // as it completes drawing we are // informed. In response to that cue, we will *then* put up the main view, // replacing the content view of the main activity with that main view. splashView.setSplashEventHandler(new SplashView.SplashEvents() { @Override public void onSplashDrawComplete() { //Post the runnable that will put up the main view uiThreadHandler.post(new Runnable() { @Override public void run() { //This method is where you will have moved // the entire initialization of your app's // main display, which normally would have been // in your onCreate() method prior to adding the // splash display. launchMainView(mainThis, savedInstanceState); } }); } }); //This will cause your splash view to draw. When it finishes, it will trigger the code above. this.setContentView(splashView); //At this point, do *not* move directly on to performing a setContentView() on your main view. // If you do, you will never see the splash view at all. // You simply wait for the splash view to signal you that it has completed drawing itself, and // *then* call launchMainView(), which will itself call setContentView() again, passing it // your main view. } //Here is a stripped-down version of launchMainView(). You will typically have some additional // initializations here - whatever might have been present originally in your onCreate() method. public void launchMainView(main mainThis, Bundle savedInstanceState) { myRootView = new MyRootView(mainThis); setContentView(myRootView); } 

El enfoque anterior está funcionando muy bien para mí. Lo he utilizado sólo para el API nivel 8 y he probado ese código en varios dispositivos, incluidos teléfonos y tablets, con Android 2.2.1, 2.3.3 y 4.0.1 (ICS).

Advertencia:

La responsabilidad potencial del enfoque anterior es la posibilidad de que, por alguna combinación de circunstancias, la vista de salpicadura no signifique que se ha completado y, por lo tanto, la salpicadura quedaría "pegada" en la pantalla principal, sin vista principal para reemplazarla . Eso nunca me ha ocurrido, pero me gustaría solicitar comentarios aquí con respecto a si la anulación de dispatchDraw () en el SplashView anterior podría no ser llamado. Realizé una inspección visual del código que dispara dispatchDraw (), y me parece que siempre se llamará, dadas las inicializaciones que he hecho en el constructor de SplashView.

Si alguien tiene un mejor método para anular para el mismo propósito, agradecería escucharlo. Me sorprendió que no fue capaz de encontrar ninguna anulación específicamente adaptada al fuego cuando una vista había terminado de mostrar, y por lo tanto, si existe uno y de alguna manera lo perdí, por favor, publicar un comentario sobre eso a continuación. Comentarios que afirman que este enfoque funcionará también son muy bienvenidos!

No es tan dificil; Usted apenas crea una visión que usted utilizará como su pantalla de bienvenida (una que tiene la disposición simple y no requiere porciones de medir), fijó eso como el contenido para la actividad usando setContentView;

A continuación, llame a setContentView en la actividad de nuevo con el diseño complejo que tarda un tiempo en generar. Incluso puede utilizar Asynctask para cargar datos antes de llamar a setContent la segunda vez con su diseño complejo. Eso depende de si está limitado por la carga de datos o el edificio de la vista.

 public class MyLocationListener extends Activity { public Handler myHandler = new Handler(){ public void handlerMessage(Message msg){ set you contentView here after splash screen } } public MyLocationListener(Context context){ this.context = context; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.splashscreen); // don't set Content View here instead start a thread here to do the task yiou want to do. // Now post message from new thread to UI Thread using handlers. } } 

La mejor manera de hacer una pantalla de bienvenida es:

  1. Si el usuario presiona hacia atrás, debe ir rápidamente a la pantalla principal.
  2. Sin animación

Llegué a esta buena solución:

 import android.app.Activity; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.view.WindowManager; import br.eti.fml.android.sigame.R; import java.util.concurrent.atomic.AtomicBoolean; public class LauncherActivity extends Activity { private AsyncTask goingToNextScreen; private AtomicBoolean alreadyShown = new AtomicBoolean(false); public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.launcher); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); //noinspection unchecked goingToNextScreen = new AsyncTask() { @Override protected Object doInBackground(Object... objects) { try { Thread.sleep(1500); } catch (InterruptedException e) { // ignores } return null; } @Override protected void onPostExecute(Object o) { goNext(); } }.execute(); } @Override public void onBackPressed() { if (goingToNextScreen != null) { goingToNextScreen.cancel(true); goNext(); } } private void goNext() { if (alreadyShown.compareAndSet(false, true)) { startActivity(new Intent(LauncherActivity.this, HomeActivity.class)); overridePendingTransition(0, 0); finish(); overridePendingTransition(0, 0); } } } 

En algún momento, con splash, la aplicación necesita unos milisegundos o segundos para cargar el contenido de la Actividad.

Si sólo desea una "imagen de backgorund" como de costumbre pantalla de bienvenida. Creo que la mejor manera es usando Temas.

Utilizando SherlockActionBar por ejemplo:

 <style name="SplashTheme" parent="Theme.Sherlock.NoActionBar"> ... <item name="android:windowBackground">@drawable/splash</item> ... </style> 

Donde splash puede ser un archivo .9 para llenar la pantalla.

Y la Actividad en el Manifiesto debe ser algo como

  <activity android:name=".SplashActivity" ... android:theme="@style/SplashTheme" ...> ... </activity> 

Entonces no necesita la línea setContent (Ver) en su código. Y el tema se cargará más rápido que el contenido.

Esto le permite tener una pantalla de inicio desde el inicio de la carga de la aplicación. Sin ventanas negras o barras de acción o algo así.

// código para el código Splash

 public class SplashScreen extends Activity { static int SPLASH_TIMEOUT = 5000; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.splash_layout); new Handler().postDelayed(new Runnable() { @Override public void run() { startActivity(new Intent(SplashScreen.this, MainActivity.class)); finish(); } }, SPLASH_TIMEOUT); } } 

Aquí SPLASH_TIMEOUT definirá cuánto tiempo debe mostrar su propia actividad, así que cambie este valor de acuerdo a su necesidad.

// código para MainActivity.class

 public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } } 
FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.