【问题标题】:Simulate animation inside custom view on Android在 Android 上的自定义视图中模拟动画
【发布时间】:2014-03-21 08:56:01
【问题描述】:

我有一个自定义视图,它看起来像一个数字从 0 到 9 的旋转轮。 基本上,当我从服务器下载某些内容时,它会从 0 向下滚动到 9,当返回值时,动画就会停止。动画是通过更新我正在绘制的文本的 Y 值来制作的

一些相关代码:

public class TimeSpinner extends View
{

    .....
    private void initialize()
    {
    .....

        handler = new Handler();
        repetitiveRunnable = new Runnable() {
            @Override
            public void run() {
                updateData();
            }
        };

    }

    public void updateData() {

        float delta = 3;

        mDigitY += delta;
        mDigitAboveY += delta;
        mDigitBelowY += delta;

        //Test if animation needs to stop
        if (mCurrentDigit == animateToDigit) {
            handler.removeCallbacks(repetitiveRunnable);
        } else {
            handler.postDelayed(repetitiveRunnable, 5);
        }

        invalidate();
    }


    public void startLoadingDigit() {
        handler.postDelayed(repetitiveRunnable, 5);
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);

        canvas.drawText(mDigitString, mDigitX, mDigitY, mDigitPaint);
        canvas.drawText(mDigitAboveString, mDigitX, mDigitAboveY, mDigitPaint);
        canvas.drawText(mDigitBelowString, mDigitX, mDigitBelowY, mDigitPaint);

    }

}

问题是 UI 线程需要在其他视图上进行一些绘图,因此根据手机的速度,动画并不流畅。就像帧速率不好,或者有时会停止半秒。在功能强大的设备上相当不错。

现在的问题是,我该怎么做才能使动画流畅,而与应用程序正在做什么无关? View 很简单,不是 SurfaceView。我应该以某种方式使用线程吗?一个代码示例会很棒。

稍后编辑。

我尝试使用 AsycTask 更新 Y 坐标

public class TimeSpinnerTask extends AsyncTask<Void, Void, Void> {

        @Override
        protected Void doInBackground(Void... arg0) {
            Thread.currentThread().setPriority(Thread.MAX_PRIORITY);

            while (animationRunning) {

                updateData();
                publishProgress();

                try {
                    Thread.sleep(35);
                } catch (InterruptedException e) {

                }
            }
            return null;
        }


        @Override
        protected void onProgressUpdate(Void... values) {
            invalidate();
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(Void result) {
            invalidate();
            super.onPostExecute(result);
        }
    }

public void updateData() {

        mDigitY += delta;
        mDigitAboveY += delta;
        mDigitBelowY += delta;

        if (mDigitAboveY > findCenterY(mCurrentDigit)) {
            setCurrentDigit(mDigitAbove);
        }

        if (mCurrentDigit == animateToDigit) {

            animationRunning = false;

            setCurrentDigit(animateToDigit);
        }
    }

虽然这看起来更流畅一些,但在旧设备上我仍然会快门,有时动画甚至会暂停 1 秒钟然后继续。

异步任务以executeOnExecutor 启动,视图显示在 Android Maps V2 的地图控件上。尤其是在地图加载图块时会出现模板。

也许我没有采用最好的方法。这是我想要获得的最终结果: 有什么想法吗?

【问题讨论】:

  • 您的帖子延迟了 5 毫秒?这意味着 200 fps,为什么?
  • 每个 postDelayed 将 3 添加到 Y 坐标...在 5 毫秒时,我得到了合理的速度。这可能与不使用单独的线程有关......
  • 然后使用自定义动画,覆盖 applyTransformation 并根据其 interpolatedTime 更改 Y 坐标
  • @pskink 我不认为这很容易。由于我正在制作动画,因此我需要在屏幕上显示 2 个数字,当滚动发生时,我不确定是否可以使用动画来实现。
  • 一个放在 Y 上,第二个放在 Y + SOMECONSTANTOFFSET 上?对吗?

标签: android multithreading android-animation android-custom-view


【解决方案1】:

您是否尝试过使用ValueAnimator?这是为管理类似于您的动画而设计的。它将为您处理线程和帧速率。

// Create a new value animator that will use the range 0 to 1
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);

// It will take 5000ms for the animator to go from 0 to 1
animator.setDuration(5000);

// Callback that executes on animation steps. 
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float value = ((Float) (animation.getAnimatedValue())).floatValue();

        Log.d("ValueAnimator", "value=" + value);

        // Here you can now translate or redraw your view
        // You need to map 'value' to your animation in regards to time
        // eg) mDigitY = value; invalidate();
    }
});

您也可以通过使用OvershootInterpolator 在动画结束时实现弹性效果。

animator.setInterpolator(new OvershootInterpolator());

【讨论】:

  • 很有趣,所以我可以在我的自定义视图中使用这个动画师吗?我去看看
  • 我已经更新了代码以更好地解释需要做什么。需要对您当前的算法进行一些修改。例如,您可以删除“delta”的概念
  • addUpdateListenerValueAnimator 的一个方法。
【解决方案2】:

如果您同时运行其他 AsyncTask,您可以尝试使用 task.executeOnExecutor 在单独的线程上启动 AsyncTask 以与它们分离。但绘图仍会在主线程上完成。

我建议寻找其他可能阻塞 UI 线程或导致垃圾收集器启动的大内存分配的东西。在您的 logcat 中查找 GC_FOR_ALLOC freed 190K, 13% free 5409K/6164K, paused 67ms, total 71ms

另外,drawText 是相对昂贵的操作。您可以缓存字体绘制的结果并从位图中绘制。

但如果地图阻塞了 UI 线程,那么如果不直接使用 OpenGL,您将无能为力。

【讨论】:

  • 异步任务以executeOnExecutor 启动,视图显示在 Android Maps V2 的地图控件上。尤其是在地图加载图块时会出现模板。
  • 我又添加了一件你可以尝试的东西 :)
【解决方案3】:

如何将这些图像放在应用程序的可绘制文件夹中。然后,您可以让一个动画文件夹保存一个 XML 文件,该文件定义图像从 0 到 9 的过渡。将此动画设置为 imageView。最后,当您确实从网络服务返回一个数字时,将 imageView 的关联图像重置为与该数字对应的图像。

动画 XML(number_slider.xml)

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:drawable="@drawable/num_0"
        android:duration="150"/>
    <item
        android:drawable="@drawable/num_1"
        android:duration="150"/>
    <item
        android:drawable="@drawable/num_2"
        android:duration="150"/>
    <item
        android:drawable="@drawable/num_3"
        android:duration="150"/>
</animation-list> 

代码: a) 开始动画

imageView.setBackgroundResource(R.anim.number_slider);
AnimationDrawable  loadingViewAnim = (AnimationDrawable) thumbnail.getBackground();
loadingViewAnim.start();

b) 停止动画并将网络服务返回的数字设置为图像视图

loadingViewAnim.stop();
imageView.setBackgroundColor(Color.WHITE);
imageView.setImageDrawable(context.getResources().getDrawable(R.drawable.num_3));

最后可以在 number_slider.xml 文件中定义各种效果,让动画更加“动画化”。你可以引入“反弹效果”、“速度控制”,任何你能想象到的东西。此外,不需要序列化,我认为这种方法将使动画在所有设备配置中更加流畅。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多