支持GIF动画的ImageView

http://blog.csdn.net/yarkey09/article/details/13022387


网上有很多关于怎么实现android播放GIF的帖子。但是本人发现,其中多多少少都有些不如人意的地方。因此,花了几天时间,重写了ImageView以实现GIF图片的播放。在此小结一下,也希望可以给后来者一点参考。

    大致我们会在网上搜到下面四种解决方法:

    【方案一】用外部工具拆分GIF

    【方案二】用Android开源项目GifView包

    【方案三】手动解码GIF

    【方案四】用系统自带的类Movie

    本文采用方案四,继承ImageView实现GIF动画播放,支持ImageView的命名空间属性设置,支持ImageView通用接口。

 

    项目源码下载地址:

    http://download.csdn.net/detail/yarkey09/6499717


【什么是GIF】 

    GIF,就我的理解,就是很多张位图图片的集合,然后使用了某种编码方式,使得它可以体积很小但是又够清晰。由于体积小,不依赖特别的平台,所以GIF很流行。

好吧,知道的就这么多,各位看官想了解清楚的话还是请自行百度吧。不过了解了大概概念,我们就可以知道,其实让GIF播放,实际就是显示多张图片而已。

 

【方案一】用外部工具拆分GIF

    大概情况是这样:

    1,首先我们得有一张GIF (提示:选择赏心悦目的动画,可以提高学习兴趣哦^_^)

    2,然后使用工具,千刀万剐将GIF分成多张图片 => pic0.png,pic1.png,pic2.png,pic3.png,pic4.png,pic5.png

    3,接着编写android xml资源文件放在drawable目录下,说明各个帧图片以及时间duration

    4,然后代码里面使用AnimationDrawable类即可实现

    四张图片按照xml定义的时间,一张张切换,看起来就是动画了!

    动画资源文件格式是这样:(drawable/anim_gif.xml)

[html]  view plain copy
  1. <animation-list xmlns:android="http://schemas.android.com/apk/res/android">  
  2. <item android:duration="150" android:drawable="@drawable/pic0" />  
  3. <item android:duration="150" android:drawable="@drawable/pic1" />  
  4. <item android:duration="150" android:drawable="@drawable/pic2" />  
  5. <item android:duration="150" android:drawable="@drawable/pic3" />  
  6. <item android:duration="150" android:drawable="@drawable/pic4" />  
  7. <item android:duration="150" android:drawable="@drawable/pic5" />  
  8. </animation-list>  

    布局文件可以是这样:

[html]  view plain copy
  1. <ImageView android:layout_width="wrap_content"  
  2. android:layout_height="wrap_content"  
  3. android:background="@drawable/anim_gif"  
  4. android:id="@+id/imgGif"></ImageView>  

    代码是这样:

[java]  view plain copy
  1. ImageView imageView = (ImageView) findViewById(R.id.imgGif);  
  2. Object ob = imageView.getBackground();  
  3. AnimationDrawable anim = (AnimationDrawable) ob;  
  4. anim.start();  

    如果我们需要在界面上显示一个简单且固定的动画,单纯用于点缀画面,增强动感,这种方法比较方便。当然,这种方法在某些场合下显得很不灵活,不能满足要求效果,那么请继续参考后面的方法吧。

参考帖子:Android开发:教您如何让Gif动画动起来

 

【方案二】用Android开源项目GifView包

    我们同样可以在网上搜到这个开源项目的相关应用。有了这个包,我们要让GIF播放这个事情就变得非常轻松。看看它那强大的接口就知道了!使用GifView几乎就跟ImageView是一样的。方便!开源项目确实有很多代码都有非常好的学习价值,表示有空应该好好拜读一番!

[java]  view plain copy
  1. // 从xml中得到GifView的句柄   
  2. gif1 = (GifView) findViewById(R.id.gif1);   
  3. // 设置Gif图片源   
  4. gif1.setGifImage(R.drawable.gif1);   
  5. // 添加监听器   
  6. gif1.setOnClickListener(this);   
  7. // 设置显示的大小,拉伸或者压缩   
  8. gif1.setShowDimension(300300);   
  9. // 设置加载方式:先加载后显示、边加载边显示、只显示第一帧再显示  
  10. gif1.setGifImageType(GifImageType.COVER);   

参考帖子:介绍一个Android开源项目:GifView——Android显示GIF动画

 

【方案三】手动解码GIF

    用java来解码,很多人会觉得效率比较低。但是我们目的是学习,完全可以尝试一下!当然,也可以用native代码完成解码,在java用JNI调用。

    将GIF文件解码后,我们可以得到所有想要的信息。比如Gif版本GIF87a, GIF89a等等,关键是我们可以得到几张Bitmap图片,还有各张图片的显示延续时间。其实,这里解码工作也就大致等同于上面的方案一。不同的是,我们的app可以直接播放GIF,而不需要外部的工具!

    有了各帧图片以及显示延续时间,我们便可以开始了!新建一个线程用于计时,时间一到就刷新View切换图片。这就是GIF了!

    注意一下在非主线程让View刷新,应该调用postInvalidate() 而不是invalidate()。

    下面参考帖子附有解码源程序,然后按照参考文档来阅读,很快可以看明白^_^

参考帖子: Android 解码播放GIF图像

参考文档: GIF文件格式

 

【方案四】用系统自带的类Movie

    接下来说说具体要讲的基于Movie的实现方法吧!

    使用Movie类播放GIF很简单。但是我们的目的是,继承ImageView,保留它显示图片的基本功能,尽量使得接口函数能够通用简便。这样,原先使用ImageView的项目代码只要经过少量的修改,即可支持GIF动画。根据要求,我们至少要重载下面四个通用接口以支持GIF动画:

[java]  view plain copy
  1. public void setImageResource( int resID )  
  2. public void setImageURI( Uri uri )  
  3. public void setScaleType( ScaleType scaleType )  
  4. public void setPadding( int left, int top, int right, int bottom )  

     说明一下:

