Afficher GIF animé

Je veux afficher des images GIF animées dans mon application. Comme j’ai découvert le difficile, Android ne supporte pas nativement les GIF animés.

Cependant, il peut afficher des animations à l’aide d’ AnimationDrawable :

Développer> Guides> Images et graphiques> Présentation des Drawables

L’exemple utilise l’animation enregistrée en tant qu’images dans les ressources de l’application, mais ce dont j’ai besoin, c’est d’afficher directement GIF animé.

Mon plan est de séparer les images GIF animées des images et d’append chaque image à AnimationDrawable.

Est-ce que quelqu’un sait comment extraire des images de GIF animé et les convertir en Drawable ?

En fait, Android peut décoder et afficher des GIF animés en utilisant la classe android.graphics.Movie.

Ce n’est pas trop documenté, mais est en référence SDK . En outre, il est utilisé dans Samples dans ApiDemos dans l’ exemple BitmapDecode avec un drapeau animé.

METTRE À JOUR:

Utiliser la glisse:

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

usage:

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

voir docs

ancienne réponse:

Ceci est une excellente bibliothèque:

https://github.com/koral–/android-gif-drawable

Utilisation de XML:

  

Utilisation de JAVA:

 GifImageView givImageView = (GifImageView)findViewById...; 

GifDrawable peut être construit directement à partir de différentes sources:

  //asset file GifDrawable gifFromAssets = new GifDrawable( getAssets(), "anim.gif" ); //resource (drawable or raw) GifDrawable gifFromResource = new GifDrawable( getResources(), R.drawable.anim ); //byte array byte[] rawGifBytes = ... GifDrawable gifFromBytes = new GifDrawable( rawGifBytes ); //FileDescriptor FileDescriptor fd = new RandomAccessFile( "/path/anim.gif", "r" ).getFD(); GifDrawable gifFromFd = new GifDrawable( fd ); //file path GifDrawable gifFromPath = new GifDrawable( "/path/anim.gif" ); //file File gifFile = new File(getFilesDir(),"anim.gif"); GifDrawable gifFromFile = new GifDrawable(gifFile); //AssetFileDescriptor AssetFileDescriptor afd = getAssets().openFd( "anim.gif" ); GifDrawable gifFromAfd = new GifDrawable( afd ); //InputStream (it must support marking) InputStream sourceIs = ... BufferedInputStream bis = new BufferedInputStream( sourceIs, GIF_LENGTH ); GifDrawable gifFromStream = new GifDrawable( bis ); //direct ByteBuffer ByteBuffer rawGifBytes = ... GifDrawable gifFromBytes = new GifDrawable( rawGifBytes ); 

et setImageDrawable:

 givImageView.setImageDrawable(GifDrawable); 

