2010-11-13 32 views
5

Lorsque vous créez un LiveWallpaper sous Android 2.2+, vous obtenez un canevas (ou l'équivalent 3D) sur lequel dessiner. Je voudrais dessiner certains éléments en utilisant les outils intégrés de l'interface utilisateur Android plutôt que de tout construire à partir de zéro en utilisant des commandes de canevas ou en chargeant une image bitmap d'interface utilisateur pré-rendue.Utilisation des ressources de mise en page dans un LiveWallpaper sur Android

La conversion d'une seule vue en bitmap fonctionne correctement. c'est-à-dire que cela fonctionne 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); 

Mais, la conversion d'un LinearLayout avec des enfants pose des problèmes. Vous seulement obtenir le LinearLayout lui-même et aucun de ses enfants. Par exemple, si je mets le LinearLayout à un fond blanc, je reçois une boîte blanche bien rendue, mais aucun des enfants TextView n'est dans le Bitmap. J'ai également essayé d'utiliser DrawingCache avec des résultats similaires.

Le code que j'utilise est l'exemple de cube avec les seules modifications étant une commande de dessin supplémentaire. Le LinearLayout fonctionne bien comme un toast ou comme une vue régulière (c'est-à-dire que tout se montre bien), sur le LiveWallpaper, tout ce que j'ai, c'est l'arrière-plan de LinearLayout.

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

Est-ce que quelqu'un sait si vous avez besoin de faire quelque chose de spécial pour que les enfants soient rendus correctement à mon bitmap? c'est-à-dire que dois-je faire quelque chose de spécial pour que la mise en page rende le reste des enfants? Devrais-je écrire une fonction pour faire récursivement quelque chose à tous les enfants?

Je pourrais tout composer moi-même mais, comme l'affichage est assez statique (je dessine une fois et garde une copie de l'image pour dessiner en arrière-plan), cela me semble plus facile et efficace.

Edit: En creusant un peu plus dans l'état de la mise en page il semble que la mise en page ne progresse pas dans l'arbre de vue (la LinearLayout obtient sa mise en page calculée quand je l'appelle la mise en page() mais les enfants avoir une taille nulle (0x0)). Selon le message de Romain Guy en 2008 android developer post. Vous devez attendre le passage de mise en page ou forcer la mise en page vous-même. Le problème est comment puis-je attendre un passage de disposition d'un moteur de papier peint pour un LinearLayout qui n'est pas attaché au groupe de vue racine? Et comment puis-je mettre en page manuellement chaque élément enfant si la mise en page nécessite que vous définissiez la gauche, le haut, la droite, le bas lorsque je ne sais pas ce que cela devrait être.

J'ai essayé d'appeler forceLayout sur les enfants mais cela ne semble pas aider non plus. Je ne suis pas sûr de la façon dont le cadre de mise en page fonctionne dans les coulisses (en plus de cela il fait une mise en page en deux passes). Y a-t-il un moyen de faire manuellement passer la mise en page, c'est-à-dire maintenant? Puisque ce n'est pas une activité, je ne pense pas que beaucoup de choses se passent comme je le souhaite.

+0

avez-vous mis en œuvre la solution proposée?J'évalue la même approche, mais je voudrais apprendre de vos expériences – dparnas

Répondre

8

Live Wallpapers ont été spécialement conçus pour ne pas utiliser les widgets UI standard. Cependant, il est possible de les utiliser quand même. Vous devrez forcer une mise en page en appelant d'abord measure() sur la vue, puis layout(). Vous pouvez trouver plus d'informations dans mon presentation.

+13

@JustinBuser Je suis l'un des ingénieurs qui ont conçu et mis en œuvre des fonds d'écran animés sur l'équipe Android. Les cours que vous mentionnez n'ont rien à voir avec les widgets standard de l'interface utilisateur dont nous parlons. Le fait est que vous ne pouvez pas ajouter des vues directement à un fond d'écran en direct (en utilisant setContentView(), addView(), etc.) Par exemple, un ListView. La présentation à laquelle je suis lié concerne la façon de mesurer et de mettre en page les Vues. –

+1

La présentation explique comment appeler measure() et layout() sur Views. Une fois qu'une vue a été mesurée et disposée, elle peut être dessinée sur un canevas, par exemple sur un fond d'écran en direct. –

3

Voici un exemple de groupe de vues, de boutons et d'images projetés et affichés dans un fond d'écran animé. Vous pouvez également contourner le bogue de jeton de fenêtre null et ajouter des vues directement via WindowManager si vous définissez le type de fenêtre sur 0. Vous devez attraper l'exception qu'il lance et les résultats sont quelque peu erratiques mais cela fonctionne pour la plupart.

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

Et c'est pourquoi nous ne pouvons pas avoir de belles choses ... "Vous devez attraper l'exception qu'il lance et les résultats sont quelque peu erratiques mais cela fonctionne pour la plupart." Vraiment??? – Dan

+3

Ma réponse était surtout une réponse à celle qui déclare que "Live Wallpapers ont été spécialement conçus pour ne pas utiliser les widgets de l'interface utilisateur standard." ce qui est une réponse ridicule et trompeuse. C'est comme dire que les avions ont été «spécifiquement conçus pour ne pas être submersibles». Mon point de vue est fondamentalement que ce n'est pas parce que vous ne faites pas quelque chose de preuve de l'eau que cela ne peut pas être fait. Les 15 minutes que j'ai passées à écrire ce code d'exemple prouvent que cela peut effectivement être fait, mais je ne me suis pas vraiment senti enclin à le déboguer complètement après avoir prouvé mon point, d'où l'avertissement d'attraper une erreur. –

+0

cette solution fonctionne réellement! : D le code est un peu foiré et il manque un peu de code mais ça a marché. Cependant j'ai une question. J'ai une sous-classe étend GLSurfaceView, qui a un objet 3D étant rendu, comment pourrais-je les deux montré dans lequel l'objet 3D est à l'arrière-plan le ImageView sur le dessus? Merci! –