Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Creating a Marquee Effect with Custom Drawable on Android

Tech 1

A custom drawable implementation for creating a marquee animation offers smootehr performance compared to traditional view-based approaches, primarily due to optimized color interpolation.

public class AnimatedGradientBorderDrawable extends Drawable {

    private Paint borderPaint;
    private RectF drawingBounds;
    private RectF targetRect = new RectF();
    private float cornerRadius = dpToPx(10);
    private float strokeThickness = dpToPx(10);
    private int purpleColor = Color.parseColor("#855bff");
    private int blueColor = Color.parseColor("#3cfff8");
    private int greenColor = Color.parseColor("#855bff");
    private int yellowColor = Color.parseColor("#ffd220");
    private int[] gradientColors = {purpleColor, blueColor, greenColor,
            yellowColor, purpleColor, blueColor, greenColor,
            yellowColor, purpleColor, blueColor, greenColor};
    private int currentStrokeWidth;
    private Matrix transformMatrix = new Matrix();
    private float rotationAngle = 0f;

    public AnimatedGradientBorderDrawable(int strokeWidth) {
        this.currentStrokeWidth = strokeWidth;
        initializePaint();
    }

    public float getRotation() {
        return rotationAngle;
    }

    public void setRotation(float angle) {
        rotationAngle = angle;
        invalidateSelf();
    }

    private void initializePaint(){
        borderPaint = new Paint();
        borderPaint.setStyle(Paint.Style.STROKE);
        if(currentStrokeWidth < 0){
            borderPaint.setStrokeWidth(strokeThickness);
        } else {
            borderPaint.setStrokeWidth(currentStrokeWidth);
        }
    }

    @Override
    public void draw(@NonNull Canvas canvas) {
        transformMatrix.reset();
        transformMatrix.setRotate(rotationAngle, drawingBounds.centerX(), drawingBounds.centerY());
        ((SweepGradient)borderPaint.getShader()).setLocalMatrix(transformMatrix);
        canvas.drawRect(targetRect, borderPaint);
    }

    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        super.setBounds(left, top, right, bottom);
        drawingBounds = new RectF(left, top, right, bottom);
        targetRect.left = 0;
        targetRect.top = 0;
        targetRect.right = drawingBounds.width();
        targetRect.bottom = drawingBounds.height();
        SweepGradient gradient = new SweepGradient(drawingBounds.centerX(), drawingBounds.centerY(), gradientColors, null);
        borderPaint.setShader(gradient);
    }

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

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {
        borderPaint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    private Property<AnimatedGradientBorderDrawable, Float> rotationProperty =
            new Property<AnimatedGradientBorderDrawable, Float>(Float.class, "rotation") {
                @Override
                public Float get(AnimatedGradientBorderDrawable drawable) {
                    return drawable.getRotation();
                }

                @Override
                public void set(AnimatedGradientBorderDrawable drawable, Float value) {
                    drawable.setRotation(value);
                }
            };

    private ObjectAnimator animation;

    public void startAnimation() {
        animation = ObjectAnimator.ofFloat(this, rotationProperty, 0f, 360f);
        animation.setDuration(10000L);
        animation.setInterpolator(new LinearInterpolator());
        animation.setRepeatCount(ValueAnimator.INFINITE);
        animation.start();
    }

    public void stopAnimation() {
        if (animation != null && animation.isRunning()) {
            animation.cancel();
        }
    }

    private float dpToPx(float dp) {
        return dp * Resources.getSystem().getDisplayMetrics().density;
    }
}

Usage example:

private void applyBackground(RelativeLayout container) {
    AnimatedGradientBorderDrawable drawable = new AnimatedGradientBorderDrawable(paddingWidth);
    drawable.setBounds(0, 0, screenWidth, screenHeight - statusBarHeight);
    container.setBackground(drawable);
    drawable.startAnimation();
}

The paddingWidth parameter controls the thickness of the animated border. Utilize utility functions for screan dimensions and status bar height.

public static int getStatusBarHeight(Context context) {
    int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
        return context.getResources().getDimensionPixelSize(resourceId);
    }
    return 0;
}

This approach leverages canvas rotation and shader transformations to achieve smooth visual transitions.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.