【问题标题】:Android - Fade out bitmap image on canvasAndroid - 在画布上淡出位图图像
【发布时间】:2013-08-16 05:59:17
【问题描述】:

我正在画布上绘制一个缩放位图,并希望在指定时间淡出我的图像。

基本上,当我的角色图像越过画布的某个部分时,我需要角色图像慢慢消失(3 秒),然后页面会自动重定向到下一个 java 类。

目前,我的图像只是重定向到新的 java 类,请参阅下面的一些代码,了解我是如何创建图像的。

Resources res = getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, res.getDisplayMetrics());
imgSpacing = (int) px / 2;
int size = (int) ((PhoneWidth / 5) - px);

chrImg = BitmapFactory.decodeResource(getResources(), R.drawable.character);
chrImg = Bitmap.createScaledBitmap(chrImg, size, size, true);

然后在画布上onDraw:

if(indexX == mazeFinishX && indexY == mazeFinishY)
{
    canvas.drawBitmap(finish, j * totalCellWidth, i * totalCellHeight, null);
    // As soon as the character moves over this square they are automatically re-directed to new page
    // This is where I want to fade the character image out before the re-direct
}

我在网上查看过,但无法完全弄清楚如何让褪色功能适用于从我的游戏资源可绘制文件夹中获取的可绘制图像。谢谢

