0

I have an ImageView and I am trying to fade from one image to the next using this code:

Drawable bgs[] = new Drawable[2];
public void redraw(int[][] grid) {
    bgs[0] = bgs[1];
    bgs[1] = new GameDrawable(grid, prefs.colors);
    if (bgs[0] == null) {
        gameField.setImageDrawable(bgs[1]);
    } else {
        TransitionDrawable crossfader = new TransitionDrawable(bgs);
        crossfader.setCrossFadeEnabled(true);
        gameField.setImageDrawable(crossfader);
        crossfader.startTransition(500);
    }
}

gameField is correctly referenced as an ImageView.

gameDrawable simply extends Drawable and draws the grid.

On each move and action the new GameDrawable is being rendered correctly but there is no fading whatsoever. The new image is simply displayed instantaneously. I have tried lengthening the transition time and swapping the order of the drawables with no effect.

Any help on is appreciated.

Update: I have now set my transition to something ridiculously long like 500000. The first drawable shows for a few seconds and then suddenly the second drawable appears. So still no transition.

Update 2: I think my Drawable might be implemented incorrectly, so I have attached the code.

public class GameDrawable extends Drawable {

    private Paint paint = new Paint();
    private float blockWidth = 1;
    private int[][] myGrid;
    private int myColor;
    private List<Point> myPoints;

    public GameDrawable(int[][] grid) {
        super();
        this.myGrid = grid;
        this.myColor = colors[yourColor];
        paint.setStrokeWidth(1);
        paint.setStyle(Paint.Style.FILL);
        paint.setAlpha(0);
        this.myPoints = yourPoints;
    }

    @Override
    public void draw(Canvas canvas) {
        float height = getBounds().height();
        float width = getBounds().width();
        blockWidth = width / myGrid.length;
        if (height / myGrid.length < blockWidth) {
            blockWidth = height / myGrid.length;
        }

        for (int x = 0; x < myGrid.length; x++) {
            for (int y = 0; y < myGrid[x].length; y++) {
                paint.setColor(colors[myGrid[x][y]]);
                canvas.drawRect(x * blockWidth, y * blockWidth, (x+1)*blockWidth, (y+1)*blockWidth, paint);
            }
        }
    }

    @Override
    public void setAlpha(int alpha) {
        paint.setAlpha(alpha);
        invalidateSelf();
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
        paint.setColorFilter(cf);
        invalidateSelf();
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }
}
Michael Frey
  • 908
  • 2
  • 14
  • 35
  • How often is _redraw_ being called? I.e is it possible it gets called so soon again it would override existing crossfader from running till its end? – harism Aug 08 '15 at 19:45
  • It gets called only when the user presses a button. i.e. Only once, and then not again until another button is pressed. – Michael Frey Aug 08 '15 at 19:46
  • I've just tried this with two simple shape drawables and everything works perfectly. Presumably the key lies in your custom `Drawable` implementation. For example, I'm presuming you have `setAlpha()` implemented in a meaningful way? – ci_ Aug 14 '15 at 19:54
  • @ci_ I think that might be my issue. I have just added my custom drawable code to the question. – Michael Frey Aug 14 '15 at 19:58
  • I have a custom Drawable and Ireturn PixelFormat.TRANSLUCENT in getOpacity() and TransitionDrawable works fine. Not sure if this will help but you could try. – David Heisnam Aug 14 '15 at 20:12
  • Check the docs for getOpacity() http://developer.android.com/reference/android/graphics/drawable/Drawable.html#getOpacity() it's one of a number of constant and _not_ the alpha value. – ci_ Aug 14 '15 at 20:15

1 Answers1

4

Looking at your code, I see a problem at the line

bgs[0] = bgs[1];

bgs[1] has not yet been defined before this line and so bgs[0] is null for the first method call. Because of this, (bgs[0] == null) is true, and so the later defined bgs[1] is directly set to the gameField ImageView.

Use corrected code below.

Drawable bgs[] = new Drawable[2];
Drawable firstDrawable = getResources().getDrawable(R.drawable.transparent);

public void redraw(int[][] grid) {
    bgs[0] = firstDrawable;
    bgs[1] = new GameDrawable(grid, prefs.colors);
    firstDrawable = bgs[1];
    TransitionDrawable crossfader = new TransitionDrawable(bgs);
    crossfader.setCrossFadeEnabled(true);
    gameField.setImageDrawable(crossfader);
    crossfader.startTransition(500);
}

Note that TransitionDrawable does not work properly when the Drawable sizes are different. So you may need to resize firstDrawable beforehand.

EXTRA: I would avoid setCrossFadeEnabled(true) since the whole TransitionDrawable becomes translucent during the transition, revealing the background. Sometimes, this creates a "blinking" effect and destroys the smoothness of the transition.

EDIT: Looking at your custom Drawable implementation, I think the problem lies in the line

canvas.drawColor(Color.WHITE);

in the draw() method.

I looked at TransitionDrawable.java source and found that setAlpha is called on the drawables to get the cross fade effect. However, your canvas has a solid white color and setAlpha() only affects the paint. Hope this is your answer.

EDIT 2: The actual problem, as pointed out by Michael, was that TransitionDrawable's setAlpha() calls on the Drawables were rendered ineffective due to paint.setColor() in the GameDrawable's draw() method overriding the paint's alpha value set by the TransitionDrawable.

David Heisnam
  • 2,463
  • 1
  • 21
  • 32
  • That is why I have the check for null and only set the background on the first draw. Each following draw uses the transition. Your code is certainly cleaner. Still refuses to fade though. I think my overwritten drawable might be implemented incorrectly. – Michael Frey Aug 14 '15 at 19:55
  • Hmm It could be your Drawable. I think sharing the code for it might help. – David Heisnam Aug 14 '15 at 20:03
  • I added it to the original question – Michael Frey Aug 14 '15 at 20:16
  • @MichaelFrey I added more to my answer. Please check and let me know if it helped. – David Heisnam Aug 14 '15 at 20:27
  • Do you have a suggestion for the background. Should i simply not render it or can i apply the alpha to the background? – Michael Frey Aug 14 '15 at 20:49
  • @MichaelFrey If you really need the background, set it to the ImageView itself, either in xml or code. – David Heisnam Aug 14 '15 at 20:51
  • @MichaelFrey At this point, I can only make a few suggestions. paint.setStrokeWidth() and Paint.Style.FILL are incompatible. Also, if you are going to be using the drawable only in a TransitionDrawable, remove invalidateSelf() from setAlpha() because there is twice the number of draws during the Transition, because the drawable's draw(), along with setAlpha() is called from the TransitionDrawable multiple times to achieve the fade effect. – David Heisnam Aug 15 '15 at 06:50
  • I finally found out what's wrong. Thank you for pointing me in the right direction. I set the color of my paint using setColor(). My color contains an alpha value already, so any alpha value set by transitiondrawable is being overwritten by paint.setColor(). If you update your answer I'll award you the bounty :-) – Michael Frey Aug 15 '15 at 07:19
  • I literally came to tell you the same thing! Wow, amazing how we came upon the exact problem at the same time. – David Heisnam Aug 15 '15 at 07:29
  • @MichaelFrey I've edited the answer. I wish it could be a cleaner. Anyway, thanks for the bounty! – David Heisnam Aug 15 '15 at 07:39