AChep / AcDisplay

AcDisplay is a new way of handling notifications in Android.
acdisplay.org
GNU General Public License v2.0
750 stars 226 forks source link

PlayPauseDrawable offcentered #73

Closed PaulWoitaschek closed 9 years ago

PaulWoitaschek commented 9 years ago

How can I customize the PlayPauseDrawable class? Its great, but its a bit weird bacause the play aligns left with the pause, so The play is a bit off-centered. How can the centering be archieved?

A nice thing would be to directly use the material svg paths like here: https://raw.githubusercontent.com/google/material-design-icons/master/av/svg/production/ic_play_arrow_24px.svg

AChep commented 9 years ago

The Play button is defined by this contant:

    /**
     * Play icon
     */
    private static final float[][] VERTEX_PLAY = {
            {1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f}, // x coordinates
            {0.5f, 0.5f, 0.0f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f, 0.5f}  // y coordinates
    };

, where all of those points will be multiplied by drawable's size. Note, that VERTEX_PAUSE, VERTEX_PLAY and VERTEX_STOP must have the same number of points.

PaulWoitaschek commented 9 years ago

Is it possible to convert a svg into such a path?

AChep commented 9 years ago

@Ph1b yes, I think so. At least, the easiest way would be to open this icon in a graphic editor and get its points manually. For example, look at this square: image and the code:

    private static final float[][] VERTEX_EXAMPLE = {
            {0f, 1f, 1f, 0f}, // x coordinates
            {0f, 0f, 1f, 1f}  // y coordinates
    };
PaulWoitaschek commented 9 years ago

Damn, I dont understand the coordinates. I guess the order matters?

I found this: http://www.professorcloud.com/svg-to-canvas/

Which gave me this output which I guess has the coordinates. But how can I match them to the matrix?

var draw = function(ctx) {
ctx.save();
ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(24,0);
ctx.lineTo(24,24);
ctx.lineTo(0,24);
ctx.closePath();
ctx.clip();
ctx.translate(0,0);
ctx.translate(0,0);
ctx.scale(1,1);
ctx.translate(0,0);
ctx.strokeStyle = 'rgba(0,0,0,0)';
ctx.lineCap = 'butt';
ctx.lineJoin = 'miter';
ctx.miterLimit = 4;
ctx.save();
ctx.beginPath();
ctx.moveTo(8,5);
ctx.lineTo(8,19);
ctx.lineTo(19,12);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.restore();
ctx.restore();
};
AChep commented 9 years ago

@Ph1b right, the order matters. Maybe this code from TransformationDrawable will help you:

    protected TransformationDrawable(@NonNull float[][]... vertex) {
        mVertex = vertex;
    }

    ...

    public void setTransformation(float progress) {
        mProgress = progress;
        Rect rect = getBounds();

        final float size = Math.min(Math.min(
                rect.right - rect.left,
                rect.bottom - rect.top), mSize);
        final float left = rect.left + (rect.right - rect.left - size) / 2;
        final float top = rect.top + (rect.bottom - rect.top - size) / 2;

        mPath.reset();
        // Set initial point
        mPath.moveTo(
                left + calcTransformation(0, 0, progress, size),
                top + calcTransformation(1, 0, progress, size));
        // Add all other points
        for (int i = 1; i < mVertex[0][0].length; i++) {
            mPath.lineTo(
                    left + calcTransformation(0, i, progress, size),
                    top + calcTransformation(1, i, progress, size));
        }
        // Close the path (if needed)
        mPath.close();
        invalidateSelf();
    }

    private float calcTransformation(int type, int i, float progress, float size) {
        float v0 = mVertex[mFromShape][type][i] * (1f - progress);
        float v1 = mVertex[mToShape][type][i] * progress;
        return (v0 + v1) * size;
    }

Your code is pretty close to required format. I don't recommend simple copy-pasting, cause the animation will probably look ugly. You should think about the transformations. :+1:

PaulWoitaschek commented 9 years ago

Ah okay. Since play is a rectangle, it just has 3 points, because of:

ctx.moveTo(8,5);
ctx.lineTo(8,19);
ctx.lineTo(19,12);

it would be