également mis (main / assets / htmls / https://stackoverflow.com/questions/3660209/display-animated-gif/name.gif) [avec ce html ajuster à la taille]

      

déclarez par exemple dans votre fichier XML comme ceci (main / res / layout / name.xml): [vous définissez la taille, par exemple]

  

dans votre activité mettez le code suivant à l’intérieur de onCreate

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

Si vous voulez charger dynamicment, vous devez charger la vue Web avec des données:

 // or "[path]/https://stackoverflow.com/questions/3660209/display-animated-gif/name.gif" (eg: file:///android_asset/https://stackoverflow.com/questions/3660209/display-animated-gif/name.gif for resources in asset folder), and in loadDataWithBaseURL(), you don't need to set base URL, on the other hand, it's similar to loadData() method. Ssortingng gifName = "http://soffr.miximages.com/java/name.gif"; Ssortingng yourData = "\n" + " \n" + " \n" + " \n" + " "; // Important to add this atsortingbute 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. Ssortingng base = Environment.getExternalStorageDirectory().getAbsolutePath().toSsortingng(); webView.loadDataWithBaseURL(base + '/', yourData, "text/html", "utf-8", null); 

suggestion: il est préférable de charger gif avec des images statiques pour plus d’informations, consultez https://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html

C’est ça, j’espère que vous aidez.

J’ai résolu le problème en divisant les animations GIF en images avant de l’enregistrer sur le téléphone, de sorte que je n’aurais pas eu à traiter avec Android.

Ensuite, je télécharge chaque image sur le téléphone, en crée un puis crée AnimationDrawable – très similaire à l’exemple de ma question

J’ai trouvé un moyen très simple, avec un exemple de travail agréable et simple ici

afficher un widget animé

Avant de le faire fonctionner, il y a des tâches à faire dans le code

DANS CE QUI SUIT

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

juste remplacer

 setContentView(new MYGIFView()); 

dans

 setContentView(new MYGIFView(this)); 

ET EN

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

Fournissez votre propre fichier d’animation gif

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

REMPLACER LA PREMIÈRE LIGNE DANS

 public MYGIFView(Context context) { 

selon le nom de la classe …

Après avoir fait cela, peu de changements devraient fonctionner comme pour moi …

j’espère que cette aide

Façons de montrer GIF animé sur Android:

  • Classe de cinéma Comme mentionné ci-dessus, c’est assez buggé.
  • WebView C’est très simple à utiliser et fonctionne habituellement . Mais parfois, cela commence à mal se comporter, et c’est toujours sur certains appareils obscurs que vous n’avez pas. De plus, vous ne pouvez pas utiliser plusieurs instances dans aucun type de vue de liste, car cela fait des choses dans votre mémoire. Cependant, vous pourriez le considérer comme une approche primaire.
  • Code personnalisé pour décoder les gifs en bitmaps et les afficher en tant que Drawable ou ImageView. Je mentionnerai deux bibliothèques:

https://github.com/koral–/android-gif-drawable – le décodeur est implémenté en C, il est donc très efficace.

https://code.google.com/p/giffiledecoder – decoder est implémenté en Java, ce qui facilite son utilisation. Toujours assez efficace, même avec des fichiers volumineux.

Vous trouverez également de nombreuses bibliothèques basées sur la classe GifDecoder. C’est aussi un décodeur basé sur Java, mais cela fonctionne en chargeant le fichier entier dans la mémoire, ainsi il est seulement applicable aux petits fichiers.

J’ai eu beaucoup de mal à faire fonctionner le gif animé sous Android. Je n’avais que deux travailleurs suivants:

  1. WebView
  2. Ion

WebView fonctionne bien et très facilement, mais le problème est que cela ralentit l’affichage et que l’application ne répond plus une seconde. Je n’ai pas aimé ça. J’ai donc essayé différentes approches (NE FONCTIONNE PAS):

  1. ImageViewEx est obsolète!
  2. picasso n’a pas chargé de gif animé
  3. android-gif-drawable a l’ air génial, mais cela a causé des problèmes avec le NDK dans mon projet. Cela a provoqué l’arrêt de ma bibliothèque NDK locale et je n’ai pas pu le réparer

J’en ai eu des allers-retours avec Ion ; Enfin, je le fais fonctionner, et il est vraiment rapide 🙂

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

Utilisez ImageViewEx , une bibliothèque qui permet d’utiliser un gif aussi facilement qu’avec ImageView .

Glide

Image Loader Library pour Android, recommandé par Google.

  • Glide est assez similaire à Picasso mais c’est beaucoup plus rapide que Picasso.
  • Glide consum moins de mémoire que Picasso.

Qu’est-ce que Glide a mais Picasso n’a pas

Une possibilité de charger GIF Animation dans un simple ImageView pourrait être la fonctionnalité la plus intéressante de Glide. Et oui, tu ne peux pas faire ça avec Picasso. entrer la description de l'image ici Quelques liens importants

  1. https://github.com/bumptech/glide
  2. http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en

Personne n’a mentionné la bibliothèque Ion ou Glide . ils fonctionnent très bien.

Il est plus facile à gérer par rapport à une WebView.

J’ai eu du succès avec la solution proposée dans cet article , une classe appelée GifMovieView , qui rend une View qui peut ensuite être affichée ou ajoutée à un ViewGroup spécifique. Découvrez les autres méthodes présentées dans les parties 2 et 3 de l’article spécifié.

Le seul inconvénient de cette méthode est que l’anti-aliasing sur le film n’est pas très bon (doit être un effet secondaire de l’utilisation de la classe de Movie Android “ombreuse”). Il est alors préférable de définir l’arrière-plan sur une couleur unie dans votre fichier GIF animé.

Essayez ceci, ci-dessous le fichier gif d’affichage de code dans la barre de progression

loading_activity.xml (dans le dossier Layout)

    

custom_loading.xml (dans le dossier pouvant être dessiné)

ici je mets black_gif.gif (dans le dossier à dessiner), vous pouvez mettre votre propre gif ici

 < ?xml version="1.0" encoding="utf-8"?>  

LoadingActivity.java (dans le dossier 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); } } 

Quelques reflections sur l’exemple BitmapDecode … Fondamentalement, il utilise la classe Movie ancienne, mais plutôt sans caractère de android.graphics. Sur les versions récentes de l’API, vous devez désactiver l’accélération matérielle, comme décrit ici . C’était pour moi autrement.

   

Voici l’exemple BitmapDecode raccourci avec seulement la partie GIF. Vous devez créer votre propre Widget (View) et le dessiner par vous-même. Pas aussi puissant qu’un 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(); } } } } 

Deux autres méthodes, l’une avec ImageView avec WebView, se trouvent dans ce didacticiel . La méthode ImageView utilise la licence android-gifview d’ Apache de Google Code.

@PointerNull a donné une bonne solution, mais ce n’est pas parfait. Il ne fonctionne pas sur certains appareils avec de gros fichiers et affiche des animations Gif avec des images delta sur la version pré-ICS. J’ai trouvé la solution sans ces bugs. C’est une bibliothèque avec décodage natif à dessiner: koral’s android-gif-drawable .

Mettez-le dans une WebView, il doit pouvoir l’afficher correctement, car le navigateur par défaut prend en charge les fichiers gif. (Froyo +, si je ne me trompe pas)

Je pense que la meilleure bibliothèque pour gérer les fichiers gif est celle-ci: par koral