【问题讨论】:

    标签: android image bitmap android-canvas fadeout


    【解决方案1】:
            if(indexX == mazeFinishX && indexY == mazeFinishY)
            {
                canvas.drawBitmap(finish, j * totalCellWidth, i * totalCellHeight, null);
                // As soon as the character moves over this square they are automtically re-directs to new page
                new CountDownTimer(0500, 1000) {
                    @Override
                    public void onTick(long millisUntilFinished) {}
                    @Override
                    public void onFinish() {
    Paint paint = new Paint();
    paint.setAlpha(25);
    canvas.drawBitmap(chrImg, 0, 0, paint);
                    }
                }.start();
                new CountDownTimer(0500, 1000) {
                    @Override
                    public void onTick(long millisUntilFinished) {}
                    @Override
                    public void onFinish() {
    Paint paint = new Paint();
    paint.setAlpha(45);
    canvas.drawBitmap(chrImg, 0, 0, paint);             }
                }.start();
                new CountDownTimer(0500, 1000) {
                    @Override
                    public void onTick(long millisUntilFinished) {}
                    @Override
                    public void onFinish() {
    Paint paint = new Paint();
    paint.setAlpha(70);
    canvas.drawBitmap(chrImg, 0, 0, paint);             }
                }.start();
                new CountDownTimer(0500, 1000) {
                    @Override
                    public void onTick(long millisUntilFinished) {}
                    @Override
                    public void onFinish() {
    Paint paint = new Paint();
    paint.setAlpha(100);
    canvas.drawBitmap(chrImg, 0, 0, paint);             }
                }.start();
                // This is where I want to fade the character image out before the re-direct
                new CountDownTimer(3000, 1000) {
    
                @Override
                public void onTick(long millisUntilFinished) {
                    // TODO Auto-generated method stub
    
                }
    
                @Override
                public void onFinish() {
                    // TODO Auto-generated method stub
    
                        Intent i = new Intent(currentclass.this, nextClass.class);
                        startActivity(i);
                }
            }.start();
    
        }
    

    这将使您的位图变得透明(alpha 将为 100).. 然后在 3 秒后重定向到下一个类。

    更新: 对于位图,您不能使用 .setAlpha,所以我们必须使用绘画,所以我们每次都创建一个新绘画,然后使用绘画设置位图。但是如果你有一个 imageView 你可以使用 .setalpha

    【讨论】:

    • 这可以延迟重定向,但是我想让图像慢慢消失。本质上,我有一个虫洞图像,并希望角色图像通过某种动画消失在其中(我正在考虑让图像消失)。
    • 刚试过你的代码,但是意识到它是来自可绘制资源的位图,你不能使用 setAlpha。还有其他方法吗,请看我的原始代码来复制图像。
    【解决方案2】:

    如果您认为将来可能会想要更改淡入淡出动画,例如缩放和/或旋转,那么您应该使用动画 XML。

    但为了快速位图淡出,您可以重复发布延迟失效消息。您可能希望将无效区域限制在字符位图所在的位置:

    private static final int FADE_MILLISECONDS = 3000; // 3 second fade effect
    private static final int FADE_STEP = 120;          // 120ms refresh
    
    // Calculate our alpha step from our fade parameters
    private static final int ALPHA_STEP = 255 / (FADE_MILLISECONDS / FADE_STEP);
    
    // Initializes the alpha to 255
    private Paint alphaPaint = new Paint();
    
    // Need to keep track of the current alpha value
    private int currentAlpha = 255;
    
    @Override
    protected void onDraw(Canvas canvas) {
        ...
        if(indexX == mazeFinishX && indexY == mazeFinishY) {
    
            // Drawing your wormhole?
            int x = j * totalCellWidth;
            int y = i * totalCellHeight;
            canvas.drawBitmap(finish, x, y, null);
    
            if (currentAlpha > 0) {
    
               // Draw your character at the current alpha value
               canvas.drawBitmap(chrImg, x, y, alphaPaint);
    
               // Update your alpha by a step
               alphaPaint.setAlpha(currentAlpha);
               currentAlpha -= ALPHA_STEP;
    
               // Assuming you hold on to the size from your createScaledBitmap call
               postInvalidateDelayed(FADE_STEP, x, y, x + size, y + size);
    
            } else {
               // No character draw, just reset your alpha paint
               currentAlpha = 255;
               alphaPaint.setAlpha(currentAlpha);
    
               // Now do your redirect
            }
        }
        ...
    }
    

    我建议将常量 FADE_MILLISECONDS 和 FADE_STEP 放入 res/integers.xml,这样它们就不会被硬编码。

    【讨论】:

    • 我已经实现了你上面的代码,并且褪色工作正常。然而,这里的问题是我的重定向不起作用。它使用重定向代码命中函数,但似乎忽略了它,有什么想法吗?
    • 我添加了一个“currentAlpha”字段,使用 setAlpha/getAlpha 可能除了 1 的 alpha 步长之外没有任何效果。
    【解决方案3】:

    我解释这一点的最佳方式是提供一个完整的自定义视图示例,您可以从中提取所需的部分。下面是一个实现这一点的视图。

    public class CharacterView extends View {
    
        private Paint mCharacterPaint;
        private Bitmap mCharacterBitmap;
        private Transformation mTransformation;
        private AlphaAnimation mFadeOut;
    
        public CharacterView(Context context) {
            super(context);
            init(context);
        }
    
        public CharacterView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        public CharacterView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context);
        }
    
        private void init(Context context) {
            //This would be your character image instead
            mCharacterBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
    
            //We need a paint to efficiently modify the alpha
            mCharacterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            //This is needed to house the current value during the animation
            mTransformation = new Transformation();
            //Construct an animation that will do all the timing math for you
            mFadeOut = new AlphaAnimation(1f, 0f);
            mFadeOut.setDuration(500);
            //Use a listener to trigger the end action
            mFadeOut.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) { }
    
                @Override
                public void onAnimationEnd(Animation animation) {
                    //Trigger your action to change screens here.
                }
    
                @Override
                public void onAnimationRepeat(Animation animation) { }
            });
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                //In this example, touching the view triggers the animation
                // your code would run this sequence when the character reaches the
                // appropriate coordinates (P.S. I would not advocate putting this code
                // inside of onDraw()
                mFadeOut.start();
                mFadeOut.getTransformation(System.currentTimeMillis(), mTransformation);
                invalidate();
            }
            return true;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            //...existing drawing code...
    
            canvas.drawBitmap(mCharacterBitmap, 0, 0, mCharacterPaint);
            if (mFadeOut.hasStarted() && !mFadeOut.hasEnded()) {
                mFadeOut.getTransformation(System.currentTimeMillis(), mTransformation);
                //Keep drawing until we are done
                mCharacterPaint.setAlpha((int)(255 * mTransformation.getAlpha()));
                invalidate();
            } else {
                //Reset the alpha if animation is canceled
                mCharacterPaint.setAlpha(255);
            }
        }
    }
    

    动态修改所绘制内容透明度的最有效方法是使用Paint 绘图将其应用到Canvas。您需要在每个帧转换中不断地invalidate() 视图以显示动画,直到角色消失。该示例使用Animation 对象和Transformation 对象来处理所有计时数学,以使动画实际上看起来不错。

    这里的要点是您通过一些外部触发器启动动画(我不建议检查何时在 onDraw() 中淡出,在更新这些角色位置的地方可能更好。在我的示例中,为简单起见,我在触摸视图时开始此操作。

    此触发器启动动画并获取初始变换值,然后invalidate() 触发新的onDraw()。在动画运行时,onDraw() 会因invalidate() 而重复,并且每次迭代时,油漆上的 alpha 都会略微降低。

    动画结束后,它会为你调用AnimationListener,这样你就可以在onAnimationEnd()里面触发屏幕转换了。

    【讨论】:

    • 你应该只使角色被重绘的区域无效。
    • 如果整体视图很大,这可能是一个很好的优化。尽管在许多常见情况下(例如启用硬件加速),脏矩形实际上被忽略了;因此,除非性能保证,否则通常不值得付出努力。
    • 一位 Android 开发人员的回答似乎与您关于硬件加速忽略脏区的断言相矛盾:stackoverflow.com/questions/7233830/…
    • 我绝对相信 Romain 在这个主题上的话。其实他描述的Activity简历的案例就是我想到的。干杯。
    猜你喜欢
    • 2016-02-07
    • 2022-07-06
    • 2021-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多