[java]  view plain copy
  1. // 我们设置了图片,那么跟ImageView一样显示出图片  
  2. setImageResource( R.drawable.pngtest );   
  3. // 我们这次设置了GIF动画,那么应该显示动画  
  4. setImageResource( R.drawable.giftest );   
  5. // 支持SD卡中的GIF动画  
  6. setImageURI( Uri.parse( "file://" + Environment.getExternalStorageDirectory().getPath() + "/sdcard_giftest.gif" );   

    为了实现以上要求,其中遇到很多问题,我们慢慢说吧。

 

-1- Movie 是啥东西

    android.graphics.Movie 在SDK文档中没有说明,翻看源代码,发现它只是一个java壳,实际上直接调用native代码。这样导致我们没能快速学习掌握它的用法。

    不过幸亏有APIDemo!这真的是一个好东西!打开其中BitmapDecode我们可以发现代码中就用了Movie类!

    直接安装APIDemo到手机中,运行... 发现旗子飘动起来了!

    它的源代码简单清晰,大概是这样。Movie对象管理着时间轴上对应的GIF各帧图片,我们通过传入时间,便可以取出对应的帧,然后再用draw()方法,将当前的帧画到画布canvas上面。如果我们的View不停的刷新,时间不停地跑,Movie的帧就不停的切换,那么画出来的View就动起来了!

 

-2- Copy APIDemo 源码

    那么好了,按照它的代码,我们可以很快copy一份出来,然后编译安装到手机,我们想GIF似乎就这样完成了。关键代码如下。

[java]  view plain copy
  1. private static class MovieGifView extends View {  
  2.   
  3.     private Movie mMovie;  
  4.     private long mMovieStart;  
  5.   
  6.     public MovieGifView(Context context) {  
  7.         super(context);  
  8.         java.io.InputStream is;  
  9.         is = context.getResources().openRawResource(R.drawable.animated_gif);  
  10.         mMovie = Movie.decodeStream(is);  
  11.     }  
  12.   
  13.     @Override  
  14.     protected void onDraw(Canvas canvas) {  
  15.   
  16.         long now = android.os.SystemClock.uptimeMillis();  
  17.         if (mMovieStart == 0) { // first time  
  18.             mMovieStart = now;  
  19.         }  
  20.         if (mMovie != null) {  
  21.             int dur = mMovie.duration();  
  22.             if (dur == 0) {  
  23.                 dur = 1000;  
  24.             }  
  25.             int relTime = (int) ((now - mMovieStart) % dur);  
  26.             mMovie.setTime(relTime);  
  27.             mMovie.draw(canvas, getWidth() - mMovie.width(), getHeight() - mMovie.height());  
  28.             invalidate();  
  29.         }  
  30.     }  
  31. }  

    但是结果却是那么不如人意,自己写的app在一部平板(android 4.3)上运行时,GIF没有动起来,美女并没有向我眨眼!

    我第一反应便是拿去我的屌丝神机I589(I5830电信版 android 2.3) 上试试。结果反而动起来了!这么神马回事?!

    难道是android 4.3 版本太新,Movie方法不支持?后来我又找到了一部android 4.1 的手机,安装发现,GIF同样没有动!

    奇怪!头疼!

 

-3- hardwareAccelerated 惹的祸

    为什么APIDemo的代码可以,我的代码直接copy,却不行了?我翻看了很久代码,最后找到了唯一不同点,在这里 -> AndroidManifest.xml

[html]  view plain copy
  1. <activity android:hardwareAccelerated="false"  
  2.           android:name=".graphics.BitmapDecode" android:label="Graphics/BitmapDecode">  
  3.     <intent-filter>  
  4.         <action android:name="android.intent.action.MAIN" />  
  5.         <category android:name="android.intent.category.SAMPLE_CODE" />  
  6.     </intent-filter>  
  7. </activity>  

    BitmapDecode Activity 属性设置中,有个东东不曾相识→ → android:hardwareAccelerated="false",不使能硬件加速?什么概念?

    于是便开始查看各种说明,大概意思我是这么理解的:硬件加速并不是什么新鲜的东西,已经运用于windows composition 或 OpenGL games等等。而android 在 3.0之后的版本开始支持。但是它现在暂时只支持standard widgets and drawables。一旦使能硬件加速的特性,所有的画图工作都交给GPU来做。

    但是,我们现在是自定义View类,使用Movie类的draw()方法画图,这个方法并没有在硬件加速支持列表(如下)中找到踪影。

    The following table describes the support level of various operations across API levels:

     
 API level
 < 16161718
Canvas
drawBitmapMesh() (colors array)
drawPicture()
drawPosText()
drawTextOnPath()
drawVertices()
setDrawFilter()
clipPath()
clipRegion()
clipRect(Region.Op.XOR)
clipRect(Region.Op.Difference)
clipRect(Region.Op.ReverseDifference)
clipRect() with rotation/perspective

详见官方说明: Android 3.0 (API level 11) Hardware Acceleration

    所以,我们认为硬件加速不支持Movie draw()方法。而I589(android 2.3)本身没有这个特性,所以不出现问题。而android 4.3平板具备硬件加速并默认开启,而我们没有关掉,所以出了问题。我准备尝试关了这个特性再试试,再不行就死给你看!

    关闭Hardware Acceleeration可以有几种方法,针对不同的级别(Application, Activity, Window, View )。具体请详见官方说明。

    为了影响最小,可以使用View级别的 setLayerType(View.LAYER_TYPE_SOFTWARE, null);

    关掉了硬件加速,我的GIF终于动起来了!美女开始眨眼,多么好看的GIF动画!欢呼吧!\(^o^)/

 

-4- 继承ImageView

    GIF已经动起来了,感觉事情已经搞定了一大半。但是后面发现,实际上不是这样的!

    再次说说我们的设计目标:设计一个类可以通过设置 Resource ID或者URI 播放图片和GIF动画。

    我们自然想到继承ImageView,然后加入GIF功能代码。这样可以节省很多代码。但是查看ImageView源码,很多private成员变量, private成员函数,真是让人望而却步呀。但是,耐心的啃一啃ImageView的源码,大概还是可以看出思路的。

 

-5- 解读ImageView源码

源码链接:Android ImageView.java 源码在线阅读

    个人认为ImageView源码最关键的部分,也是我们继承它最需要考虑的问题有两点:

    - 控件的大小 (onMeasure()回调方法)

    - 图片的大小与位置 (configureBounds()私有方法)

 

    a) 图片的大小与位置

    先谈谈图片的大小与位置吧,因为待会它在onMeasure方法中会用到。

    两个关键的成员变量 mDrawable, mDrawMatrix。一个是"图片",一个是矩阵。mDrawable调节自身大小颜色透明度等等。mDrawMatrix则定义了画布的缩放平移旋转等等。在onDraw()方法调用之前,我们必须设置好这两个变量,才能画出正确的图形。ImageView对这两个变量的配置,大部分工作在configureBounds()方法中完成。

    configureBounds()私有方法,根据当前View的大小(除去Padding部分)、Drawable实际大小、以及ScaleType参数,设置了图片最终要显示的大小以及对齐等属性。而padding参数的平移效果,在onDraw()中通过平移画布实现。

