显示动画GIF

时间:2010-09-07 15:42:31

标签: java android animated-gif

我想在我的应用程序中显示动画GIF图像。 正如我发现Android本身难以支持动画GIF一样。

但是,它可以使用AnimationDrawable显示动画:

Develop > Guides > Images & Graphics > Drawables Overview

该示例使用在应用程序资源中保存为框架的动画,但我需要的是直接显示动画gif。

我的计划是将动画GIF分解为帧并将每个帧添加为可绘制到AnimationDrawable。

有谁知道如何从动画GIF中提取帧并将每个帧转换为Drawable

32 个答案:

答案 0 :(得分:185)

Android实际上可以使用android.graphics.Movie类解码和显示动画GIF。

这没有太多记录,但在SDK Reference。此外,它在Samples in ApiDemos in BitmapDecode示例中使用了一些动画标记。

答案 1 :(得分:48)

<强>更新

使用滑行:

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

用法:

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

请参阅docs

答案 2 :(得分:28)

还把(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" />
你的Activity中的

将下一个代码放在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's 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

是的,我希望你能帮忙。

答案 3 :(得分:21)

我在将 gif 动画分割成帧之前解决了这个问题,然后将其保存到手机中,因此我不必在Android中处理它。

然后我将每一帧下载到手机上,从中创建Drawable,然后创建 AnimationDrawable - 非常类似于我的问题中的示例

答案 4 :(得分:16)

我发现了一种非常简单的方法,这里有一个简单的工作示例

display animated widget

在开始工作之前,在代码中有一些要做的事情

以下

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

只需替换

setContentView(new MYGIFView());

setContentView(new MYGIFView(this));

AND IN

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

提供您自己的gif动画文件

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

取代第一线

public MYGIFView(Context context) {

根据班级名称......

完成这些小改动后,它应该像我一样......

希望这个帮助

答案 5 :(得分:9)

我很难让Android动画gif工作。我只跟着两个工作:

  1. web视图
  2. Ion
  3. WebView工作正常并且非常简单,但问题是它会使视图加载速度变慢,并且应用程序在一秒左右的时间内无响应。我不喜欢那样。所以我尝试了不同的方法(DID NOT WORK):

    1. ImageViewEx已被弃用!
    2. picasso未加载动画gif
    3. android-gif-drawable看起来很棒,但它在我的项目中引起了一些有线NDK问题。它导致我的本地NDK库停止工作,我无法修复它
    4. 我和Ion来回徘徊;最后,我有它的工作,它真的很快: - )

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

答案 6 :(得分:9)

在Android上显示动画GIF的方法:

  • 电影课。如上所述,这是相当错误的。
  • web视图。它使用起来非常简单,通常有效。但有时它会开始行为不端,而且它总是存在于你没有的一些模糊设备上。此外,您不能在任何类型的列表视图中使用多个实例,因为它会对您的内存起作用。不过,您可能会将其视为主要方法。
  • 将GIF解码为位图并将其显示为Drawable或ImageView的自定义代码。我会提到两个库:

https://github.com/koral--/android-gif-drawable - 解码器在C中实现,因此非常有效。

https://code.google.com/p/giffiledecoder - 解码器以Java实现,因此更容易使用。即使是大文件,效率仍然相当高。

您还可以找到许多基于GifDecoder类的库。这也是一个基于Java的解码器,但它的工作原理是将整个文件加载到内存中,因此它只适用于小文件。

答案 7 :(得分:8)

<强> Glide

适用于Android的Image Loader Library,由Google推荐。

  • Glide与毕加索非常相似,但这比毕加索要快得多。
  • Glide比Picasso消耗的内存更少。

滑翔有什么,但毕加索没有

将GIF动画加载到简单的ImageView的功能可能是Glide最有趣的功能。是的,你不能用毕加索做到这一点。 enter image description here 一些重要的链接 -

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

答案 8 :(得分:7)

使用ImageViewEx,一个使用gif的库就像使用ImageView一样简单。

答案 9 :(得分:6)

试试这个,下面是代码显示进度条中的gif文件

loading_activity.xml(在布局文件夹中)

<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(在drawable文件夹中),你可以把你自己的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);

    }

}

答案 10 :(得分:6)

没人提到IonGlide库。他们的工作非常好。

