Показать Анимированный GIF

Я хочу отображать анимированные изображения GIF в моем приложении. Как я узнал, жесткий Android не поддерживает анимированный GIF изначально.

Однако он может отображать анимацию с помощью AnimationDrawable:

Разработка> Руководства> Изображения и графика> Обзор чертежей

В примере используется анимация, сохраненная как кадры в ресурсах приложения, но мне нужно отображать анимированный gif напрямую.

Мой план состоит в том, чтобы разбить анимированный GIF на фреймы и добавить каждый фрейм как возможно для анимации AnimationDrawable.

Кто-нибудь знает, как извлечь кадры из анимированного GIF и преобразовать каждый из них в Drawable?

Ответ 1

Android действительно может декодировать и отображать анимированные GIF файлы, используя класс android.graphics.Movie.

Это не слишком документировано, но находится в SDK Reference. Кроме того, он используется в Примеры в ApiDemos в формате BitmapDecode с некоторым анимированным флагом.

Ответ 2

ОБНОВИТЬ:

Используйте скольжение:

dependencies {
  implementation 'com.github.bumptech.glide:glide:4.0.0'
}

использование:

Glide.with(context).load(GIF_URI).into(new GlideDrawableImageViewTarget(IMAGE_VIEW));

см документы

Ответ 3

также помещается (main/assets/htmls/name.gif) [с этим html настраивается на размер]

<html style="margin: 0;">
<body style="margin: 0;">
<img src="name.gif" style="width: 100%; height: 100%" />
</body>
</html>

объявите в своем Xml, например, как это (main/res/layout/name.xml): [вы определяете размер, например]

<WebView
android:layout_width="70dp"
android:layout_height="70dp"
android:id="@+id/webView"
android:layout_gravity="center_horizontal" />

в вашей деятельности поместите следующий код внутри onCreate

web = (WebView) findViewById(R.id.webView); 
web.setBackgroundColor(Color.TRANSPARENT); //for gif without background
web.loadUrl("file:///android_asset/htmls/name.html");

Если вы хотите динамически загружать нагрузку, вам необходимо загрузить webview с данными:

// or "[path]/name.gif" (e.g: file:///android_asset/name.gif for resources in asset folder), and in loadDataWithBaseURL(), you don't need to set base URL, on the other hand, it similar to loadData() method.
String gifName = "name.gif";
String yourData = "<html style=\"margin: 0;\">\n" +
        "    <body style=\"margin: 0;\">\n" +
        "    <img src=" + gifName + " style=\"width: 100%; height: 100%\" />\n" +
        "    </body>\n" +
        "    </html>";
// Important to add this attribute to webView to get resource from outside.
webView.getSettings().setAllowFileAccess(true);

// Notice: should use loadDataWithBaseURL. BaseUrl could be the base url such as the path to asset folder, or SDCard or any other path, where your images or the other media resides related to your html
webView.loadDataWithBaseURL("file:///android_asset/", yourData, "text/html", "utf-8", null);
// Or if you want to load image from SD card or where else, here is the idea.
String base = Environment.getExternalStorageDirectory().getAbsolutePath().toString();
webView.loadDataWithBaseURL(base + '/', yourData, "text/html", "utf-8", null);

: лучше загрузить gif со статическими изображениями для получения дополнительной информации. https://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html

Что, надеюсь, вы поможете.

Ответ 4

Я решил проблему, разделив анимацию gif на фреймы, прежде чем сохранять ее на телефоне, поэтому мне не придется иметь дело с ней в Android.

Затем я загружаю каждый фрейм на телефон, создаю Drawable из него, а затем создаю AnimationDrawable - очень похоже на пример из моего вопроса

Ответ 5

я нашел очень простой способ, с хорошим и простым рабочим примером здесь

отображать анимированный виджет

Прежде чем заставить его работать, в коде есть несколько способов сделать

В СЛЕДУЮЩЕМ

    @Override
    public void onCreate(Bundle savedInstanceState){    
        super.onCreate(savedInstanceStated);   
        setContentView(new MYGIFView());
    }    
}

просто замените

setContentView(new MYGIFView());

в

setContentView(new MYGIFView(this));

И IN

public GIFView(Context context) {
    super(context);

Предоставьте свой собственный файл анимации gif

    is = context.getResources().openRawResource(R.drawable.earth);
    movie = Movie.decodeStream(is);
}