[java]  view plain copy
  1. // <span style="background-color:#f0f0f0">yarkey@20131029</span> :   
  2. // configureBounds()方法的产物:mDrawable,mDrawMatrix  
  3.   
  4.   
  5. // onDraw()方法将使用这里的mDrawable,mDrawMatrix作画  
  6.   
  7.   
  8.   
  9. private void configureBounds() {  
  10.     if (mDrawable == null || !mHaveFrame) {  
  11.         return;  
  12.     }  
  13.   
  14.     int dwidth = mDrawableWidth;  
  15.     int dheight = mDrawableHeight;  
  16.   
  17.     int vwidth = getWidth() - mPaddingLeft - mPaddingRight;  
  18.     int vheight = getHeight() - mPaddingTop - mPaddingBottom;  
  19.   
  20.     boolean fits = (dwidth < 0 || vwidth == dwidth) &&  
  21.                    (dheight < 0 || vheight == dheight);  
  22.       
  23.     // yarkey@20131029 : 以下根据ScaleType设置mDrawMatrix  
  24.     if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {  
  25.         /* If the drawable has no intrinsic size, or we're told to 
  26.             scaletofit, then we just fill our entire view. 
  27.         */  
  28.         // yarkey@20131029 : fitXY的情况较简单,直接将Drawable缩放至View的大小即可(除去padding)  
  29.         mDrawable.setBounds(00, vwidth, vheight);  
  30.         mDrawMatrix = null;  
  31.     } else {  
  32.         // We need to do the scaling ourself, so have the drawable  
  33.         // use its native size.  
  34.   
  35.   
  36.   
  37.         // yarkey@20131029 : 获取图片固有的大小  
  38.           // dwidth = mDrawable.getIntrinsicWidth()  
  39.         // dheight = mDrawable.getIntrinsicHeight()  
  40.         // 涉及设备像素密度(density),图片存放目录(drawable-mdpi/drawable-hdpi)  
  41.         mDrawable.setBounds(00, dwidth, dheight);  
  42.   
  43.         if (ScaleType.MATRIX == mScaleType) {  
  44.             // Use the specified matrix as-is.  
  45.             if (mMatrix.isIdentity()) {  
  46.                 mDrawMatrix = null;  
  47.             } else {  
  48.                 mDrawMatrix = mMatrix;  
  49.             }  
  50.         } else if (fits) {  
  51.             // The bitmap fits exactly, no transform needed.  
  52.             mDrawMatrix = null;  
  53.         } else if (ScaleType.CENTER == mScaleType) {  
  54.             // Center bitmap in view, no scaling.  
  55.             // <span style="background-color:#f0f0f0">yarkey@20131029</span> : 按原图大小居中显示,超过View长宽则截取中间部分  
  56.             mDrawMatrix = mMatrix;  
  57.             mDrawMatrix.setTranslate((int) ((vwidth - dwidth) * 0.5f + 0.5f),  
  58.                                      (int) ((vheight - dheight) * 0.5f + 0.5f));  
  59.         } else if (ScaleType.CENTER_CROP == mScaleType) {  
  60.             mDrawMatrix = mMatrix;  
  61.   
  62.             float scale;  
  63.             float dx = 0, dy = 0;  
  64.             // yarkey@20131029 : centerCrop,长宽算出比例,然后取比例“大”的  
  65.             if (dwidth * vheight > vwidth * dheight) {  
  66.                 scale = (float) vheight / (float) dheight;   
  67.                 dx = (vwidth - dwidth * scale) * 0.5f;  
  68.             } else {  
  69.                 scale = (float) vwidth / (float) dwidth;  
  70.                 dy = (vheight - dheight * scale) * 0.5f;  
  71.             }  
  72.   
  73.             mDrawMatrix.setScale(scale, scale);  
  74.             mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));  
  75.         } else if (ScaleType.CENTER_INSIDE == mScaleType) {  
  76.             mDrawMatrix = mMatrix;  
  77.             float scale;  
  78.             float dx;  
  79.             float dy;  
  80.             // <span style="background-color:#f0f0f0">yarkey@20131029</span> : centerCrop,长宽算出比例,然后取比例“小”的                  
  81.             if (dwidth <= vwidth && dheight <= vheight) {  
  82.                 scale = 1.0f;  
  83.             } else {  
  84.                 scale = Math.min((float) vwidth / (float) dwidth,  
  85.                         (float) vheight / (float) dheight);  
  86.             }  
  87.               
  88.             dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);  
  89.             dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f);  
  90.   
  91.             mDrawMatrix.setScale(scale, scale);  
  92.             mDrawMatrix.postTranslate(dx, dy);  
  93.         } else {  
  94.             // <span style="background-color:#f0f0f0">yarkey@20131029</span> : 剩下fitCenter,fitStart,fitEnd三种  
  95.                // Generate the required transform.  
  96.             mTempSrc.set(00, dwidth, dheight);  
  97.             mTempDst.set(00, vwidth, vheight);  
  98.               
  99.             mDrawMatrix = mMatrix;  
  100.             mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));  
  101.         }  
  102.     }  
  103. }  

    一旦mDrawable或者mDrawMatrix需要改变的时候,configureBounds() 方法就会被调用。大概是这样子的:

    

    只要调用invalidate()方法,onDraw()方法就会被调用,从而刷新界面。

    我们现在关心的是,播放GIF的时候,我们手上是Movie对象,而不是Drawable对象。因此,用于Drawable的位置计算,不能适用于Movie的场合。ImageView可以通过Drawable setBounds()方法设置大小,Movie却没有这种方法,因此我们只能通过缩放画布(canvas)来实现相同的效果。

    至于怎么用Matrix矩阵来变换画图,请移步:

参考帖子:Android Matrix理论与应用详解

 

    b) 控件的大小
    控件的大小需要在onMeasure()方法中设置,使用setMeasuredDimension()方法。根据输入参数int widthMeasureSpec, int heightMeasureSpec 可以得到父容器(Layout)提供的Measure模式Mode以及参考大小Size。MeasureSpec.UNSPECIFIED, MeasureSpec.AT_MOST, MeasureSpec.EXACTLY。

    对应这三种模式的不同设置方法,ImageView源码注释得很清楚(resolveAdjustedSize()方法中)。我们到时copy它的代码就可以(因为是private方法,子类不可以调用)。

    重写onMeasure()方法,我们归为一点:把里面的mDrawable替换为mMovie即可。

    但是,执行完onMeasure()后,如果立即去getWidth(), getHeight(),我们只会得到旧值!如果想要onMeasure()后,立即计算图片的缩放移动旋转参数,那么需要用getMeasuredWidth()和getMeasuredHeight()代替。

    调用requestLayout()方法,可以触发onMeasure()方法。

 