与WebView相比,它更容易处理。

答案 11 :(得分:5)

我已经成功使用了within this article提议的解决方案,这是一个名为GifMovieView的类,它会呈现一个View,然后可以显示或添加到特定的ViewGroup。查看指定文章第2部分和第3部分中介绍的其他方法。

这种方法的唯一缺点是对电影的抗锯齿效果不是那么好(必须是使用“阴暗”Android Movie类的副作用)。然后,最好在动画GIF中将背景设置为纯色。

答案 12 :(得分:4)

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

答案 13 :(得分:4)

@PointerNull提供了很好的解决方案,但并不完美。它在某些具有大文件的设备上不起作用,并且在前ICS版本上显示带有增量帧的错误Gif动画。 我找到了没有这个错误的解决方案。它是具有可绘制的原生解码的库:koral's android-gif-drawable

答案 14 :(得分:4)

关于BitmapDecode示例的一些想法......基本上它使用了android.graphics中的古老而无特征的Movie类。 在最近的API版本中,您需要关闭硬件加速as described here。否则,这对我来说是分裂的。

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

这是仅使用GIF部分缩短的BitmapDecode示例。你必须制作自己的小部件(视图)并自己绘制它。不像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();
            }
        }
    }
}

可以在this fine tutorial中找到另外两种方法,一种是使用ImageView另一种是使用WebView。 ImageView方法使用来自Google Code的Apache许可android-gifview

答案 15 :(得分:2)

在src文件夹下创建一个名为“ Utils”的程序包,并创建一个名为“ GifImageView”的类

public class GifImageView extends View {
    private InputStream mInputStream;
    private Movie mMovie;
    private int mWidth, mHeight;
    private long mStart;
    private Context mContext;
    public GifImageView(Context context) {
        super(context);
        this.mContext = context;
    }
    public GifImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        if (attrs.getAttributeName(1).equals("background")) {
            int id = Integer.parseInt(attrs.getAttributeValue(1).substring(1));
            setGifImageResource(id);
        }
    }
    private void init() {
        setFocusable(true);
        mMovie = Movie.decodeStream(mInputStream);
        mWidth = mMovie.width();
        mHeight = mMovie.height();
        requestLayout();
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(mWidth, mHeight);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        long now = SystemClock.uptimeMillis();
        if (mStart == 0) {
            mStart = now;
        }
        if (mMovie != null) {
            int duration = mMovie.duration();
            if (duration == 0) {
                duration = 1000;
            }
            int relTime = (int) ((now - mStart) % duration);
            mMovie.setTime(relTime);
            mMovie.draw(canvas, 0, 0);
            invalidate();
        }
    }
    public void setGifImageResource(int id) {
        mInputStream = mContext.getResources().openRawResource(id);
        init();
    }
    public void setGifImageUri(Uri uri) {
        try {
            mInputStream = mContext.getContentResolver().openInputStream(uri);
            init();
        } catch (FileNotFoundException e) {
            Log.e("GIfImageView", "File not found");
        }
    }
}

现在在MainActivity中定义GifImageView 文件:src / activity / MainActivity.class

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        GifImageView gifImageView = (GifImageView) findViewById(R.id.GifImageView);
        gifImageView.setGifImageResource(R.drawable.smartphone_drib);
    }
}

现在的UI部件 文件:res / layout / activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:gravity="center"
    android:background="#111E39"
    tools:context=".Activity.MainActivity">
    <com.android.animatedgif.Utils.GifImageView
        android:id="@+id/GifImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" />

</RelativeLayout>

资源代码:Android example

答案 16 :(得分:2)

将其放入WebView,它必须能够正确显示它,因为默认浏览器支持gif文件。 (Froyo +,如果我没有记错的话)

答案 17 :(得分:2)

仅适用于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只能使用source作为名称,使用ByteBuffer,File,resourceId,URI,ContentResolver创建源对象,并使用它以AnimatedImageDrawable的形式创建Drawable(多态调用)< / p>

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)

注意:您也可以使用ImageDecoder#decodeBitmap创建Bitmap

输出:

AnimatedDrawable also supports resizing, frame and color manipulation

答案 18 :(得分:2)

将动画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

使用.gif文件的地址创建html:

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