ЗАМЕНИТЕ ПЕРВУЮ ЛИНИЮ

public MYGIFView(Context context) {

в соответствии с именем класса...

после выполнения этих небольших изменений он должен работать для меня...

надеюсь, что эта помощь

Ответ 6

Способы показать анимированный GIF на Android:

  • Класс фильма. Как упоминалось выше, это довольно плохо.
  • WebView. Он очень прост в использовании и обычно работает. Но иногда он начинает плохо себя вести, и это всегда на некоторых неясных устройствах, которых у вас нет. Кроме того, вы не можете использовать несколько экземпляров в любом виде списка, потому что это делает вещи в вашей памяти. Тем не менее, вы можете рассматривать его как основной подход.
  • Пользовательский код для декодирования gifs в растровые изображения и отображения их как Drawable или ImageView. Я упомянул две библиотеки:

https://github.com/koral--/android-gif-drawable - декодер реализован на C, поэтому он очень эффективен.

https://code.google.com/p/giffiledecoder - декодер реализован на Java, поэтому с ним проще работать. Все еще достаточно эффективен даже при больших файлах.

Вы также найдете множество библиотек на основе класса GifDecoder. Это также основанный на Java декодер, но он работает, загружая весь файл в память, поэтому он применим только к небольшим файлам.

Ответ 7

Мне очень тяжело было иметь анимированный gif, работающий в Android. У меня были только две следующие работы:

  1. WebView
  2. ион

WebView работает нормально и очень легко, но проблема заключается в том, что просмотр загружается медленнее, и приложение будет неактуально в течение секунды или около того. Мне это не понравилось. Поэтому я пробовал разные подходы (НЕ РАБОТАЕТ):

  1. ImageViewEx устарел!
  2. picasso не загружал анимированный gif
  3. Android-gif-drawable отлично смотрится, но это вызвало некоторые связанные с NDK проблемы в моем проекте. Это заставило мою локальную библиотеку NDK перестать работать, и я не смог ее исправить

У меня были некоторые взад и вперед с Ion; Наконец, я работаю, и это очень быстро :-)

Ion.with(imgView)
  .error(R.drawable.default_image)
  .animateGif(AnimateGifMode.ANIMATE)
  .load("file:///android_asset/animated.gif");

Ответ 8

Используйте ImageViewEx библиотеку, которая упрощает использование gif с помощью ImageView.

Ответ 9

Glide

Библиотека загрузчика изображений для Android, рекомендованная Google.

  • Glide очень похож на Picasso, но это намного быстрее, чем Picasso.
  • Glide потребляет меньше памяти, чем Picasso.

То, что имеет эта Glide, но Пикассо не

Возможность загрузки GIF-анимации в простой ImageView может быть самой интересной особенностью Glide. И да, вы не можете сделать это с Пикассо. введите описание изображения здесь Некоторые важные ссылки -

Ответ 10

Никто не упомянул Ion или Glide библиотека. они работают очень хорошо.

Легче обрабатывать по сравнению с WebView.

Ответ 11

У меня был успех с предложенным решением в этой статье, класс под названием GifMovieView, который отображает View, который затем может быть отображен или добавлен в конкретный ViewGroup. Ознакомьтесь с другими методами, представленными в частях 2 и 3 указанной статьи.

Единственным недостатком этого метода является то, что сглаживание в фильме не так хорошо (должно быть побочным эффектом использования "тенистого" Android Movie класса). Затем вам лучше установить фон в сплошной цвет в вашем анимированном GIF.

Ответ 12

Попробуйте это, обновите отображаемый gif файл кода.

loading_activity.xml(в папке Layout)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff" >

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:indeterminate="true"
        android:indeterminateDrawable="@drawable/custom_loading"
        android:visibility="gone" />

</RelativeLayout>

custom_loading.xml(в выделенной папке)

здесь я ставлю black_gif.gif(в папке с возможностью переноса), вы можете разместить свой собственный gif здесь

<?xml version="1.0" encoding="utf-8"?>
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/black_gif"
    android:pivotX="50%"
    android:pivotY="50%" />

LoadingActivity.java(в папке res)

public class LoadingActivity extends Activity {

    ProgressBar bar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_loading);
        bar = (ProgressBar) findViewById(R.id.progressBar);
        bar.setVisibility(View.VISIBLE);

    }

}

