2010-11-13 6 views
5

Quando si crea un LiveWallpaper in Android 2.2+, si ottiene una tela (o qualsiasi altra cosa l'equivalente 3D) su cui disegnare. Mi piacerebbe disegnare alcuni elementi usando gli strumenti integrati dell'interfaccia utente Android piuttosto che creare tutto da zero usando i comandi canvas o caricando un bitmap UI pre-renderizzato.Utilizzo delle risorse di layout in un LiveWallpaper su Android

La conversione di una singola vista in una bitmap funziona correttamente. cioè funziona perfettamente:

// 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); 

Tuttavia, la conversione di un LinearLayout con i bambini causa problemi. È solo ottenere il LinearLayout stesso e nessuno dei suoi figli. Ad esempio, se imposto che LinearLayout abbia uno sfondo bianco, ottengo una casella bianca ben renderizzata, ma nessuno dei bambini di TextView si trova nella bitmap. Ho anche provato a utilizzare DrawingCache con risultati simili.

Il codice che sto usando è l'esempio del cubo con le uniche modifiche che sono un comando extra di disegno. Il LinearLayout funziona bene come un brindisi o come una vista normale (ad esempio, tutto si mostra bene), sul LiveWallpaper tutto ciò che ottengo è lo sfondo di LinearLayout reso.

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); 

Qualcuno sa se è necessario fare qualcosa di speciale per far visualizzare correttamente i bambini alla mia bitmap? Devo in qualche modo fare qualcosa di speciale per rendere il layout di rendering il resto dei bambini? Devo scrivere una funzione per fare ricorsivamente qualcosa a tutti i bambini?

Potrei comporre tutto da solo ma, dal momento che il display è abbastanza statico (cioè disegno una volta sola e conservo una copia della bitmap per disegnare sullo sfondo) questo mi sembra più facile e comunque abbastanza efficiente.

Edit: Scavando un po 'più nello stato del layout sembra come se il layout non sta procedendo verso il basso l'albero di vista (cioè il LinearLayout ottiene il suo layout computerizzata quando chiamo il layout(), ma i bambini avere una dimensione nulla (0x0)). Secondo il post di Romain Guy nel 2008 android developer post. Devi aspettare il passaggio del layout o forzare il layout da solo. Il problema è: come posso aspettare un passaggio di layout da un motore di carta da parati per un LinearLayout che non è collegato al gruppo di visualizzazione di root? E come posso impaginare manualmente ogni elemento figlio se il layout richiede di impostare il lato sinistro, superiore, destro, inferiore quando non so che cosa dovrebbero essere.

Ho provato a chiamare forceLayout sui bambini ma non sembra essere d'aiuto. Non sono sicuro di come il framework di layout funzioni dietro le quinte (oltre a un layout a due passaggi). C'è un modo per farlo manualmente passare il layout, cioè adesso? Dal momento che non si tratta di un'attività, non penso che molte delle normali cose di base stiano accadendo nel modo in cui mi piacerebbe.

+0

hai implementato la soluzione proposta?Sto valutando lo stesso approccio, ma vorrei imparare dalle tue esperienze – dparnas

risposta

8

Gli sfondi dal vivo sono stati progettati in modo specifico per NON utilizzare i widget dell'interfaccia utente standard. Comunque è possibile usarli comunque. Dovrai forzare il passaggio di un layout da prima chiamata measure() sulla vista, quindi layout(). Puoi trovare maggiori informazioni nel mio presentation.

+13

@JustinBuser Sono uno degli ingegneri che hanno progettato e implementato sfondi dal vivo sul team di Android. Le classi che hai menzionato non hanno nulla a che fare con i widget UI standard di cui stiamo parlando. Il punto è che non è possibile aggiungere viste direttamente a uno sfondo animato (usando setContentView(), addView(), ecc.) Ad esempio, un ListView. La presentazione a cui mi sono collegato riguarda il modo in cui misurare e visualizzare le visualizzazioni. –

+1

La presentazione spiega come chiamare measure() e layout() su Views. Una volta che una vista è stata misurata e tracciata, può essere disegnata su una tela, ad esempio su uno sfondo animato. –

3

Ecco un esempio di un gruppo di viste, pulsante e visualizzazione di immagini disposti e visualizzati in un Live Wallpapers. È anche possibile aggirare il bug del token della finestra nulla e aggiungere viste direttamente tramite WindowManager se si imposta il tipo di finestra su 0. È necessario rilevare l'eccezione che genera e i risultati sono in qualche modo irregolari ma funzionano per la maggior 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

Ed è per questo che non possiamo avere cose belle ... "Devi prendere l'eccezione che getta ei risultati sono in qualche modo irregolari ma funziona per la maggior parte." Veramente??? – Dan

+3

La mia risposta è stata per lo più una risposta a quello che afferma "I Live Wallpapers sono stati appositamente progettati per NON usare i widget standard dell'interfaccia utente." che è una risposta ridicola e fuorviante. È come dire che gli aerei erano "progettati specificamente per non essere sommergibili". Il mio punto è fondamentalmente che solo perché non si crea qualcosa di impermeabile non significa che non può essere fatto. I 15 minuti trascorsi a scrivere quel codice di esempio dimostrano che in effetti PUO 'essere fatto, tuttavia non mi sentivo davvero propenso a debugarlo accuratamente dopo aver provato il mio punto, da qui l'avvertimento su come ottenere un errore. –

+0

questa soluzione funziona davvero! : D il codice è un po 'incasinato e manca un po' di codice, ma ha funzionato. Ho una domanda però. Ho una sottoclasse che estende GLSurfaceView, che ha un oggetto 3D in fase di rendering, come posso far visualizzare entrambi in cui l'oggetto 3D è sullo sfondo con ImageView in primo piano? Grazie! –

Problemi correlati