private static final float[][] PLAY_MODI = {
        {1f/8f, 1f/8f, 1f/19f}, // x coordinates
        {1f/5f, 1f/ 19f, 1f/12f}  // y coordinates
};

But thats much smaller than your play button.

Why is your play button not simply:

private static final float[][] VERTEX_PLAY = {
        {0f, 0f, 1f}, // x coordinates
        {0f, 1f, 0f}  // y coordinates
};
AChep commented 9 years ago

@Ph1b

    Note, that VERTEX_PAUSE, VERTEX_PLAY and VERTEX_STOP must have the same number of points.

, because each point is transforming into a corresponding point of another shape (for example, VERTEX_PLAY -> VERTEX_PAUSE)

PaulWoitaschek commented 9 years ago

Yeah now I got it, works flawless.

In case someone wants this with material icons: (directly fetched from source 1:1)

/**
 * Pause icon
 */
private static final float[][] VERTEX_PAUSE = {
        {10f / 24f, 6f / 24f, 6f / 24f, 10f / 24f, 10f / 24f, 14f / 24f, 14f / 24f, 18f / 24f, 18f / 24f},
        {5f / 24f, 5f / 24f, 19f / 24f, 19f / 24f, 5f / 24f, 5f / 24f, 19f / 24f, 19f / 24f, 5f / 24f}
};

/**
 * Play icon
 */
private static final float[][] VERTEX_PLAY = {
        {19f / 24f, 19f / 24f, 8f / 24f, 8f / 24f, 19f / 24f, 19f / 24f, 8f / 24f, 8f / 24f, 19f / 24f},
        {12f / 24f, 12f / 24f, 5f / 24f, 9f / 24f, 12f / 24f, 12f / 24f, 5f / 24f, 19f / 24f, 12f / 24f}
};

Thanks again!

AChep commented 9 years ago

@Ph1b I think that more correct points are:

    /**
     * Pause icon
     */
    private static final float[][] VERTEX_PAUSE;

    /**
     * Play icon
     */
    private static final float[][] VERTEX_PLAY;

    /**
     * Stop icon
     */
    private static final float[][] VERTEX_STOP;

    static {
        float lef0 = 1f / 14f;
        float lef1 = 5f / 14f;
        float rig0 = 9f / 14f;
        float rig1 = 13f / 14f;

        VERTEX_PAUSE = new float[][] {
                {lef1, lef0, lef0, lef1, lef1, rig0, rig0, rig1, rig1},
                {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f}
        };

        VERTEX_STOP = new float[][] {
                {0.5f, lef0, lef0, 0.5f, 0.5f, 0.5f, 0.5f, rig1, rig1},
                {lef0, lef0, rig1, rig1, lef0, lef0, rig1, rig1, lef0}
        };

        lef0 = 4f / 14f;

        VERTEX_PLAY = new float[][] {
                {1.0f, 1.0f, lef0, lef0, 1.0f, 1.0f, lef0, lef0, 1.0f},
                {0.5f, 0.5f, 0.0f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f, 0.5f}
        };
    }

, but the result is not sharp enough, due to float coordinates and I won't include it in master.

PaulWoitaschek commented 9 years ago

You can use:

/**
 * Pause icon
 */
private static final float[][] VERTEX_PAUSE = {
        {10f, 6f, 6f, 10f, 10f, 14f, 14f, 18f, 18f},
        {5f, 5f, 19f, 19f, 5f, 5f, 19f, 19f, 5f}
};

/**
 * Play icon
 */
private static final float[][] VERTEX_PLAY = {
        {19f, 19f, 8f, 8f, 19f, 19f, 8f, 8f, 19f},
        {12f, 12f, 5f, 9f, 12f, 12f, 5f, 19f, 12f}
};

And then:

private float calcTransformation(int type, int i, float progress, float size) {
    float v0 = mVertex[mFromShape][type][i] * (1f - progress);
    float v1 = mVertex[mToShape][type][i] * progress;
    return (v0 + v1) * size / 24f;
}
PaulWoitaschek commented 9 years ago

Another question: How can I set the image immediately without animation? When I enter a screen I sometimes want to set an initial state without the hickup-animation caused by onResume()

PaulWoitaschek commented 9 years ago

Okay, got it. Passed a boolean that if true sets animator duration to 0, else to default value (300).