将此文件存储到assets目录中:

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是complete example of this two options

enter image description here

答案 19 :(得分:1)

我认为处理gif文件的更好的库是this one: by koral

使用它,我很成功,这个图书馆专门用于GIF'S;但毕加索和滑翔通用图像框架的地方;所以我认为这个库的开发人员完全专注于gif文件

答案 20 :(得分:1)

使用壁画。以下是如何做到这一点:

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

这里是样本的回购:

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

当心壁画不支持包装内容!

答案 21 :(得分:0)

如果你想使用 Glide 加载 gif:

Glide.with(this)
        .asGif()
        .load(R.raw.onboarding_layers) //Your gif resource
        .apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.NONE))
        .listener(new RequestListener<GifDrawable>() {
            @Override
            public boolean onLoadFailed(@Nullable @org.jetbrains.annotations.Nullable GlideException e, Object model, Target<GifDrawable> target, boolean isFirstResource) {
                return false;
            }

            @Override
            public boolean onResourceReady(GifDrawable resource, Object model, Target<GifDrawable> target, DataSource dataSource, boolean isFirstResource) {
                resource.setLoopCount(1);
                return false;
            }
        })
        .into((ImageView) view.findViewById(R.id.layer_icons));

答案 22 :(得分:0)

只是想补充一点,Movie类现已弃用。

  

此类在API级别P中已弃用。

建议使用此

AnimatedImageDrawable

  

可绘制动画图像(如GIF)。

答案 23 :(得分:0)

直接从URL到应用布局显示动画GIF的简便方法是使用WebView类。

第1步: 在您的布局XML中

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

第2步:在您的活动中

WebView wb;
wb = (WebView) findViewById(R.id.webView);
wb.loadUrl("https://.......);

第3步:在您的Manifest.XML中进行互联网许可

<uses-permission android:name="android.permission.INTERNET" />

第4步:如果您想让GIF背景透明并使GIF适合您的版式

wb.setBackgroundColor(Color.TRANSPARENT);
wb.getSettings().setLoadWithOverviewMode(true);
wb.getSettings().setUseWideViewPort(true);

答案 24 :(得分:0)

为了节省资源,有滑翔库。 不知道为什么要使用其他任何东西,尤其是 webview 只显示图像。 Glide 是一个完美且简单的库,它可以从 gif 中准备可绘制动画并将其直接放入 imageview。 gifdrawable 处理动画本身的逻辑。 Gif 已经 lzw 压缩了动画的原始 rgb 数据。 没有理由复杂的使用webview,管理更多的文件在app中只显示一个gif文件。

答案 25 :(得分:0)

最简单的方法 - 可以考虑以下代码

我们可以利用Imageview setImageResource,参考下面的代码。

如果你有gif的多个分割图像,下面的代码可用于显示像gif incase的图像。只需将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 :(得分:0)

我通过在帧中拆分gif并使用标准的android动画来解决这个问题

答案 27 :(得分:0)

我在应用中展示GIF的方式。我扩展了ImageView,以便人们可以自由使用它的属性。它可以显示来自url或assets目录的gif。 该库还使扩展类很容易从它继承并扩展它以支持不同的方法来初始化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");

答案 28 :(得分:0)

首先,Android浏览器应支持动画GIF。如果没有,那就是一个错误!看一下问题跟踪器。

如果您在浏览器之外显示这些动画GIF,则可能是另一回事。要做你想要的就需要支持解码动画GIF的外部库。

第一个调用端口是查看Java2D或JAI(Java高级成像)API,但如果Android Dalvik支持您应用程序中的这些库,我会非常惊讶。

答案 29 :(得分:0)

类似于@Leonti所说的,但更深入一点:

我为解决同样的问题所做的是打开GIMP,隐藏除了一个之外的所有图层,将其导出为自己的图像,然后隐藏该图层并取消隐藏下一个等等,直到我有单独的资源文件每一个人。然后我可以将它们用作AnimationDrawable XML文件中的框架。

答案 30 :(得分:-1)

您可以使用此链接中的GifAnimationDrawable库 - https://github.com/Hipmob/gifanimateddrawable,并将任何gif转换为AnimationDrawable。享受:)

答案 31 :(得分:-9)

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

      }
    }
  }
}