2010-11-13 11 views
5

Cuando crea un LiveWallpaper en Android 2.2+ obtiene un lienzo (o lo que sea que el equivalente 3D) para dibujar. Me gustaría dibujar algunos elementos utilizando las herramientas integradas de la interfaz de usuario de Android en lugar de construir todo desde cero utilizando comandos canvas o cargando un mapa de bits UI preprocesado.Uso de recursos de diseño en LiveWallpaper en Android

La conversión de una única vista a un mapa de bits funciona bien. es decir, esto funciona muy bien:

// For example this works: 
TextView view = new TextView(ctx); 
view.layout(0, 0, 200, 100); 
view.setText("test"); 
Bitmap b = Bitmap.createBitmap(200, 100, Bitmap.Config.ARGB_8888);     
Canvas tempC = new Canvas(b); 
view.draw(tempC); 
c.drawBitmap(b, 200, 100, mPaint); 

Pero, la conversión de un LinearLayout con los niños causa problemas. Usted solo obtiene el LinearLayout mismo y ninguno de ellos es hijo. Por ejemplo, si configuro LinearLayout para que tenga un fondo blanco, obtendré un cuadro blanco bien renderizado, pero ninguno de los elementos TextView está en el mapa de bits. También intenté usar DrawingCache con resultados similares.

El código que estoy usando es el ejemplo de cubo con los únicos cambios que son un comando extra de dibujo. LinearLayout funciona bien como un brindis o como una vista normal (es decir, todo se muestra bien), en el LiveWallpaper todo lo que obtengo es el fondo de LinearLayout prestado.

inflater = (LayoutInflater)getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
layout = (LinearLayout) inflater.inflate(com.example.android.livecubes.R.layout.testLinearLayout, null); 
layout.layout(0, 0, 400, 200); 

Bitmap b = Bitmap.createBitmap(400, 200, Bitmap.Config.ARGB_8888); 
Canvas tempC = new Canvas(b); 
layout.draw(tempC); 
c.drawBitmap(b, 10, 200, mPaint); 

¿Alguien sabe si necesita hacer algo especial para que los niños se reproduzcan correctamente en mi mapa de bits? es decir, ¿tengo que hacer algo especial para que el diseño represente al resto de los niños? ¿Debo escribir una función para hacer algo recursivamente a todos los niños?

Pude componer todo yo solo pero, dado que la pantalla es bastante estática (es decir, dibujo esta vez y guardo una copia del mapa de bits para dibujar en el fondo), esto me parece más fácil y aún bastante eficiente.

Editar: Mientras que cavar un poco más en el estado de la Disposición parece que la disposición no está progresando por el árbol de vista (es decir, el LinearLayout obtiene su diseño computarizada cuando llamo layout() pero los niños tener un tamaño nulo (0x0)). De acuerdo con la publicación de Romain Guy en 2008 android developer post. Tienes que esperar el paso de disposición o forzar el diseño tú mismo. El problema es cómo puedo esperar un paso de diseño de un motor de papel pintado para un LinearLayout que no está conectado al grupo de vistas raíz. ¿Y cómo puedo diseñar manualmente cada elemento secundario si el diseño requiere que configure el izquierdo, el superior, el derecho, el fondo cuando no sé cuáles deberían ser?

He intentado llamar a forceLayout en los niños, pero tampoco parece ser de ayuda. No estoy seguro de cómo funciona el marco de diseño detrás de escena (además de que tiene un diseño de dos pasos). ¿Hay alguna manera de hacerlo manualmente en el pase de diseño, es decir, ahora mismo? Como no es una actividad, no creo que muchas de las cosas de fondo estén sucediendo del modo que me gustaría.

+0

¿implementó la solución propuesta?Estoy evaluando el mismo enfoque, pero me gustaría aprender de sus experiencias – dparnas

Respuesta

8

Los fondos de pantalla en vivo fueron diseñados específicamente para NO utilizar widgets de interfaz de usuario estándar. Sin embargo, es posible usarlos de todos modos. Tendrá que forzar un paso de diseño primero llamando a measure() en la vista, luego en layout(). Puede encontrar más información en mi presentation.

+13

@JustinBuser Soy uno de los ingenieros que diseñó e implementó fondos de pantalla en vivo en el equipo de Android. Las clases que mencionas no tienen nada que ver con los widgets UI estándar de los que estamos hablando. El punto es que no puede agregar vistas directamente a un fondo de pantalla en vivo (usando setContentView(), addView(), etc.) Por ejemplo, un ListView. La presentación a la que me he vinculado es acerca de cómo medir y diseñar vistas usted mismo. –

+1

La presentación explica cómo llamar a measure() y layout() en Views. Una vez que se ha medido y distribuido una vista, se puede dibujar en un lienzo, por ejemplo, en un fondo de pantalla en vivo. –

3

