Home » Java » java – Augment Base Color Class on Android-Exceptionshub

java – Augment Base Color Class on Android-Exceptionshub

Posted by: admin February 25, 2020 Leave a comment

Questions:

I have a lot of code written for later Android APIs (26+) which supports things like Color.valueOf() and numeric color channels. I want to back-port some of that code to work with older API devices without having to change every instance of my Color class use to be something else.

Is there a way in Java to modify a class without having to create a derived class with the new functionality?

Basically, I want to augment the lesser-API Color class and code in the missing functions I use.

In my thinking, I see it conceptually like this:

augment class Color() {

    // Add in missing functions
    public static int valueOf(float r, float g, float b) {
    }

    public static int valueOf(int r, int g, int b) {
    }

    // Other functions here
}

It would compile the default Color class adding in those additional functions so they’d be available from that point forward in that app in the lesser-API device.

How to&Answers:

Is there a way in Java to modify a class without having to create a derived class with the new functionality?

There is nothing built into Java for this, sorry.

Note that the methods in question (valueOf()) are static methods. Your current code calls them like this:

Color.valueOf(foo, bar, goo);

It would not take much to write a script that replaces those calls with:

RickColorCompat.valueOf(foo, bar, goo);

Or, try Edit > Find > Replace in Path in Android Studio. Personally, I have a command-line gsub Ruby script that I wrote a while back for this sort of search-and-replace work.

Depending on what else you are using from Color, though, that approach may not work.

Answer:

I thought I would post my solution in case anybody needs the assistance.

My goal was to take an existing API 26 app back to API 9 so it would run on the Android 2.x series. Several Color member functions were missing related to processing integer colors and floating point color channels.

To bring the old API Color class up to date, I created a Colorx class and replaced all references of Color with Colorx. Here’s the source code. By making this change, the rest of my design (using integers and floating point color channels) was able to continue unchanged, with only the refactoring from Color to Colorx being required.

Hope this helps somebody. And if you have any suggestions for other changes / enhancements / bug fixes, please let me know. Thank you.

package ...;

import android.graphics.Color;

public class Colorx extends Color
{
    // Class variables
    public int m_red;
    public int m_grn;
    public int m_blu;
    public int m_alp;

    // Constant colors (taken from Color)
    public static final int BLACK       = 0xFF000000;
    public static final int DKGRAY      = 0xFF444444;
    public static final int GRAY        = 0xFF888888;
    public static final int LTGRAY      = 0xFFCCCCCC;
    public static final int WHITE       = 0xFFFFFFFF;
    public static final int RED         = 0xFFFF0000;
    public static final int GREEN       = 0xFF00FF00;
    public static final int BLUE        = 0xFF0000FF;
    public static final int YELLOW      = 0xFFFFFF00;
    public static final int CYAN        = 0xFF00FFFF;
    public static final int MAGENTA     = 0xFFFF00FF;
    public static final int TRANSPARENT = 0;

    // Constructors
    public Colorx()
    {
        super();

        // Initially set the alpha to 100%
        m_alp = 255;
    }

    public Colorx(float r, float g, float b)
    {
        String rrggbb;

        // Convert to integers
        m_red = (int)range(255.0f * r, 0.0f, 255.0f);
        m_grn = (int)range(255.0f * g, 0.0f, 255.0f);
        m_blu = (int)range(255.0f * b, 0.0f, 255.0f);

        // Parse the indicated color
        rrggbb = "#ff" + hexString2(m_red) + hexString2(m_grn) + hexString2(m_blu);
        parseColor(rrggbb);
    }

    public Colorx(float r, float g, float b, float a)
    {
        String aarrggbb;

        // Convert to integers
        m_red = (int)range(255.0f * r, 0.0f, 255.0f);
        m_grn = (int)range(255.0f * g, 0.0f, 255.0f);
        m_blu = (int)range(255.0f * b, 0.0f, 255.0f);
        m_alp = (int)range(255.0f * a, 0.0f, 255.0f);

        // Parse the indicated color
        aarrggbb = "#" + hexString2(m_alp) + hexString2(m_red) + hexString2(m_grn) + hexString2(m_blu);
        parseColor(aarrggbb);
    }

    public Colorx(int color)
    {
        String aarrggbb;

        // Convert to integers
        m_red = (int)range((color >> 16) & 0xff, 0, 255);
        m_grn = (int)range((color >>  8) & 0xff, 0, 255);
        m_blu = (int)range((color >>  0) & 0xff, 0, 255);
        m_alp = (int)range((color >> 24) & 0xff, 0, 255);

        // Parse the indicated color
        aarrggbb = "#" + hexString2(m_alp) + hexString2(m_red) + hexString2(m_grn) + hexString2(m_blu);
        parseColor(aarrggbb);
    }

    public float red()
    {
        return (float)(m_red / 255.0f);
    }

    public float green()
    {
        return (float)(m_grn / 255.0f);
    }

    public float blue()
    {
        return (float)(m_blu / 255.0f);
    }

    public float alp()
    {
        return (float)(m_alp / 255.0f);
    }

    public int toArgb()
    {
        int color =   ((m_alp & 0xff) << 24)
                    | ((m_red & 0xff) << 16)
                    | ((m_grn & 0xff) <<  8)
                    |  (m_blu & 0xff);

        return color;
    }

    public static Colorx valueOf(float r, float g, float b)
    {
        return new Colorx(r, g, b, 1.0f);
    }

    public static Colorx valueOf(float r, float g, float b, float a)
    {
        return new Colorx(r, g, b, a);
    }

    public static Colorx valueOf(int color)
    {
        return new Colorx(color);
    }

    public float range(float v, float vMin, float vMax)
    {
        return Math.max(vMin, Math.min(vMax, v));
    }

    public static String hexString2(int v)
    {
        String text;

        // Convert
        text = Integer.toHexString(v);

        // Evaluate
             if (text.length() == 2)  return text;        // It's the proper length
        else if (text.length() == 1)  return "0" + text;  // It's only one digits
        else                          return "00";        // It's an invalid length, force it to 00
    }
}