Ответ 13

Некоторые мысли на примере BitmapDecode... В основном он использует древний, но довольно беспламенный класс Movie из android.graphics. В последних версиях API вам нужно отключить аппаратное ускорение как описано здесь. Для меня это было неприятно.

<activity
            android:hardwareAccelerated="false"
            android:name="foo.GifActivity"
            android:label="The state of computer animation 2014">
</activity>

Вот пример BitmapDecode, сокращенный только с частью GIF. Вы должны сделать свой собственный виджет (вид) и нарисовать его самостоятельно. Не такой мощный, как ImageView.

import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.*;
import android.view.View;

public class GifActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new GifView(this));
    }

    static class GifView extends View {
        Movie movie;

        GifView(Context context) {
            super(context);
            movie = Movie.decodeStream(
                    context.getResources().openRawResource(
                            R.drawable.some_gif));
        }
        @Override
        protected void onDraw(Canvas canvas) {   
            if (movie != null) {
                movie.setTime(
                    (int) SystemClock.uptimeMillis() % movie.duration());
                movie.draw(canvas, 0, 0);
                invalidate();
            }
        }
    }
}

2 других метода, один с ImageView другой с WebView можно найти в этом прекрасном учебнике. Метод ImageView использует лицензионный android-gifview от Google.

Ответ 14

@PointerNull дал хорошее решение, но это не идеально. Он не работает на некоторых устройствах с большими файлами и демонстрирует багги-анимацию Gif с дельта-кадрами по предварительной версии ICS. Я нашел решение без этих ошибок. Это библиотека с встроенным декодированием: koral android-gif-drawable.

Ответ 15

Поместите его в WebView, он должен иметь возможность отображать его правильно, поскольку браузер по умолчанию поддерживает файлы gif. (Froyo +, если я не ошибаюсь)

Ответ 16

Glide 4.6

1. Загрузить gif

GlideApp.with(context)
            .load(R.raw.gif) // or url
            .into(imageview);

2. Чтобы получить файл-объект

GlideApp.with(context)
                .asGif()
                .load(R.raw.gif) //or url
                .into(new SimpleTarget<GifDrawable>() {
                    @Override
                    public void onResourceReady(@NonNull GifDrawable resource, @Nullable Transition<? super GifDrawable> transition) {

                        resource.start();
                      //resource.setLoopCount(1);
                        imageView.setImageDrawable(resource);
                    }
                });

Ответ 17

Только для Android API (Android Pie) 28 и +

используйте AnimatedImageDrawable как
// ImageView from layout
val ima : ImageView = findViewById(R.id.img_gif)
// create AnimatedDrawable 
val decodedAnimation = ImageDecoder.decodeDrawable(
        // create ImageDecoder.Source object
        ImageDecoder.createSource(resources, R.drawable.tenor))
// set the drawble as image source of ImageView
ima.setImageDrawable(decodedAnimation)
// play the animation
(decodedAnimation as? AnimatedImageDrawable)?.start()

XML-код, добавьте ImageView

<ImageView
    android:id="@+id/img_gif"
    android:background="@drawable/ic_launcher_background" <!--Default background-->
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    android:layout_width="200dp"
    android:layout_height="200dp" />

AnimatedImageDrawable - это дочерний элемент Drawable и созданный с помощью ImageDecoder.decodeDrawable

ImageDecoder.decodeDrawable который дополнительно потребовал экземпляр ImageDecoder.Source созданный ImageDecoder.createSource.

ImageDecoder.createSource может использовать источник только как имя, ByteBuffer, File, resourceId, URI, ContentResolver для создания исходного объекта и использует его для создания AnimatedImageDrawable как Drawable (полиморфный вызов)

static ImageDecoder.Source  createSource(AssetManager assets, String fileName)
static ImageDecoder.Source  createSource(ByteBuffer buffer)
static ImageDecoder.Source  createSource(File file)
static ImageDecoder.Source  createSource(Resources res, int resId)
static ImageDecoder.Source  createSource(ContentResolver cr, Uri uri)

Примечание. Вы также можете создать Bitmap с помощью ImageDecoder # decodeBitmap.

Выход:

Pi22j.gif

AnimatedDrawable также поддерживает изменение размеров, управление кадрами и цветом

Ответ 18