-6- MovieImageView

    代码比较长,这里直接贴上一个测试没有问题的版本。

    这里只实现新增方法setMovie(),如果想实现setImageResource(), setImageUri(),只要将以下的代码稍作修改,将Movie对象的初始化部分放到MovieImageView类里头完成,即可实现!

[java]  view plain copy
  1. package com.yarkey.giftest2;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Bitmap;  
  5. import android.graphics.Canvas;  
  6. import android.graphics.Matrix;  
  7. import android.graphics.Movie;  
  8. import android.graphics.drawable.Drawable;  
  9. import android.net.Uri;  
  10. import android.os.Build;  
  11. import android.util.AttributeSet;  
  12. import android.util.Log;  
  13. import android.view.View;  
  14. import android.widget.ImageView;  
  15.   
  16.   
  17. public class MovieImageView extends ImageView {  
  18.   
  19.     private static final boolean DB = true;  
  20.     private static final boolean DB_DETAIL = false;  
  21.     private static final String DB_TAG = "MovieImageView";  
  22.   
  23.     /** Feature Support GIF */  
  24.     private static final boolean FEATURE_IS_GIF_SUPPORTED = true;  
  25.   
  26.     /** @see #syncParentParameter() */  
  27.     private int mSuperPaddingTop;  
  28.     private int mSuperPaddingLeft;  
  29.     private int mSuperPaddingRight;  
  30.     private int mSuperPaddingBottom;  
  31.     private ScaleType mSuperScaleType;  
  32.     private Matrix mSuperDrawMatrix;  
  33.   
  34.     /** mMovie==null means we work the same as parent(ImageView) */  
  35.     private Movie mMovie = null;  
  36.   
  37.     private Matrix mMatrix;  
  38.     private Matrix mDrawMatrix;  
  39.   
  40.     private long mMovieStartTime = 0;  
  41.     private long mMovieDuration = 0;  
  42.   
  43.     private int mDefLayerType;  
  44.   
  45.     // AdjustViewBounds behavior will be in compatibility mode for older apps.  
  46.     private boolean mAdjustViewBoundsCompat = false;  
  47.   
  48.     public MovieImageView(Context context) {  
  49.         super(context);  
  50.         initGifAndImageView();  
  51.     }  
  52.   
  53.     public MovieImageView(Context context, AttributeSet attrs) {  
  54.         this(context, attrs, 0);  
  55.     }  
  56.   
  57.     public MovieImageView(Context context, AttributeSet attrs, int defStyle) {  
  58.         super(context, attrs, defStyle);  
  59.         initGifAndImageView();  
  60.     }  
  61.   
  62.     private void prepareForMovie(boolean isToDo) {  
  63.         if (FEATURE_IS_GIF_SUPPORTED && isToDo) {  
  64.             if (getLayerType() != View.LAYER_TYPE_SOFTWARE) {  
  65.                 setLayerType(View.LAYER_TYPE_SOFTWARE, null);  
  66.             }  
  67.             setWillNotCacheDrawing(false);  
  68.             mMovieStartTime = 0;  
  69.         } else if (mDefLayerType != 0 && mDefLayerType != getLayerType()) {  
  70.             setLayerType(mDefLayerType, null);  
  71.             mMovie = null;  
  72.         }  
  73.     }  
  74.   
  75.     /** 
  76.      * You may open an inputstream of certain GIF file, and then decode by 
  77.      * Movie.decodeStream. 
  78.      *  
  79.      * @param movie 
  80.      */  
  81.     public void setMovie(Movie movie) {  
  82.         Log("setMovie");  
  83.         if (FEATURE_IS_GIF_SUPPORTED && mMovie != movie) {  
  84.             mMovie = movie;  
  85.             if (mMovie != null) {  
  86.                 prepareForMovie(true);  
  87.                 mMovieDuration = mMovie.duration();  
  88.                 requestLayout();  
  89.                 // configureDrawMatrix();//will get called after onMeasures  
  90.             } else {  
  91.                 prepareForMovie(false);  
  92.             }  
  93.             invalidate();  
  94.         }  
  95.     }  
  96.   
  97.     private void syncParentParameter() {  
  98.         Log("syncParentParameter");  
  99.         mSuperPaddingTop = getPaddingTop();  
  100.         mSuperPaddingLeft = getPaddingLeft();  
  101.         mSuperPaddingRight = getPaddingRight();  
  102.         mSuperPaddingBottom = getPaddingBottom();  
  103.         mSuperScaleType = getScaleType();  
  104.         mSuperDrawMatrix = getImageMatrix();  
  105.     }  
  106.   
  107.     @Override  
  108.     public void setImageBitmap(Bitmap bm) {  
  109.         prepareForMovie(false);  
  110.         super.setImageBitmap(bm);  
  111.     }  
  112.   
  113.     @Override  
  114.     public void setImageDrawable(Drawable drawable) {  
  115.         prepareForMovie(false);  
  116.         super.setImageDrawable(drawable);  
  117.     }  
  118.   
  119.     @Override  
  120.     public void setImageResource(int resId) {  
  121.         prepareForMovie(false);  
  122.         super.setImageResource(resId);  
  123.     }  
  124.   
  125.     @Override  
  126.     public void setImageURI(Uri uri) {  
  127.         prepareForMovie(false);  
  128.         super.setImageURI(uri);  
  129.     }  
  130.   
  131.     @Override  
  132.     public void setScaleType(ScaleType scaleType) {  
  133.         super.setScaleType(scaleType);  
  134.         configureDrawMatrix();  
  135.     }  
  136.   
  137.     @Override  
  138.     public void setImageMatrix(Matrix matrix) {  
  139.         super.setImageMatrix(matrix);  
  140.         // We should do the following whether we are in "MovieMode" or not,  
  141.         // because we can not get the matrix from  
  142.         // parent later.  
  143.         if (matrix != null && matrix.isIdentity()) {  
  144.             matrix = null;  
  145.         }  
  146.         if (matrix == null && !mMatrix.isIdentity() || matrix != null && !mMatrix.equals(matrix)) {  
  147.             mMatrix.set(matrix);  
  148.             configureDrawMatrix();  
  149.             invalidate();  
  150.         }  
  151.     }  
  152.   
  153.     @Override  
  154.     public void setPadding(int left, int top, int right, int bottom) {  
  155.         super.setPadding(left, top, right, bottom);  
  156.         configureDrawMatrix();  
  157.     }  
  158.   
  159.     private void initGifAndImageView() {  
  160.         Log("initGifAndImageView");  
  161.         if (FEATURE_IS_GIF_SUPPORTED) {  
  162.             mMatrix = new Matrix();  
  163.             mDefLayerType = getLayerType();  
  164.         }  
  165.         mAdjustViewBoundsCompat = this.getContext().getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;  
  166.     }  
  167.   
  168.     private void configureDrawMatrix() {  
  169.         Log("configureDrawMatrix");  
  170.         if (!FEATURE_IS_GIF_SUPPORTED || mMovie == null) {  
  171.             return;  
  172.         }  
  173.   
  174.         // getWidth/Height() aren't valid until after a layout  
  175.         if (getMeasuredHeight() == 0 || getMeasuredWidth() == 0)  
  176.             return;  
  177.   
  178.         syncParentParameter();  
  179.   
  180.         int movieWidth = mMovie.width();// 实际像素  
  181.         int movieHeight = mMovie.height();  
  182.         Log("movieWidth = " + movieWidth + ", movieHeight = " + movieHeight);  
  183.   
  184.         // in pixels  
  185.         // int vWidth = getWidth() - mSuperPaddingLeft - mSuperPaddingRight;  
  186.         // int vHeight = getHeight() - mSuperPaddingTop - mSuperPaddingBottom;  
  187.   
  188.         int vWidth = getMeasuredWidth() - mSuperPaddingLeft - mSuperPaddingRight;  
  189.         int vHeight = getMeasuredHeight() - mSuperPaddingTop - mSuperPaddingBottom;  
  190.   
  191.         Log("vWidth = " + vWidth + ", vHeight = " + vHeight);  
  192.   
  193.         if (ScaleType.CENTER == mSuperScaleType) {  
  194.             mDrawMatrix = mMatrix;  
  195.             mDrawMatrix.setTranslate((int) ((vWidth - movieWidth) * 0.5f + 0.5f),  
  196.                     (int) ((vHeight - movieHeight) * 0.5f + 0.5f));  
  197.   
  198.         } else if (ScaleType.CENTER_CROP == mSuperScaleType) {  
  199.   
  200.             mDrawMatrix = mMatrix;  
  201.             float scale = Math.max((float) vHeight / (float) movieHeight, (float) vWidth / (float) movieWidth);  
  202.             float dx = (vWidth - movieWidth * scale) * 0.5f;  
  203.             float dy = (vHeight - movieHeight * scale) * 0.5f;  
  204.             mDrawMatrix.setScale(scale, scale);  
  205.             mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));  
  206.   
  207.         } else if (ScaleType.CENTER_INSIDE == mSuperScaleType) {  
  208.   
  209.             mDrawMatrix = mMatrix;  
  210.             float scale;  
  211.             if (movieWidth <= vWidth && movieHeight <= vHeight) {  
  212.                 scale = 1.0f;  
  213.             } else {  
  214.                 scale = Math.min((float) vWidth / (float) movieWidth, (float) vHeight / (float) movieHeight);  
  215.             }  
  216.             float dx = (int) ((vWidth - movieWidth * scale) * 0.5f + 0.5f);  
  217.             float dy = (int) ((vHeight - movieHeight * scale) * 0.5f + 0.5f);  
  218.             mDrawMatrix.setScale(scale, scale);  
  219.             mDrawMatrix.postTranslate(dx, dy);  
  220.   
  221.         } else if (ScaleType.FIT_XY == mSuperScaleType) {  
  222.   
  223.             mDrawMatrix = mMatrix;  
  224.             float scaleX = (float) vWidth / (float) movieWidth;  
  225.             float scaleY = (float) vHeight / (float) movieHeight;  
  226.             Log("ScaleType.FIT_XY, scaleX = " + scaleX + ", scaleY = " + scaleY);  
  227.             mDrawMatrix.setScale(scaleX, scaleY);  
  228.             // mDrawMatrix.postTranslate(mSuperPaddingLeft, mSuperPaddingTop);  
  229.   
  230.         } else if (ScaleType.MATRIX == mSuperScaleType) {  
  231.   
  232.             mDrawMatrix = mSuperDrawMatrix;  
  233.   
  234.         } else { /* fit */  
  235.             mDrawMatrix = mMatrix;  
  236.             float scale = Math.min((float) vHeight / (float) movieHeight, (float) vWidth / (float) movieWidth);  
  237.             float dx = 0.0f;  
  238.             float dy = 0.0f;  
  239.             if (ScaleType.FIT_START == mSuperScaleType) {  
  240.                 // dx = 0.0f;  
  241.                 // dy = 0.0f;  
  242.             } else if (ScaleType.FIT_CENTER == mSuperScaleType) {  
  243.                 dx = (vWidth - movieWidth * scale) * 0.5f + 0.5f;  
  244.                 dy = (vHeight - movieHeight * scale) * 0.5f + 0.5f;  
  245.             } else {/* ScaleType.FIT_END == mSuperScaleType */  
  246.                 dx = vWidth - movieWidth * scale;  
  247.                 dy = vHeight - movieHeight * scale;  
  248.             }  
  249.             mDrawMatrix.setScale(scale, scale);  
  250.             mDrawMatrix.postTranslate((int) dx, (int) dy);  
  251.         }  
  252.     }  
  253.   
  254.     private int resolveAdjustedSize(int desiredSize, int maxSize, int measureSpec) {  
  255.         Log("resolveAdjustedSize, desiredSize=" + desiredSize + ",maxSize=" + maxSize + ",measureSpec=" + measureSpec);  
  256.         int result = desiredSize;  
  257.         int specMode = MeasureSpec.getMode(measureSpec);  
  258.         int specSize = MeasureSpec.getSize(measureSpec);  
  259.         switch (specMode) {  
  260.         case MeasureSpec.UNSPECIFIED:  
  261.             /* 
  262.              * Parent says we can be as big as we want. Just don't be larger 
  263.              * than max size imposed on ourselves. 
  264.              */  
  265.             result = Math.min(desiredSize, maxSize);  
  266.             break;  
  267.         case MeasureSpec.AT_MOST:  
  268.             // Parent says we can be as big as we want, up to specSize.  
  269.             // Don't be larger than specSize, and don't be larger than  
  270.             // the max size imposed on ourselves.  
  271.             result = Math.min(Math.min(desiredSize, specSize), maxSize);  
  272.             break;  
  273.         case MeasureSpec.EXACTLY:  
  274.             // "60dp"  
  275.             // No choice. Do what we are told.  
  276.             result = specSize;  
  277.             break;  
  278.         }  
  279.         return result;  
  280.     }  
  281.   
  282.     @Override  
  283.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  284.   
  285.         Log("onMeasure");  
  286.   
  287.         if (!FEATURE_IS_GIF_SUPPORTED || mMovie == null) {  
  288.             super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  289.             return;  
  290.         }  
  291.   
  292.         syncParentParameter();  
  293.   
  294.         int w;  
  295.         int h;  
  296.   
  297.         // Desired aspect ratio of the view's contents (not including padding)  
  298.         float desiredAspect = 0.0f;  
  299.   
  300.         // We are allowed to change the view's width  
  301.         boolean resizeWidth = false;  
  302.   
  303.         // We are allowed to change the view's height  
  304.         boolean resizeHeight = false;  
  305.   
  306.         final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);  
  307.         final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);  
  308.   
  309.         if (mMovie == null) {  
  310.             w = h = 0;  
  311.         } else {  
  312.             w = mMovie.width();  
  313.             h = mMovie.height();  
  314.             Log("onMeasure, w = " + w + ", h = " + h);  
  315.             if (w <= 0)  
  316.                 w = 1;  
  317.             if (h <= 0)  
  318.                 h = 1;  
  319.   
  320.             // We are supposed to adjust view bounds to match the aspect  
  321.             // ratio of our drawable. See if that is possible.  
  322.             if (getAdjustViewBounds()) {  
  323.                 resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;  
  324.                 resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;  
  325.                 desiredAspect = (float) w / (float) h;  
  326.             }  
  327.         }  
  328.   
  329.         int widthSize, heightSize;  
  330.   
  331.         Log("onMeasure, resizeWidth=" + resizeWidth + ", resizeHeight=" + resizeHeight);  
  332.         if (resizeWidth || resizeHeight) {  
  333.   
  334.             int maxWidth = getMaxWidth();  
  335.             int maxHeight = getMaxHeight();  
  336.             /* 
  337.              * If we get here, it means we want to resize to match the drawables 
  338.              * aspect ratio, and we have the freedom to change at least one 
  339.              * dimension. 
  340.              */  
  341.   
  342.             // Get the max possible width given our constraints  
  343.             widthSize = resolveAdjustedSize(w + mSuperPaddingLeft + mSuperPaddingRight, maxWidth, widthMeasureSpec);  
  344.   
  345.             // Get the max possible height given our constraints  
  346.             heightSize = resolveAdjustedSize(h + mSuperPaddingTop + mSuperPaddingBottom, maxHeight, heightMeasureSpec);  
  347.   
  348.             if (desiredAspect != 0.0f) {  
  349.                 // See what our actual aspect ratio is  
  350.                 float actualAspect = (float) (widthSize - mSuperPaddingLeft - mSuperPaddingRight)  
  351.                         / (heightSize - mSuperPaddingTop - mSuperPaddingBottom);  
  352.   
  353.                 if (Math.abs(actualAspect - desiredAspect) > 0.0000001) {  
  354.   
  355.                     boolean done = false;  
  356.   
  357.                     // Try adjusting width to be proportional to height  
  358.                     if (resizeWidth) {  
  359.                         int newWidth = (int) (desiredAspect * (heightSize - mSuperPaddingTop - mSuperPaddingBottom))  
  360.                                 + mSuperPaddingLeft + mSuperPaddingRight;  
  361.   
  362.                         // Allow the width to outgrow its original estimate if  
  363.                         // height is fixed.  
  364.                         if (!resizeHeight && !mAdjustViewBoundsCompat) {  
  365.                             widthSize = resolveAdjustedSize(newWidth, maxWidth, widthMeasureSpec);  
  366.                         }  
  367.   
  368.                         if (newWidth <= widthSize) {  
  369.                             widthSize = newWidth;  
  370.                             done = true;  
  371.                         }  
  372.                     }  
  373.   
  374.                     // Try adjusting height to be proportional to width  
  375.                     if (!done && resizeHeight) {  
  376.                         int newHeight = (int) ((widthSize - mSuperPaddingLeft - mSuperPaddingRight) / desiredAspect)  
  377.                                 + mSuperPaddingTop + mSuperPaddingBottom;  
  378.   
  379.                         // Allow the height to outgrow its original estimate if  
  380.                         // width is fixed.  
  381.                         if (!resizeWidth && !mAdjustViewBoundsCompat) {  
  382.                             heightSize = resolveAdjustedSize(newHeight, maxHeight, heightMeasureSpec);  
  383.                         }  
  384.   
  385.                         if (newHeight <= heightSize) {  
  386.                             heightSize = newHeight;  
  387.                         }  
  388.                     }  
  389.                 }  
  390.             }  
  391.         } else {  
  392.             /* 
  393.              * We are either don't want to preserve the drawables aspect ratio, 
  394.              * or we are not allowed to change view dimensions. Just measure in 
  395.              * the normal way. 
  396.              */  
  397.             w += mSuperPaddingLeft + mSuperPaddingRight;  
  398.             h += mSuperPaddingTop + mSuperPaddingBottom;  
  399.   
  400.             w = Math.max(w, getSuggestedMinimumWidth());  
  401.             h = Math.max(h, getSuggestedMinimumHeight());  
  402.   
  403.             widthSize = resolveSizeAndState(w, widthMeasureSpec, 0);  
  404.             heightSize = resolveSizeAndState(h, heightMeasureSpec, 0);  
  405.         }  
  406.   
  407.         Log("onMeasure, widthSize=" + widthSize + ", heightSize=" + heightSize);  
  408.         setMeasuredDimension(widthSize, heightSize);  
  409.   
  410.         configureDrawMatrix();  
  411.     }  
  412.   
  413.     @Override  
  414.     protected void onDraw(Canvas canvas) {  
  415.   
  416.         if (!FEATURE_IS_GIF_SUPPORTED || mMovie == null) {  
  417.             super.onDraw(canvas);  
  418.             return;  
  419.         }  
  420.   
  421.         // Movie set time  
  422.         if (mMovieDuration == 0) {  
  423.             mMovie.setTime(0);  
  424.         } else {  
  425.             long now = android.os.SystemClock.uptimeMillis();  
  426.             if (mMovieStartTime == 0) {  
  427.                 mMovieStartTime = now;// first time  
  428.             }  
  429.             mMovie.setTime((int) ((now - mMovieStartTime) % mMovieDuration));  
  430.         }  
  431.   
  432.         // save the current matrix and clip of canvas  
  433.         int saveCount = canvas.getSaveCount();  
  434.         canvas.save();  
  435.   
  436.         boolean superCropToPadding = getCropToPadding();  
  437.         Log("superCropToPadding = " + superCropToPadding, DB_DETAIL);  
  438.         if (superCropToPadding) {  
  439.             int superScrollX = getScrollX();  
  440.             int superScrollY = getScrollY();  
  441.             int superRight = getRight();  
  442.             int superLeft = getLeft();  
  443.             int superBottom = getBottom();  
  444.             int superTop = getTop();  
  445.             canvas.clipRect(superScrollX + mSuperPaddingLeft, superScrollY + mSuperPaddingTop, superScrollX  
  446.                     + superRight - superLeft - mSuperPaddingRight, superScrollY + superBottom - superTop  
  447.                     - mSuperPaddingBottom);  
  448.         }  
  449.   
  450.         if (mDrawMatrix != null && !mDrawMatrix.isIdentity()) {  
  451.             canvas.concat(mDrawMatrix);  
  452.         }  
  453.         mMovie.draw(canvas, mSuperPaddingLeft, mSuperPaddingTop);  
  454.   
  455.         canvas.restoreToCount(saveCount);  
  456.         invalidate();  
  457.     }  
  458.   
  459.     @Override  
  460.     public void setWillNotCacheDrawing(boolean willNotCacheDrawing) {  
  461.         if (FEATURE_IS_GIF_SUPPORTED && mMovie != null) {  
  462.             super.setWillNotCacheDrawing(false);  
  463.         } else {  
  464.             super.setWillNotCacheDrawing(willNotCacheDrawing);  
  465.         }  
  466.     }  
  467.   
  468.     private static void Log(String log) {  
  469.         if (DB) {  
  470.             Log.i(DB_TAG, log);  
  471.         }  
  472.     }  
  473.   
  474.     private static void Log(String log, boolean enable) {  
  475.         if (enable) {  
  476.             Log.i(DB_TAG, log);  
  477.         }  
  478.     }  
  479. }  

    初始化Movie对象以及测试用代码:

[java]  view plain copy
  1. package com.yarkey.giftest2;  
  2.   
  3. import java.io.BufferedInputStream;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6.   
  7. import android.app.Activity;  
  8. import android.graphics.Movie;  
  9. import android.net.Uri;  
  10. import android.os.Bundle;  
  11. import android.os.Environment;  
  12. import android.util.Log;  
  13. import android.view.View;  
  14. import android.view.View.OnClickListener;  
  15. import android.widget.Button;  
  16. import android.widget.ImageView;  
  17. import android.widget.ImageView.ScaleType;  
  18.   
  19. public class MainActivity extends Activity implements OnClickListener {  
  20.   
  21.     private static final String TAG = "MainActivity";  
  22.   
  23.     ImageView image2;  
  24.     MovieImageView mView;  
  25.     Button mBtnA, mBtnB, mBtnC, mBtnD;  
  26.   
  27.     static final String ExternalPath = Environment.getExternalStorageDirectory().getPath();  
  28.   
  29.     // setVisibility test  
  30.     private static int sIndexA = 0;  
  31.     private static int[] sVisibles = new int[] { View.INVISIBLE, View.GONE, View.VISIBLE };  
  32.     private static String[] sDescrA = new String[] { "INVISIBLE""GONE""VISIBLE" };  
  33.   
  34.     // setScaleType test  
  35.     private static int sIndexB = 0;  
  36.     private static ScaleType[] sScaleTypes = new ScaleType[] { ScaleType.CENTER, ScaleType.CENTER_CROP,  
  37.             ScaleType.CENTER_INSIDE, ScaleType.FIT_CENTER, ScaleType.FIT_END, ScaleType.FIT_START, ScaleType.FIT_XY };  
  38.     private static String[] sDescrB = new String[] { "CENTER""CENTER_CROP""CENTER_INSIDE""FIT_CENTER""FIT_END",  
  39.             "FIT_START""FIT_XY" };  
  40.   
  41.     // setImageResource test  
  42.     private static int sIndexC = 0;  
  43.     private static int[] sResources = new int[] { R.drawable.pngtest1, R.drawable.giftest1, R.drawable.giftest2,  
  44.             R.drawable.giftest3 };  
  45.   
  46.     // setImageUri test  
  47.     private static int sIndexD = 0;  
  48.     private static Uri[] sImageUris = new Uri[] { Uri.parse("file://" + ExternalPath + "/giftest1.gif"),  
  49.             Uri.parse("file://" + ExternalPath + "/giftest2.gif"),  
  50.             Uri.parse("file://" + ExternalPath + "/giftest3.gif") };  
  51.   
  52.     // setMovie test  
  53.     private static int sIndexE = 0;  
  54.     private static Movie[] sMovies = new Movie[4];  
  55.     private static String[] sDescrE = new String[] { "Movie1""Movie2""Movie3""Movie4" };  
  56.   
  57.     private static int sIndexF = 0;  
  58.   
  59.     /** Called when the activity is first created. */  
  60.     @Override  
  61.     public void onCreate(Bundle savedInstanceState) {  
  62.         super.onCreate(savedInstanceState);  
  63.         // setContentView(R.layout.activity_main);  
  64.         setContentView(R.layout.activity_main);  
  65.   
  66.         image2 = (ImageView) this.findViewById(R.id.image2);  
  67.         mView = (MovieImageView) this.findViewById(R.id.gifView);  
  68.   
  69.         mBtnA = (Button) this.findViewById(R.id.btnA);  
  70.         mBtnB = (Button) this.findViewById(R.id.btnB);  
  71.         mBtnC = (Button) this.findViewById(R.id.btnC);  
  72.         mBtnD = (Button) this.findViewById(R.id.btnD);  
  73.   
  74.         mBtnA.setOnClickListener(this);  
  75.         mBtnB.setOnClickListener(this);  
  76.         mBtnC.setOnClickListener(this);  
  77.         mBtnD.setOnClickListener(this);  
  78.   
  79.         image2 = (ImageView) this.findViewById(R.id.image2);  
  80.   
  81.         InputStream uriInputStream = null;  
  82.         try {  
  83.             uriInputStream = new BufferedInputStream(this.getContentResolver().openInputStream(sImageUris[0]));  
  84.             uriInputStream.mark(uriInputStream.available());  
  85.             sMovies[0] = Movie.decodeStream(uriInputStream);  
  86.   
  87.             uriInputStream = new BufferedInputStream(this.getContentResolver().openInputStream(sImageUris[1]));  
  88.             uriInputStream.mark(uriInputStream.available());  
  89.             sMovies[1] = Movie.decodeStream(uriInputStream);  
  90.   
  91.             uriInputStream = new BufferedInputStream(this.getContentResolver().openInputStream(sImageUris[2]));  
  92.             uriInputStream.mark(uriInputStream.available());  
  93.             sMovies[2] = Movie.decodeStream(uriInputStream);  
  94.   
  95.             uriInputStream = new BufferedInputStream(this.getContentResolver().openInputStream(  
  96.                     Uri.parse("android.resource://com.yarkey.giftest2/" + R.raw.largegif)));  
  97.             uriInputStream.mark(uriInputStream.available());  
  98.             sMovies[3] = Movie.decodeStream(uriInputStream);  
  99.   
  100.         } catch (IOException e) {  
  101.             // TODO Auto-generated catch block  
  102.             e.printStackTrace();  
  103.         }  
  104.     }  
  105.   
  106.     public void onClick(View arg0) {  
  107.         Log.i("MainActivity""onClick");  
  108.         switch (arg0.getId()) {  
  109.         case R.id.btnA:  
  110.             image2.setVisibility(sVisibles[sIndexA]);  
  111.             mView.setVisibility(sVisibles[sIndexA]);  
  112.             mBtnA.setText("Visible: " + sDescrA[sIndexA]);  
  113.             sIndexA++;  
  114.             if (sIndexA >= sVisibles.length) {  
  115.                 sIndexA = 0;  
  116.             }  
  117.             break;  
  118.         case R.id.btnB:  
  119.             image2.setScaleType(sScaleTypes[sIndexB]);  
  120.             mView.setScaleType(sScaleTypes[sIndexB]);  
  121.             mBtnB.setText("ScaleType: " + sDescrB[sIndexB]);  
  122.             sIndexB++;  
  123.             if (sIndexB >= sScaleTypes.length) {  
  124.                 sIndexB = 0;  
  125.             }  
  126.             break;  
  127.         case R.id.btnC:  
  128.             image2.setImageResource(R.drawable.ic_launcher);  
  129.             mView.setMovie(sMovies[sIndexE]);  
  130.             mBtnC.setText("setMovie: " + sDescrE[sIndexE]);  
  131.             sIndexE++;  
  132.             if (sIndexE >= sMovies.length) {  
  133.                 sIndexE = 0;  
  134.             }  
  135.             break;  
  136.         case R.id.btnD:  
  137.             switch (sIndexF) {  
  138.             case 0:  
  139.                 image2.setPadding(100000);  
  140.                 mView.setPadding(100000);  
  141.                 mBtnD.setText("setPadding: 100,0,0,0");  
  142.                 break;  
  143.             case 1:  
  144.                 image2.setPadding(010000);  
  145.                 mView.setPadding(010000);  
  146.                 mBtnD.setText("setPadding: 0,100,0,0");  
  147.                 break;  
  148.             case 2:  
  149.                 image2.setPadding(001000);  
  150.                 mView.setPadding(001000);  
  151.                 mBtnD.setText("setPadding: 0,0,100,0");  
  152.                 break;  
  153.             case 3:  
  154.                 image2.setPadding(000100);  
  155.                 mView.setPadding(000100);  
  156.                 mBtnD.setText("setPadding: 0,0,0,100");  
  157.                 break;  
  158.             case 4:  
  159.                 image2.setPadding(100100100100);  
  160.                 mView.setPadding(100100100100);  
  161.                 mBtnD.setText("setPadding: 100,100,100,100");  
  162.                 break;  
  163.             case 5:  
  164.                 image2.setPadding(0000);  
  165.                 mView.setPadding(0000);  
  166.                 mBtnD.setText("setPadding: no");  
  167.                 break;  
  168.             }  
  169.             sIndexF++;  
  170.             if (sIndexF == 6) {  
  171.                 sIndexF = 0;  
  172.             }  
  173.             break;  
  174.         }  
  175.     }  
  176.   
  177.   
  178. }  

    布局文件:

[html]  view plain copy
  1. <ScrollView  
  2.     xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:id="@+id/scrollView1"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="wrap_content" >  
  6.   
  7.     <LinearLayout  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="fill_parent"  
  10.         android:orientation="vertical" >  
  11.   
  12.         <TextView  
  13.             android:id="@+id/textView1"  
  14.             android:layout_width="match_parent"  
  15.             android:layout_height="wrap_content"  
  16.             android:background="#FFBBBBBB"  
  17.             android:gravity="center"  
  18.             android:padding="10dp"  
  19.             android:text="InCallScreen"  
  20.             android:textAppearance="?android:attr/textAppearanceLarge" />  
  21.   
  22.         <ImageView  
  23.             android:id="@+id/image2"  
  24.             android:layout_width="match_parent"  
  25.             android:layout_height="100dp"  
  26.             android:background="#AACCFF"  
  27.             android:scaleType="center" />  
  28.   
  29.         <com.yarkey.giftest2.MovieImageView  
  30.             android:id="@+id/gifView"  
  31.             android:layout_width="match_parent"  
  32.             android:layout_height="100dp"  
  33.             android:background="#88AADD"  
  34.             android:scaleType="center" />  
  35.   
  36.         <Button  
  37.             android:id="@+id/btnA"  
  38.             android:layout_width="match_parent"  
  39.             android:layout_height="wrap_content"  
  40.             android:padding="10dp"  
  41.             android:text="Visible:VISIBLE" />  
  42.   
  43.         <Button  
  44.             android:id="@+id/btnB"  
  45.             android:layout_width="match_parent"  
  46.             android:layout_height="wrap_content"  
  47.             android:padding="10dp"  
  48.             android:text="ScaleType:default" />  
  49.   
  50.         <Button  
  51.             android:id="@+id/btnC"  
  52.             android:layout_width="match_parent"  
  53.             android:layout_height="wrap_content"  
  54.             android:padding="10dp"  
  55.             android:text="setMovie:null" />  
  56.   
  57.         <Button  
  58.             android:id="@+id/btnD"  
  59.             android:layout_width="match_parent"  
  60.             android:layout_height="wrap_content"  
  61.             android:padding="10dp"  
  62.             android:text="setPadding:no" />  
  63.   
  64.         <LinearLayout  
  65.             android:layout_width="match_parent"  
  66.             android:layout_height="match_parent"  
  67.             android:orientation="vertical" >  
  68.         </LinearLayout>  
  69.     </LinearLayout>  
  70.   
  71. </ScrollView>  


    好吧,整个帖子写得我自己都觉得非常乱了。

    不过,记住一下ImageView流程也好: setImageDrawable -> configureBounds -> requestLayout -> onMeasure -> setFrame -> onDraw

    我们计算Movie在View中显示的位置大小平移等效果,必须在setFrame函数调用后,才能执行(此时getWidth(), getHeight() 才会生效)。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值