Aquí hay un ejemplo de grupo de vista, botón y vista de imagen establecidos y mostrados en un Live Wallpapers. También puede solucionar el error de token de la ventana nula y agregar vistas directamente a través de WindowManager si establece el tipo de ventana en 0. Tiene que atrapar la excepción que arroja y los resultados son algo erráticos, pero funciona en su mayor parte.

import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.PixelFormat; 
import android.service.wallpaper.WallpaperService; 
import android.util.Log; 
import android.view.Gravity; 
import android.view.SurfaceHolder; 
import android.view.SurfaceHolder.Callback; 
import android.view.ViewGroup; 
import android.view.WindowManager; 
import android.view.WindowManager.LayoutParams; 
import android.widget.Button; 
import android.widget.ImageView; 
import android.widget.LinearLayout; 

public class UIWidgetWallpaper extends WallpaperService 
    { 

     private final String TAG   = getClass().getSimpleName(); 
     final static int  pixFormat = PixelFormat.RGBA_8888; 
     protected ImageView  imageView; 
     protected WindowManager windowManager; 
     protected LayoutParams layoutParams; 
     protected WidgetGroup widgetGroup; 
     protected SurfaceHolder surfaceHolder; 
     protected Button  button; 

     @Override 
     public Engine onCreateEngine() 
      { 
       Log.i(TAG, "onCreateEngine"); 
       return new UIWidgetWallpaperEngine(); 
      } 

     public class WidgetGroup extends ViewGroup 
      { 

       private final String TAG = getClass().getSimpleName(); 

       public WidgetGroup(Context context) 
        { 
         super(context); 
         Log.i(TAG, "WidgetGroup"); 
         setWillNotDraw(true); 
        } 

       @Override 
       protected void onLayout(boolean changed, int l, int t, int r, int b) 
        { 
         layout(l, t, r, b); 
        } 

      } 

     public class UIWidgetWallpaperEngine extends Engine implements Callback 
      { 

       private final String TAG = getClass().getSimpleName(); 

       @Override 
       public void onCreate(SurfaceHolder holder) 
        { 
         Log.i(TAG, "onCreate"); 
         super.onCreate(holder); 
         surfaceHolder = holder; 
         surfaceHolder.addCallback(this); 
         imageView = new ImageView(getApplicationContext()); 
         imageView.setClickable(false); 
         imageView.setImageResource(R.drawable.icon); 
         widgetGroup = new WidgetGroup(getApplicationContext()); 
         widgetGroup.setBackgroundDrawable(getWallpaper()); 
         widgetGroup.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); 
         widgetGroup.setAddStatesFromChildren(true); 
         holder.setFormat(pixFormat); 
         LinearLayout.LayoutParams imageParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 
         imageParams.weight = 1.0f; 
         imageParams.gravity = Gravity.CENTER; 
         widgetGroup.addView(imageView, imageParams); 
         button = new Button(getApplicationContext()); 
         button.setText("Test Button"); 
         LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 
         buttonParams.weight = 1.0f; 
         buttonParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; 
         widgetGroup.addView(button, buttonParams); 
        } 

       @Override 
       public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 
        { 
         Log.i(TAG, "surfaceChanged: "); 
         synchronized(surfaceHolder) 
          { 
           Canvas canvas = surfaceHolder.lockCanvas(); 
           widgetGroup.layout(0, 0, width, height); 
           imageView.layout(0, 0, width/2, height); 
           button.layout(width/2, height - 100, width, height); 
           widgetGroup.draw(canvas); 
           surfaceHolder.unlockCanvasAndPost(canvas); 
          } 
        } 

       @Override 
       public void surfaceCreated(SurfaceHolder holder) 
        { 
        } 

       @Override 
       public void surfaceDestroyed(SurfaceHolder holder) 
        { 
        } 

      } 

    } 
+0

Y es por eso que no podemos tener cosas bonitas ... "Hay que atrapar la excepción que arroja y los resultados son algo erráticos, pero funciona en su mayor parte". ¿¿¿De Verdad??? – Dan

+3

Mi respuesta fue principalmente una respuesta a la que dice "Los fondos de pantalla en vivo fueron diseñados específicamente para NO usar widgets UI estándar". que es una respuesta ridícula y engañosa. Es como decir que los aviones fueron "diseñados específicamente para no ser sumergibles". Mi punto es básicamente que el hecho de que no se haga algo resistente al agua no significa que no se pueda hacer. Los 15 minutos que pasé escribiendo ese código de ejemplo prueban que, de hecho, PUEDE hacerse, sin embargo, no me sentí realmente inclinado a depurarlo completamente después de probar mi punto, por lo tanto, la advertencia sobre detectar un error. –

+0

¡esta solución realmente funciona! : D el código está un poco desordenado y le falta un código, pero funcionó. Tengo una pregunta sin embargo. Tengo una subclase que amplía GLSurfaceView, que tiene un objeto 3D siendo renderizado, ¿cómo podría hacer que se muestren ambos en los cuales el objeto 3D está en el fondo y el ImageView en la parte superior? ¡Gracias! –

Cuestiones relacionadas