Я думаю, что лучшая библиотека для обработки gif файлов эта: koral

Используется, и я успешный, и эта библиотека посвящена GIF'S; но где picasso и glide - рамки общего назначения; поэтому я думаю, что разработчики этой библиотеки полностью сосредоточились на gif файлах

Ответ 20

Есть два варианта загрузки анимированных gif в наши приложения для Android

1) Использование Glide для загрузки gif в ImageView.

    String urlGif = "https://cdn.dribbble.com/users/263558/screenshots/1337078/dvsd.gif";
    //add Glide implementation into the build.gradle file.
    ImageView imageView = (ImageView)findViewById(R.id.imageView);
    Uri uri = Uri.parse(urlGif);
    Glide.with(getApplicationContext()).load(uri).into(imageView);

2) Использование html для загрузки gif в WebView

Создайте html с адресом в файл.gif:

<html style="margin: 0;">
<body style="margin: 0;">
<img src="https://..../myimage.gif" style="width: 100%; height: 100%" />
</body>
</html>

сохраните этот файл в каталоге активов:

enter image description here

Загрузите этот html в WebView вашего приложения:

    WebView webView =  (WebView)findViewById(R.id.webView);
    webView = (WebView) findViewById(R.id.webView);
    webView.loadUrl("file:///android_asset/html/webpage_gif.html");

Heres - полный пример этих двух вариантов.

enter image description here

Ответ 21

Прежде всего, браузер Android должен поддерживать Анимированные GIF файлы. Если это не так, это ошибка! Посмотрите на трекеры проблемы.

Если вы показываете эти анимированные GIF файлы за пределами браузера, это может быть другая история. Чтобы выполнить то, что вы запрашиваете, потребуется внешняя библиотека, которая поддерживает декодирование Анимированных GIF.

Первым портом вызова будет просмотр API Java2D или JAI (Java Advanced Imaging), хотя я был бы очень удивлен, если Android Dalvik будет поддерживать эти библиотеки в вашем приложении.

Ответ 22

Подобно тому, что сказал @Leonti, но с немного большей глубиной:

То, что я сделал для решения одной и той же проблемы, - это открыть GIMP, скрыть все слои, кроме одного, экспортировать его как свое собственное изображение, а затем скрыть этот слой и отобразить следующий и т.д., пока у меня не будет отдельных файлов ресурсов для каждого. Затем я мог бы использовать их в качестве фреймов в XML файле AnimationDrawable.

Ответ 23

Я решил это, разделив gif на фреймы и использую стандартную андроидную анимацию

Ответ 24

Что-то я сделал для показа gifs в приложениях. Я расширил ImageView, чтобы люди могли свободно использовать его атрибуты. Он может отображать gifs из url или из каталога ресурсов. В библиотеке также упрощается расширение классов, чтобы наследовать от нее и расширить ее, чтобы поддерживать различные методы для инициализации gif.

https://github.com/Gavras/GIFView

Там небольшой справочник на странице github.

Он также был опубликован в Android Arsenal:

https://android-arsenal.com/details/1/4947

Пример использования:

Из XML:

<com.whygraphics.gifview.gif.GIFView xmlns:gif_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/main_activity_gif_vie"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:scaleType="center"
        gif_view:gif_src="url:http://pop.h-cdn.co/assets/16/33/480x264/gallery-1471381857-gif-season-2.gif" />

В деятельности:

    GIFView mGifView = (GIFView) findViewById(R.id.main_activity_gif_vie);

    mGifView.setOnSettingGifListener(new GIFView.OnSettingGifListener() {
                @Override
                public void onSuccess(GIFView view, Exception e) {
                    Toast.makeText(MainActivity.this, "onSuccess()", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onFailure(GIFView view, Exception e) {

        }
});

Настройка gif программно:

mGifView.setGifResource("asset:gif1");

Ответ 25

Самый простой способ - можно рассмотреть приведенный ниже код

Мы можем использовать Imageview setImageResource, см. Ниже код для него.

Приведенный ниже код можно использовать для показа изображения, например, gif, если у вас есть несколько разделенных изображений gif. Просто разделите gif на отдельные png из онлайн-инструмента и поместите изображение в drawable, как показано ниже.

image_1.png, image_2.png и т.д.

Попросите обработчика изменить изображение динамически.

int imagePosition = 1;
    Handler handler = new Handler();
        Runnable runnable = new Runnable() {
            public void run() {
                updateImage();
            }
        };




    public void updateImage() {

                appInstance.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        int resId = getResources().getIdentifier("image_" + imagePosition, "drawable", appInstance.getPackageName());
                        gifImageViewDummy.setImageResource(resId);
                        imagePosition++;
    //Consider you have 30 image for the anim
                        if (imagePosition == 30) {
//this make animation play only once
                            handler.removeCallbacks(runnable);

                        } else {
    //You can define your own time based on the animation
                            handler.postDelayed(runnable, 50);
                        }

//to make animation to continue use below code and remove above if else
// if (imagePosition == 30)
//imagePosition = 1;
// handler.postDelayed(runnable, 50);
// 
                    }
                });
              }