Utilisé et j’ai du succès et cette bibliothèque est dédiée aux GIF’S; mais où le picasso et le glide sont des frameworks d’image d’ usage général ; donc je pense que les développeurs de cette bibliothèque se sont entièrement concentrés sur les fichiers gif

Utilisez la fresque. Voici comment procéder:

http://frescolib.org/docs/animations.html

Voici le repo avec l’échantillon:

https://github.com/facebook/fresco/tree/master/samples/animation

Attention à la fresque ne supporte pas le contenu enveloppant!

Il y a deux options pour charger des gifs animés dans nos applications Android

1) Utilisation de Glide pour charger le gif dans une ImageView .

  Ssortingng 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) Utiliser un HTML pour charger le gif dans une WebView

Créez le fichier HTML avec l’adresse dans le fichier .gif:

      

stocker ce fichier dans le répertoire assets:

entrer la description de l'image ici

Le chargement de ce fichier HTML dans WebView de votre application:

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

Heres est un exemple complet de ces deux options .

entrer la description de l'image ici

Glisse 4.6

1. Pour charger gif

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

2. Pour obtenir l’object fichier

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

Pour seulement l’API Android (Android Pie) 28 et +

utilisez AnimatedImageDrawable comme

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

Code XML, ajoutez un ImageView

  app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" android:layout_width="200dp" android:layout_height="200dp" /> 

AnimatedImageDrawable est un enfant de Drawable et créé par ImageDecoder.decodeDrawable

ImageDecoder.decodeDrawable qui nécessitait en outre l’instance de ImageDecoder.Source créée par ImageDecoder.createSource .

ImageDecoder.createSource ne peut prendre la source que comme nom, ByteBuffer, File, resourceId, URI, ContentResolver pour créer un object source et l’utilise pour créer AnimatedImageDrawable comme Drawable (appel polymorphe)

 static ImageDecoder.Source createSource(AssetManager assets, Ssortingng 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) 

Remarque: Vous pouvez également créer un Bitmap aide d’ ImageDecoder # decodeBitmap .

Sortie:

AnimatedDrawable prend également en charge le redimensionnement, la manipulation d’images et de couleurs

Tout d’abord, le navigateur Android devrait prendre en charge les GIF animés. Si ce n’est pas le cas, c’est un bug! Jetez un oeil sur les trackers de problème.

Si vous affichez ces GIF animés en dehors d’un navigateur, cela pourrait être une autre histoire. Pour faire ce que vous demandez, une bibliothèque externe prenant en charge le décodage des GIF animés est nécessaire.

Le premier point d’appel serait de regarder l’API Java2D ou JAI (Java Advanced Imaging), mais je serais très surpris que Android Dalvik prenne en charge ces bibliothèques dans votre application.

Semblable à ce que @Leonti a dit, mais avec un peu plus de profondeur:

Ce que j’ai fait pour résoudre le même problème a été d’ouvrir GIMP, de masquer tous les calques sauf un, de l’exporter sous sa propre image, de cacher le calque et de dévoiler le suivant, etc. . Ensuite, je pourrais les utiliser comme images dans le fichier XML AnimationDrawable.

J’ai résolu ce problème en divisant gif en images et en utilisant une animation Android standard

Quelque chose que j’ai fait pour montrer des gifs dans les applications. J’ai étendu ImageView pour que les utilisateurs puissent utiliser librement leurs atsortingbuts. Il peut afficher les gifs de l’URL ou du répertoire des ressources. La bibliothèque facilite également l’extension des classes et son extension pour prendre en charge différentes méthodes d’initialisation du fichier gif.

https://github.com/Gavras/GIFView

Il y a un petit guide sur la page github.

Il a également été publié sur Android Arsenal:

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

Exemple d’utilisation:

À partir de XML:

  

Dans l’activité:

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

Définir le gif par programmation:

 mGifView.setGifResource("asset:gif1"); 

Manière la plus simple – Peut-on considérer le code ci-dessous

Nous pouvons tirer parti de Imageview setImageResource, reportez-vous au code ci-dessous pour la même chose.

Le code ci-dessous peut être utilisé pour afficher l’image comme gif si vous avez l’image fractionnée multiple de gif. Il suffit de diviser le gif en png individuel à partir d’un outil en ligne et de placer l’image dans le dessin au format ci-dessous

image_1.png, image_2.png, etc.

Demandez au gestionnaire de changer l’image dynamicment.

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

Le moyen le plus simple d’afficher le fichier GIF animé directement à partir de l’URL dans la mise en page de votre application consiste à utiliser la classe WebView.

Étape 1: Dans votre XML de mise en page

  

Je voulais juste append que la classe Movie est maintenant obsolète.

Cette classe était obsolète au niveau de l’API P.

Il est recommandé de l’utiliser

AnimatedImageDrawable

Peut être dessiné pour dessiner des images animées (comme GIF).

Vous pouvez utiliser la bibliothèque GifAnimationDrawable trouvée dans ce lien – https://github.com/Hipmob/gifanimateddrawable , et qui convertit tout fichier gif en AnimationDrawable. Prendre plaisir 🙂

 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, AtsortingbuteSet 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()); } } } }