【问题标题】:Android: make animation from still imagesAndroid:从静止图像制作动画
【发布时间】:2011-04-10 10:20:22
【问题描述】:

我在 drawable 目录中有一系列静止图像,总共有 500 多张图像。我需要制作一个动画(每秒加载大约 20 张图像)。我希望它能够顺利运行并且没有内存不足异常。

我的想法是,将 2 到 3 秒的图像(40 到 60 个图像)加载到内存中并显示,然后将它们处理掉(释放内存),然后再将接下来 2 到 3 秒的图像加载。这种技术可以防止内存不足异常。它只是一个想法,我不知道它是否是一个好主意。请用一些代码指导我一些更好的想法......如果我的想法更好并且可以工作,那么请告诉我一些帮助代码来做到这一点。

阅读回复并按照您的建议进行操作后,我编写了如下代码:

  <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/llMain">

<ViewFlipper android:id="@+id/imageflipper"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
<ImageView android:id="@+id/ImageView01"
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent"        
        android:scaleType="centerInside"        
        android:layout_gravity="center" />
    </ViewFlipper>
    </LinearLayout>

这是我做动画的代码:

public class Animation extends Activity {
ViewFlipper flipper;
int myIndex = 216;
private final Handler handler = new Handler();
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    flipper=(ViewFlipper)findViewById(R.id.imageflipper); 
    doTheAutoRefresh();
        //displayData();
}

private void doTheAutoRefresh() {
    handler.postDelayed(new Runnable() {
        public void run(){
            displayData(); // this is where you put your refresh code
            doTheAutoRefresh();
             }
         }, 30);

}

private void displayData()
{
    Resources r = getResources();
    if(myIndex > 230){
        myIndex = 216;
        ImageView myImg = (ImageView)findViewById(R.id.ImageView01);
        myImg.setImageResource(r.getIdentifier("drum0" + myIndex, "drawable", "com.vt.animation"));

        myIndex += 1;
        flipper.showNext();
    }
    else{
        ImageView myImg = (ImageView)findViewById(R.id.ImageView01);

        myImg.setImageResource(r.getIdentifier("drum0" + myIndex, "drawable", "com.vt.animation"));

        myIndex += 1;
        flipper.showNext();
    }
}

}

但它非常慢。我已将刷新时间设置为 30 毫秒,但实际上它的刷新速度不会太快,而是刷新时间约为 1 秒。有什么建议可以让它感觉像真正的动画一样快吗?

谢谢,

