相信很多朋友在使用AnimationDrawable做帧动画时,图片过大或者数量过多,很容易就遇到内存溢出问题;
图片过大有很多相关的处理方法,但使用xml配置文件加载数量过多的图片时(40-50张就出现溢出),就比较棘手。
在stackoverflow找到的相关解决方法:http://stackoverflow.com/questions/8692328/causing-outofmemoryerror-in-frame-by-frame-animation-in-android
<-------------------------------------------------------------------------------------------------->
需要jar包:commons-io-1.4.jar
工具类代码如下:
1 import java.io.IOException; 2 import java.util.ArrayList; 3 import java.util.List; 4 import org.apache.commons.io.IOUtils; 5 import org.xmlpull.v1.XmlPullParser; 6 import org.xmlpull.v1.XmlPullParserException; 7 import android.content.Context; 8 import android.content.res.XmlResourceParser; 9 import android.graphics.BitmapFactory; 10 import android.graphics.drawable.AnimationDrawable; 11 import android.graphics.drawable.BitmapDrawable; 12 import android.graphics.drawable.Drawable; 13 import android.os.Handler; 14 import android.widget.ImageView; 15 23 public class MyAnimationDrawable { 24 public static class MyFrame { 25 byte[] bytes; 26 int duration; 27 Drawable drawable; 28 boolean isReady = false; 29 } 30 31 public interface OnDrawableLoadedListener { 32 public void onDrawableLoaded(ListmyFrames); 33 } 34 35 40 public static void animateRawManuallyFromXML(int resourceId, 41 final ImageView imageView, final Runnable onStart, 42 final Runnable onComplete) { 43 loadRaw(resourceId, imageView.getContext(), 44 new OnDrawableLoadedListener() { 45 @Override 46 public void onDrawableLoaded(List myFrames) { 47 if (onStart != null) { 48 onStart.run(); 49 } 50 animateRawManually(myFrames, imageView, onComplete); 51 } 52 }); 53 } 54 55 // 56 private static void loadRaw(final int resourceId, final Context context, 57 final OnDrawableLoadedListener onDrawableLoadedListener) { 58 loadFromXml(resourceId, context, onDrawableLoadedListener); 59 } 60 61 // 62 private static void loadFromXml(final int resourceId, 63 final Context context, 64 final OnDrawableLoadedListener onDrawableLoadedListener) { 65 new Thread(new Runnable() { 66 @Override 67 public void run() { 68 final ArrayList myFrames = new ArrayList (); 69 70 XmlResourceParser parser = context.getResources().getXml( 71 resourceId); 72 73 try { 74 int eventType = parser.getEventType(); 75 while (eventType != XmlPullParser.END_DOCUMENT) { 76 if (eventType == XmlPullParser.START_DOCUMENT) { 77 78 } else if (eventType == XmlPullParser.START_TAG) { 79 80 if (parser.getName().equals("item")) { 81 byte[] bytes = null; 82 int duration = 1000; 83 84 for (int i = 0; i < parser.getAttributeCount(); i++) { 85 if (parser.getAttributeName(i).equals( 86 "drawable")) { 87 int resId = Integer.parseInt(parser 88 .getAttributeValue(i) 89 .substring(1)); 90 bytes = IOUtils.toByteArray(context 91 .getResources() 92 .openRawResource(resId)); 93 } else if (parser.getAttributeName(i) 94 .equals("duration")) { 95 duration = parser.getAttributeIntValue( 96 i, 1000); 97 } 98 } 99 100 MyFrame myFrame = new MyFrame(); 101 myFrame.bytes = bytes; 102 myFrame.duration = duration; 103 myFrames.add(myFrame); 104 } 105 106 } else if (eventType == XmlPullParser.END_TAG) { 107 108 } else if (eventType == XmlPullParser.TEXT) { 109 110 } 111 112 eventType = parser.next(); 113 } 114 } catch (IOException e) { 115 e.printStackTrace(); 116 } catch (XmlPullParserException e2) { 117 // TODO: handle exception 118 e2.printStackTrace(); 119 } 120 121 // Run on UI Thread 122 new Handler(context.getMainLooper()).post(new Runnable() { 123 @Override 124 public void run() { 125 if (onDrawableLoadedListener != null) { 126 onDrawableLoadedListener.onDrawableLoaded(myFrames); 127 } 128 } 129 }); 130 } 131 }).run(); 132 } 133 134 // 135 private static void animateRawManually(List myFrames, 136 ImageView imageView, Runnable onComplete) { 137 animateRawManually(myFrames, imageView, onComplete, 0); 138 } 139 140 // 141 private static void animateRawManually(final List myFrames, 142 final ImageView imageView, final Runnable onComplete, 143 final int frameNumber) { 144 final MyFrame thisFrame = myFrames.get(frameNumber); 145 146 if (frameNumber == 0) { 147 thisFrame.drawable = new BitmapDrawable(imageView.getContext() 148 .getResources(), BitmapFactory.decodeByteArray( 149 thisFrame.bytes, 0, thisFrame.bytes.length)); 150 } else { 151 MyFrame previousFrame = myFrames.get(frameNumber - 1); 152 ((BitmapDrawable) previousFrame.drawable).getBitmap().recycle(); 153 previousFrame.drawable = null; 154 previousFrame.isReady = false; 155 } 156 157 imageView.setImageDrawable(thisFrame.drawable); 158 new Handler().postDelayed(new Runnable() { 159 @Override 160 public void run() { 161 // Make sure ImageView hasn't been changed to a different Image 162 // in this time 163 if (imageView.getDrawable() == thisFrame.drawable) { 164 if (frameNumber + 1 < myFrames.size()) { 165 MyFrame nextFrame = myFrames.get(frameNumber + 1); 166 167 if (nextFrame.isReady) { 168 // Animate next frame 169 animateRawManually(myFrames, imageView, onComplete, 170 frameNumber + 1); 171 } else { 172 nextFrame.isReady = true; 173 } 174 } else { 175 if (onComplete != null) { 176 onComplete.run(); 177 } 178 } 179 } 180 } 181 }, thisFrame.duration); 182 183 // Load next frame 184 if (frameNumber + 1 < myFrames.size()) { 185 new Thread(new Runnable() { 186 @Override 187 public void run() { 188 MyFrame nextFrame = myFrames.get(frameNumber + 1); 189 nextFrame.drawable = new BitmapDrawable(imageView 190 .getContext().getResources(), 191 BitmapFactory.decodeByteArray(nextFrame.bytes, 0, 192 nextFrame.bytes.length)); 193 if (nextFrame.isReady) { 194 // Animate next frame 195 animateRawManually(myFrames, imageView, onComplete, 196 frameNumber + 1); 197 } else { 198 nextFrame.isReady = true; 199 } 200 201 } 202 }).run(); 203 } 204 } 205 206 //带时间的方法 211 public static void animateManuallyFromRawResource( 212 int animationDrawableResourceId, ImageView imageView, 213 Runnable onStart, Runnable onComplete, int duration) throws IOException, 214 XmlPullParserException { 215 AnimationDrawable animationDrawable = new AnimationDrawable(); 216 217 XmlResourceParser parser = imageView.getContext().getResources() 218 .getXml(animationDrawableResourceId); 219 220 int eventType = parser.getEventType(); 221 while (eventType != XmlPullParser.END_DOCUMENT) { 222 if (eventType == XmlPullParser.START_DOCUMENT) { 223 224 } else if (eventType == XmlPullParser.START_TAG) { 225 226 if (parser.getName().equals("item")) { 227 Drawable drawable = null; 228 229 for (int i = 0; i < parser.getAttributeCount(); i++) { 230 if (parser.getAttributeName(i).equals("drawable")) { 231 int resId = Integer.parseInt(parser 232 .getAttributeValue(i).substring(1)); 233 byte[] bytes = IOUtils.toByteArray(imageView 234 .getContext().getResources() 235 .openRawResource(resId));//IOUtils.readBytes 236 drawable = new BitmapDrawable(imageView 237 .getContext().getResources(), 238 BitmapFactory.decodeByteArray(bytes, 0, 239 bytes.length)); 240 } else if (parser.getAttributeName(i) 241 .equals("duration")) { 242 duration = parser.getAttributeIntValue(i, 66); 243 } 244 } 245 246 animationDrawable.addFrame(drawable, duration); 247 } 248 249 } else if (eventType == XmlPullParser.END_TAG) { 250 251 } else if (eventType == XmlPullParser.TEXT) { 252 253 } 254 255 eventType = parser.next(); 256 } 257 258 if (onStart != null) { 259 onStart.run(); 260 } 261 animateDrawableManually(animationDrawable, imageView, onComplete, 0); 262 } 263 264 private static void animateDrawableManually( 265 final AnimationDrawable animationDrawable, 266 final ImageView imageView, final Runnable onComplete, 267 final int frameNumber) { 268 final Drawable frame = animationDrawable.getFrame(frameNumber); 269 imageView.setImageDrawable(frame); 270 new Handler().postDelayed(new Runnable() { 271 @Override 272 public void run() { 273 // Make sure ImageView hasn't been changed to a different Image 274 // in this time 275 if (imageView.getDrawable() == frame) { 276 if (frameNumber + 1 < animationDrawable.getNumberOfFrames()) { 277 // Animate next frame 278 animateDrawableManually(animationDrawable, imageView, 279 onComplete, frameNumber + 1); 280 } else { 281 // Animation complete 282 if (onComplete != null) { 283 onComplete.run(); 284 } 285 } 286 } 287 } 288 }, animationDrawable.getDuration(frameNumber)); 289 } 290 }
调用方法如下:(帧动画的使用这里不说了,比较容易实现)
参数:anm_image_view-->自定义的xml文件; anm_image_view-->展示的ImageView控件;
1 MyAnimationDrawable.animateRawManuallyFromXML(R.anim.welcome_anim, 2 anm_image_view, new Runnable() { 3 4 @Override 5 public void run() { 6 // TODO onStart 7 // 动画开始时回调 9 } 10 }, new Runnable() { 11 12 @Override 13 public void run() { 14 // TODO onComplete 15 // 动画结束时回调 18 } 19 }); 25 }