Ответ 26

Легкий способ отображения анимированного GIF непосредственно из URL-адреса в макет приложения - использовать класс WebView.

Шаг 1. В вашем макете XML

<WebView
android:id="@+id/webView"
android:layout_width="50dp"
android:layout_height="50dp"
/>

Ответ 27

Просто хотел добавить, что класс Movie теперь устарел.

Этот класс устарел на уровне API P.

Рекомендуется использовать это

AnimatedImageDrawable

Возможность рисования анимированных изображений (например, GIF).

Ответ 29

Вы можете использовать библиотеку GifAnimationDrawable, найденную в этой ссылке - https://github.com/Hipmob/gifanimateddrawable, и которые конвертируют любой gif в AnimationDrawable. Наслаждайтесь:)

Ответ 30

public class Test extends GraphicsActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(new SampleView(this));
  }

  private static class SampleView extends View {
    private Bitmap mBitmap;
    private Bitmap mBitmap2;
    private Bitmap mBitmap3;
    private Bitmap mBitmap4;
    private Drawable mDrawable;

    private Movie mMovie;
    private long mMovieStart;

    // Set to false to use decodeByteArray
    private static final boolean DECODE_STREAM = true;

    private static byte[] streamToBytes(InputStream is) {
      ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
      byte[] buffer = new byte[1024];
      int len;
      try {
        while ((len = is.read(buffer)) >= 0) {
          os.write(buffer, 0, len);
        }
      } catch (java.io.IOException e) {
      }
      return os.toByteArray();
    }

    public SampleView(Context context) {
      super(context);
      setFocusable(true);

      java.io.InputStream is;
      is = context.getResources().openRawResource(R.drawable.icon);

      BitmapFactory.Options opts = new BitmapFactory.Options();
      Bitmap bm;

      opts.inJustDecodeBounds = true;
      bm = BitmapFactory.decodeStream(is, null, opts);

      // now opts.outWidth and opts.outHeight are the dimension of the
      // bitmap, even though bm is null

      opts.inJustDecodeBounds = false; // this will request the bm
      opts.inSampleSize = 4; // scaled down by 4
      bm = BitmapFactory.decodeStream(is, null, opts);

      mBitmap = bm;

      // decode an image with transparency
      is = context.getResources().openRawResource(R.drawable.icon);
      mBitmap2 = BitmapFactory.decodeStream(is);

      // create a deep copy of it using getPixels() into different configs
      int w = mBitmap2.getWidth();
      int h = mBitmap2.getHeight();
      int[] pixels = new int[w * h];
      mBitmap2.getPixels(pixels, 0, w, 0, 0, w, h);
      mBitmap3 = Bitmap.createBitmap(pixels, 0, w, w, h,
          Bitmap.Config.ARGB_8888);
      mBitmap4 = Bitmap.createBitmap(pixels, 0, w, w, h,
          Bitmap.Config.ARGB_4444);

      mDrawable = context.getResources().getDrawable(R.drawable.icon);
      mDrawable.setBounds(150, 20, 300, 100);

      is = context.getResources().openRawResource(R.drawable.animated_gif);

      if (DECODE_STREAM) {
        mMovie = Movie.decodeStream(is);
      } else {
        byte[] array = streamToBytes(is);
        mMovie = Movie.decodeByteArray(array, 0, array.length);
      }
    }

    @Override
    protected void onDraw(Canvas canvas) {
      canvas.drawColor(0xFFCCCCCC);

      Paint p = new Paint();
      p.setAntiAlias(true);

      canvas.drawBitmap(mBitmap, 10, 10, null);
      canvas.drawBitmap(mBitmap2, 10, 170, null);
      canvas.drawBitmap(mBitmap3, 110, 170, null);
      canvas.drawBitmap(mBitmap4, 210, 170, null);

      mDrawable.draw(canvas);

      long now = android.os.SystemClock.uptimeMillis();
      if (mMovieStart == 0) { // first time
        mMovieStart = now;
      }
      if (mMovie != null) {
        int dur = mMovie.duration();
        if (dur == 0) {
          dur = 1000;
        }
        int relTime = (int) ((now - mMovieStart) % dur);
        mMovie.setTime(relTime);
        mMovie.draw(canvas, getWidth() - mMovie.width(), getHeight()
            - mMovie.height());
        invalidate();
      }
    }
  }
}