【问题讨论】:

    标签: android image animation


    【解决方案1】:

    使用FrameAnimation,例如res/drawable/movie.xml

    <?xml version="1.0" encoding="utf-8"?>
    <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
        android:oneshot="true">
        <item android:drawable="@drawable/frame1" android:duration="50" />
        <item android:drawable="@drawable/frame2" android:duration="50" />
        <item android:drawable="@drawable/frame3" android:duration="50" />
        etc...
    </animation-list>
    

    然后在 Java 中:

    imageView.setBackgroundResource(R.drawable.movie);
    AnimationDrawable anim = (AnimationDrawable) imageView.getBackground();
    anim.start();
    

    【讨论】:

    • 在没有 OutOfMemoryException 的情况下它是否适用于 500 张图像?它会在绘制每张图像后调用 gc() 吗?什么是暂停/恢复/停止/播放之类的东西?我可以用这种方法实现这些功能吗?
    • 我没试过这么多图片,但它是由Android框架处理的,所以它应该是智能的。我很确定它可以很好地处理内存。也就是说,对于完整的视频来说,这可能还不够......为什么不把你的图像变成一个视频,把它放到 res/raw 中,然后用 MediaPlayer 播放它:这很容易,而且它有暂停,玩,寻找等等。并且压缩会更好,可能会更好......
    • 非常感谢。我认为视频无法解决我的问题,因为我正在尝试开发一些应用程序,比如会说话的圣诞老人。你可以在这里查看link
    • 现在我尝试了此链接link (我尝试过的最后一个)中提到的技术,在添加每个图像后我调用codesystem.gc() 的循环中进行了一些更改到 ViewFlipper。到目前为止,除了加载(应用程序的加载速度很慢)之外,它看起来都很棒。您对此的建议将不胜感激。 :)
    • 这会导致内存不足错误,因为看起来它会将它们全部加载到内存中。
    【解决方案2】:

    好的。这么多天后,我遇到的最大问题和最简单的解决方案。我从没想到会这么容易做到……:D

    我已经使用处理程序和计时器来实现,只需要一个图像视图,没有鳍状肢,没有动画师什么都没有......这是我的解决方案:

    ----- main.xml 文件 -----

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/background">
    
    <ImageView 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:id="@+id/imageView1">
    </ImageView>
    

    这是我的做法:

    public class MainActivity extends Activity {
    private ImageView _imagView;
    private Timer _timer;
    private int _index;
    private MyHandler handler;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        handler= new MyHandler();
        _imagView=(ImageView) findViewById(R.id.imageView1);
    
        _index=0;
        _timer= new Timer();
        _timer.schedule(new TickClass(), 500, 200);
    }
    
    private class TickClass extends TimerTask
    {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            handler.sendEmptyMessage(_index);
            _index++;
        }
    }
    
    private class MyHandler extends Handler
    {
        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            super.handleMessage(msg);
    
            try {
                    Bitmap bmp= BitmapFactory.decodeStream(MainActivity.this.getAssets().open("drum_"+_index+".png"));
                    _imagView.setImageBitmap(bmp);
    
                    Log.v("Loaing Image: ",_index+"");
            } catch (IOException e) {
                // TODO Auto-generated catch block
                Log.v("Exception in Handler ",e.getMessage());
            }
        }
    }
    

    }

    注意:我已将所有图片放入资产目录。

    它就是这么简单,没什么大不了的……

    我希望它对想要这样的人有所帮助:)

    【讨论】:

    • @Mudasser Hassan 如果所有图像都在服务器端,我必须下载所有图像并执行此类工作。 (播放视频或图像动画)。我该怎么做。我需要所有图像都显示为 360 度旋转单个图像。就像 mathieusavard.info/threesixty/demo.html
    • 谢谢。如果所有图像都来自服务器,我们必须下载所有图像并显示。你给点主意了吗?我正面临着这方面的问题。请帮忙。
    • 如果你使用Handler.post(),代码会更短。另一件事是您在 UI 线程上解码图像,这不好。更优雅的解决方案是提前解码背景中的下一张图像(使用AsyncTask),并在 200 毫秒后或图像解码后显示,如果需要更多时间。
    【解决方案3】:

    加载多张图片非常昂贵。

    我认为最好加载包含该特定动画的所有动作的单个图像。动画片段。

    private Bitmap animation = BitmapFactory.decodeResource(getResources(), R.drawable.myPng);

    思路是遍历位图。

    【讨论】:

    • 雪碧不是我的情况。我告诉过你,我有 500 多张图片。第一个我不能制作 500 张图片的条带,但是如果我制作,我认为它不可能加载那个巨大的条带,或者这不是一个好方法......寻找一些更好的想法......
    【解决方案4】:

    嗯,我正在使用 viewFlipper,在视图之间切换。这个的好处是你可以看到之前的图片滑出而新的图片滑入。

    内图显示方式:

        if (direction == NEXT) {
            viewFlipper.setInAnimation(slideLeftIn);
            viewFlipper.setOutAnimation(slideLeftOut);
    
            if (currImg < max)
                currImg++;
            if (currImg == max)
                currImg = 0;
    
            if (currentView == 0) {
                currentView = 1;
                ImageView iv = (ImageView) findViewById(R.id.ImageView02);
                iv.setImageResource(images[currImg]);
            } else if (currentView == 1) {
                currentView = 2;
                ImageView iv = (ImageView) findViewById(R.id.ImageView03);
                iv.setImageResource(images[currImg]);
            } else {
                currentView = 0;
                ImageView iv = (ImageView) findViewById(R.id.ImageView01);
                iv.setImageResource(images[currImg]);
            }
            viewFlipper.showNext();
        }
        else if (direction == PREV) {
            viewFlipper.setInAnimation(slideRightIn);
            viewFlipper.setOutAnimation(slideRightOut);
    
            if (currImg > 0)
                currImg--;
            else if (currImg <= 0)
                currImg = (max-1);
    
            if (currentView == 0) {
                currentView = 2;
                ImageView iv = (ImageView) findViewById(R.id.ImageView03);
                iv.setImageResource(images[currImg]);
            } else if (currentView == 2) {
                currentView = 1;
                ImageView iv = (ImageView) findViewById(R.id.ImageView02);
                iv.setImageResource(images[currImg]);
            } else {
                currentView = 0;
                ImageView iv = (ImageView) findViewById(R.id.ImageView01);
                iv.setImageResource(images[currImg]);
            }
            viewFlipper.showPrevious();
    

    在 XML 文件中:

            <ViewFlipper android:id="@+id/imageflipper"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" >
    
            <ImageView android:id="@+id/ImageView01"
                android:layout_width="wrap_content" 
                android:layout_height="wrap_content"        
                android:scaleType="centerInside"        
                android:layout_gravity="center" />
    
            <ImageView android:id="@+id/ImageView02"
                android:layout_width="wrap_content" 
                android:layout_height="wrap_content"        
                android:scaleType="centerInside"        
                android:layout_gravity="center" />
    
            <ImageView android:id="@+id/ImageView03"
                android:layout_width="wrap_content" 
                android:layout_height="wrap_content"        
                android:scaleType="centerInside"        
                android:layout_gravity="center" />
    
            </ViewFlipper>
    

    【讨论】:

    • 还有两件事我很困惑。 1、回收码在哪里?您如何回收旧图像并加载新图像? 2,我想做一个动画,不是左右翻转图像而是平滑的动画(新图像加载和旧图像自动卸载)......我是Android新手,对JAVA也不太了解。任何代码示例都会非常有帮助和赞赏...... :)
    • 好吧,Java 有垃圾收集器,所以你不必像 obj-c for iphone 等那样手动释放内存。你可以使用 system.gc() 来强制启动垃圾收集器。 iv.setImageResource() 正在更改 imageview 中的图像,我声明了 2 个动画(用于旧图片和新图片),它们将用于为开关设置动画。如果您想要自动动画而不是手势,只需添加一些计时器并使用上述方法,例如每 2 秒或 smth。 Ofc,从 xml 声明动画并保留方向的值(在我的情况下,我需要 NEXT 或 PREV)。
    • 我已经阅读了您的代码,并在其他帖子/网站上进行了更多探索之后。我编辑了我的问题并将我的代码添加到其中。它的刷新太慢了。我想让它变得非常快,感觉就像真正的动画。有什么建议吗?谢谢
    猜你喜欢
    • 1970-01-01
    • 2012-05-04
    • 1970-01-01
    • 1970-01-01
    • 2014-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-28
    相关资源
    最近更新 更多