在Android上的LiveWallpaper中使用布局资源

在Android上的LiveWallpaper中使用布局资源,android,canvas,android-layout,android-linearlayout,Android,Canvas,Android Layout,Android Linearlayout,在Android 2.2+中创建LiveWallpaper时,您可以在画布(或任何3D等价物)上绘制。我希望使用内置的Android UI工具绘制一些元素,而不是使用画布命令或加载预渲染的UI位图从头开始构建所有内容 将单个视图转换为位图效果良好。i、 e.这很有效: // For example this works: TextView view = new TextView(ctx); view.layout(0, 0, 200, 100); view.setText("test"); Bi

在Android 2.2+中创建LiveWallpaper时,您可以在画布(或任何3D等价物)上绘制。我希望使用内置的Android UI工具绘制一些元素,而不是使用画布命令或加载预渲染的UI位图从头开始构建所有内容

将单个视图转换为位图效果良好。i、 e.这很有效:

// 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);
但是,转换与儿童的线性布局会带来问题。你只得到线性布局本身,没有一个是孩子。例如,如果我将LinearLayout设置为具有白色背景,我会得到一个渲染良好的白色框,但没有TextView子项在位图中。我也尝试过使用DrawingCache,得到了类似的结果

我使用的代码是立方体示例,唯一的变化是一个额外的draw命令。LinearLayout可以用作祝酒词或常规视图(即,所有内容都很好地显示),在LiveWallpaper上,我得到的只是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);
有人知道你是否需要做一些特殊的事情来让孩子们正确地渲染到我的位图中吗?i、 我是否需要做一些特殊的事情来让布局呈现其余的孩子?我应该写一个函数递归地对所有的孩子做些什么吗

我可以自己合成所有内容,但是,由于显示是相当静态的(即,我绘制了一次,并保留了位图的副本以在背景上绘制),这对我来说似乎更容易,而且仍然相当有效

编辑: 在深入了解布局状态的同时,它看起来好像布局没有沿着视图树向下移动(即,当我调用Layout()时,LinearLayout会计算其布局,但子项的大小为null(0x0))。根据Romain Guy在2008年的帖子。您必须等待布局通过或自己强制布局。问题是,对于未附着到根视图组的线性布局,如何等待墙纸引擎的布局传递?如果布局要求您设置左、上、右、下,而我不知道这些应该是什么,那么如何手动布局每个子元素呢


我试着给孩子们打电话,但似乎也没用。我不确定布局框架在幕后是如何工作的(除此之外,它还有两次布局)。是否有一种方法可以手动使其完成布局过程,即现在?由于这不是一项活动,我不认为很多正常的背景内容都是按照我所希望的方式进行的。

Live墙纸是专门设计的,不使用标准的UI小部件。但是,无论如何都可以使用它们。您必须首先在视图上调用measure(),然后调用layout(),从而强制自己通过布局。您可以在my.

中找到更多信息。下面是一个视图组、按钮和图像视图的示例,它们在实时壁纸中布局和显示。如果将窗口类型设置为0,还可以解决空窗口标记错误,并直接通过WindowManager添加视图。您必须捕获它抛出的异常,结果有些不稳定,但它在大多数情况下都有效

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 )
                    {
                    }

            }

    }

您是否实施了建议的解决方案?我正在评估相同的方法,但希望从您的experiences@JustinBuser我是Android团队中设计和实现实时壁纸的工程师之一。您提到的类与我们正在讨论的标准UI小部件无关。关键是不能直接将视图添加到实时墙纸(使用setContentView()、addView()等),例如ListView。我链接到的演示文稿是关于如何自己测量和布局视图的。演示文稿介绍了如何在视图上调用measure()和layout()。一旦一个视图被测量和布局,它就可以被绘制到画布上,比如在一张活的壁纸上。这就是为什么我们不能拥有美好的事物。。。“你必须抓住它抛出的异常,结果有些不稳定,但它在大多数情况下都有效。”真的吗???我的回答主要是对“实时墙纸是非常专门设计的,不使用标准UI小部件。”这是一个荒谬和误导性的回答。这就好比说飞机是“专门设计的,不是用来潜水的”。我的观点是,仅仅因为你不做防水的东西并不意味着它不能做。我花了15分钟编写该示例代码,证明它实际上是可以完成的,但是在证明了我的观点之后,我并没有真正想彻底调试它,因此出现了关于捕获错误的警告。此解决方案确实有效!:代码有点混乱,缺少一些代码,但确实有效。不过我有个问题。我有一个子类extends GLSURFACHEVIEW,它有一个正在渲染的3D对象,如果3D对象位于背景中,我如何使它们都显示在顶部的ImageView中?谢谢