class GraphicsActivity extends Activity {
  // set to true to test Picture
  private static final boolean TEST_PICTURE = false;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  }

  @Override
  public void setContentView(View view) {
    if (TEST_PICTURE) {
      ViewGroup vg = new PictureLayout(this);
      vg.addView(view);
      view = vg;
    }

    super.setContentView(view);
  }
}

class PictureLayout extends ViewGroup {
  private final Picture mPicture = new Picture();

  public PictureLayout(Context context) {
    super(context);
  }

  public PictureLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  @Override
  public void addView(View child) {
    if (getChildCount() > 1) {
      throw new IllegalStateException(
          "PictureLayout can host only one direct child");
    }

    super.addView(child);
  }

  @Override
  public void addView(View child, int index) {
    if (getChildCount() > 1) {
      throw new IllegalStateException(
          "PictureLayout can host only one direct child");
    }

    super.addView(child, index);
  }

  @Override
  public void addView(View child, LayoutParams params) {
    if (getChildCount() > 1) {
      throw new IllegalStateException(
          "PictureLayout can host only one direct child");
    }

    super.addView(child, params);
  }

  @Override
  public void addView(View child, int index, LayoutParams params) {
    if (getChildCount() > 1) {
      throw new IllegalStateException(
          "PictureLayout can host only one direct child");
    }

    super.addView(child, index, params);
  }

  @Override
  protected LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(LayoutParams.MATCH_PARENT,
        LayoutParams.MATCH_PARENT);
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    final int count = getChildCount();

    int maxHeight = 0;
    int maxWidth = 0;

    for (int i = 0; i < count; i++) {
      final View child = getChildAt(i);
      if (child.getVisibility() != GONE) {
        measureChild(child, widthMeasureSpec, heightMeasureSpec);
      }
    }

    maxWidth += getPaddingLeft() + getPaddingRight();
    maxHeight += getPaddingTop() + getPaddingBottom();

    Drawable drawable = getBackground();
    if (drawable != null) {
      maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
      maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
    }

    setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
        resolveSize(maxHeight, heightMeasureSpec));
  }

  private void drawPict(Canvas canvas, int x, int y, int w, int h, float sx,
      float sy) {
    canvas.save();
    canvas.translate(x, y);
    canvas.clipRect(0, 0, w, h);
    canvas.scale(0.5f, 0.5f);
    canvas.scale(sx, sy, w, h);
    canvas.drawPicture(mPicture);
    canvas.restore();
  }

  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(mPicture.beginRecording(getWidth(), getHeight()));
    mPicture.endRecording();

    int x = getWidth() / 2;
    int y = getHeight() / 2;

    if (false) {
      canvas.drawPicture(mPicture);
    } else {
      drawPict(canvas, 0, 0, x, y, 1, 1);
      drawPict(canvas, x, 0, x, y, -1, 1);
      drawPict(canvas, 0, y, x, y, 1, -1);
      drawPict(canvas, x, y, x, y, -1, -1);
    }
  }

  @Override
  public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
    location[0] = getLeft();
    location[1] = getTop();
    dirty.set(0, 0, getWidth(), getHeight());
    return getParent();
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    final int count = super.getChildCount();

    for (int i = 0; i < count; i++) {
      final View child = getChildAt(i);
      if (child.getVisibility() != GONE) {
        final int childLeft = getPaddingLeft();
        final int childTop = getPaddingTop();
        child.layout(childLeft, childTop,
            childLeft + child.getMeasuredWidth(),
            childTop + child.getMeasuredHeight());

      }
    }
  }
}