Использование ресурсов макета в LiveWallpaper на Android

Когда вы создаете LiveWallpaper в Android 2.2+, вы получаете холст (или любой другой 3D-эквивалент) для рисования. Я хотел бы рисовать некоторые элементы с помощью встроенных инструментов пользовательского интерфейса Android, а не создавать все с нуля, используя команды холста или загрузку предварительно обработанного растрового изображения пользовательского интерфейса.

Преобразование одного представления в растровое изображение работает нормально. то есть это прекрасно работает:

// 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 с дочерними элементами вызывает проблемы. Вы только получаете сам LinearLayout и ни один из его дочерних элементов. Например, если я задаю для LinearLayout белый фон, я получаю хорошо отрисованное белое поле, но ни один из дочерних элементов TextView не находится в растровом изображении. Я также пытался использовать DrawingCache с аналогичными результатами.

Код, который я использую, представляет собой пример куба с единственными изменениями, заключающимися в дополнительной команде рисования. 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);

Кто-нибудь знает, нужно ли вам делать что-то особенное, чтобы дети правильно отображались в моем растровом изображении? то есть мне нужно как-то сделать что-то особенное, чтобы макет отображал остальные дочерние элементы? Должен ли я написать функцию, которая рекурсивно что-то делает со всеми дочерними элементами?

Я мог бы скомпоновать все сам, но, поскольку отображение довольно статично (т.е. я рисую это один раз и сохраняю копию растрового изображения для рисования на фоне), это кажется мне проще и все же довольно эффективно.

Редактировать: При более глубоком изучении состояния макета кажется, что макет не продвигается вниз по дереву представления (т.е. LinearLayout вычисляет свой макет, когда я вызываю layout(), но дочерние элементы имеют нулевой (0x0) размер). Согласно сообщению Romain Guy в 2008 году сообщение разработчика Android . Вам нужно дождаться прохождения макета или форсировать макет самостоятельно. Проблема в том, как я могу дождаться прохождения макета от механизма обоев для LinearLayout, который не подключен к корневой группе просмотра? И как я могу вручную разместить каждый дочерний элемент, если макет требует, чтобы вы установили левый, верхний, правый, нижний, когда я не знаю, какими они должны быть.

Я пытался вызывать forceLayout для детей, но это тоже не помогает. Я не уверен, как фреймворк макета работает за кулисами (помимо того, что он делает двухпроходный макет). Есть ли способ вручную заставить его выполнить макет, то есть прямо сейчас? Поскольку это не действие, я не думаю, что многие обычные фоновые вещи происходят так, как мне хотелось бы.


person William    schedule 13.11.2010    source источник
comment
Вы реализовали предложенное решение? Я оцениваю тот же подход, но хотел бы учиться на вашем опыте   -  person dparnas    schedule 12.01.2012


Ответы (2)


Живые обои были специально разработаны, чтобы НЕ использовать стандартные виджеты пользовательского интерфейса. Однако использовать их в любом случае можно. Вам придется принудительно передать макет самостоятельно, сначала вызвав метод Measure() в представлении, а затем layout(). Дополнительную информацию можно найти в моей презентации.

person Romain Guy    schedule 15.02.2011
comment
@JustinBuser Я один из инженеров, разработавших и внедривших живые обои в команде Android. Упомянутые вами классы не имеют ничего общего со стандартными виджетами пользовательского интерфейса, о которых мы говорим. Дело в том, что вы не можете добавлять представления непосредственно в живые обои (используя setContentView(), addView() и т. д.). Например, ListView. Презентация, на которую я ссылаюсь, посвящена тому, как самостоятельно измерять и компоновать представления. - person Romain Guy; 02.11.2012
comment
В презентации объясняется, как вызвать метод Measure() и layout() в представлениях. После того, как представление было измерено и размещено, его можно нарисовать на холсте, например, на живых обоях. - person Romain Guy; 16.11.2012

Вот пример группы представлений, кнопок и изображений, размещенных и отображаемых в Live Wallpapers. Вы также можете обойти ошибку маркера пустого окна и добавить представления непосредственно через WindowManager, если вы установите тип окна на 0. Вы должны поймать исключение, которое оно выдает, и результаты несколько ошибочны, но это работает по большей части.

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

            }

    }
person Justin Buser    schedule 15.11.2012
comment
И именно поэтому у нас не может быть хороших вещей... Вы должны поймать исключение, которое оно выдает, и результаты несколько ошибочны, но по большей части это работает. Действительно??? - person Dan; 02.12.2012
comment
Мой ответ был в основном ответом на тот, в котором говорится, что живые обои были специально разработаны, чтобы НЕ использовать стандартные виджеты пользовательского интерфейса. что является нелепым и вводящим в заблуждение ответом. Это все равно, что сказать, что самолеты были специально спроектированы так, чтобы их нельзя было погружать в воду. Моя точка зрения в основном заключается в том, что если вы не делаете что-то водонепроницаемым, это не значит, что это невозможно сделать. 15 минут, которые я потратил на написание этого примера кода, доказывают, что это на самом деле МОЖЕТ быть сделано, однако я действительно не чувствовал склонности тщательно отлаживать его после доказательства своей точки зрения, отсюда и предупреждение об обнаружении ошибки. - person Justin Buser; 01.01.2013
comment
это решение действительно работает! :D код немного перепутался и не хватает нескольких кодов, но он работал. У меня есть вопрос. У меня есть подкласс, расширяющий GLSurfaceView, в котором визуализируется 3D-объект, как я могу показать их оба, в которых 3D-объект находится на заднем плане, а ImageView сверху? Спасибо! - person ponnex; 17.02.2016