Tek Eye Logo

Tek Eye

Android Color Picker Tutorial

User Interface (UI) design is an important part of an app. Color (or colour in British English) is an important part of a UI. A color picker can be used to customize or edit an app theme or UI. Plus they can be used for artistic apps. This tutorial provides an Android Color Picker example app, but not just showing one way. Two ways are provided in this article and lots of color pickers are available around the web.

Android Color Picker

(This Android color picker tutorial assumes that Android Studio is installed, a basic App can be created and run, and the code in this article can be correctly copied into Android Studio. The example code can be changed to meet your own requirements. When entering code in Studio add import statements when prompted by pressing Alt-Enter.)

Android Logo

How is an Android Color Defined?

What is a color in the Android Operating System (OS)? A color in Android is simply a number, a 32-bit number. 32-bits fits into a Java int. Therefore a standard color in Android is stored in an int. The 32-bits of the color int are divided into four lots of eight bits (four bytes). The four bytes are the Alpha (transparancy), Red, Green and Blue parts of a color. A color is therefore often simply referred to as an RGB or ARGB value. Each byte can hold 256 values (0-255). Therefore, the total number of possible colors are 2565x256x256=16777216. Over 16 million colours and at 256 transparency levels. To define a color just assign a value to an integer, here is an example:

//---------A-R-G-B-
int lime=0xff00ff00;
findViewById(R.id.textView).setBackgroundColor(lime);

A Simple Android Color Picker Example

Because an Android color is just a combination of four bytes a simple color picker can be made with four Seekbars dropped on to a View. Then take the value from the Seekbars and convert them into a color using the Android Color class.

Simple Android Color Picker

For an example on using the Seekbar see the Tek Eye article SeekBar Android Example Code and Tutorial. Here is the layout with four Seekbars used to set an Android color (as shown above):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="5dp"
    tools:context="com.example.colorpicker.MainActivity">
    <TextView
        android:id="@+id/labelA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="A"
        android:textAppearance="?android:attr/textAppearanceMedium" />
    <SeekBar
        android:id="@+id/seekA"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/labelA"
        android:layout_toRightOf="@id/labelA"
        android:max="255"
        android:progress="255" />
    <TextView
        android:id="@+id/labelR"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/labelA"
        android:text="R"
        android:textAppearance="?android:attr/textAppearanceMedium" />
    <SeekBar
        android:id="@+id/seekR"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/labelR"
        android:layout_below="@+id/seekA"
        android:layout_toRightOf="@+id/labelR"
        android:max="255"
        android:progress="255" />
    <TextView
        android:id="@+id/labelG"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/labelR"
        android:text="G"
        android:textAppearance="?android:attr/textAppearanceMedium" />
    <SeekBar
        android:id="@+id/seekG"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/labelG"
        android:layout_below="@+id/seekR"
        android:layout_toRightOf="@+id/labelG"
        android:max="255"
        android:progress="255" />
    <TextView
        android:id="@+id/labelB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/labelG"
        android:text="B"
        android:textAppearance="?android:attr/textAppearanceMedium" />
    <SeekBar
        android:id="@+id/seekB"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/labelB"
        android:layout_below="@+id/seekG"
        android:layout_toRightOf="@+id/labelB"
        android:max="255"
        android:progress="255" />
    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/labelB"
        android:background="@android:color/darker_gray"
        android:orientation="vertical">
        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@+id/labelB"
            android:layout_centerHorizontal="true"
            android:layout_margin="24dp"
            android:background="@android:color/white"
            android:padding="24dp"
            android:text="0xffffffff"
            android:textAlignment="center"
            android:textColor="@android:color/black"
            android:textSize="36dp" />
    </LinearLayout>
</RelativeLayout>

Here is the Java Activity source code (MainActivity.java) used for the above layout:

package com.example.colorpicker;

import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.SeekBar;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener {
    //Reference the seek bars
    SeekBar SeekA;
    SeekBar SeekR;
    SeekBar SeekG;
    SeekBar SeekB;
    //Reference the TextView
    TextView ShowColor;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //Get a reference to the seekbars
        SeekA=(SeekBar)findViewById(R.id.seekA);
        SeekR=(SeekBar)findViewById(R.id.seekR);
        SeekG=(SeekBar)findViewById(R.id.seekG);
        SeekB=(SeekBar)findViewById(R.id.seekB);
        //Reference the TextView
        ShowColor=(TextView)findViewById(R.id.textView);
        //This activity implements SeekBar OnSeekBarChangeListener
        SeekA.setOnSeekBarChangeListener(this);
        SeekR.setOnSeekBarChangeListener(this);
        SeekG.setOnSeekBarChangeListener(this);
        SeekB.setOnSeekBarChangeListener(this);
    }
    //Satisfy the implements
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
        //get current ARGB values
        int A=SeekA.getProgress();
        int R=SeekR.getProgress();
        int G=SeekG.getProgress();
        int B=SeekB.getProgress();
        //Reference the value changing
        int id=seekBar.getId();
        //Get the chnaged value
        if(id == com.example.colorpicker.R.id.seekA)
                A=progress;
        else if(id == com.example.colorpicker.R.id.seekR)
                R=progress;
        else if(id == com.example.colorpicker.R.id.seekA)
                G=progress;
        else if(id == com.example.colorpicker.R.id.seekA)
                B=progress;
        //Build and show the new color
        ShowColor.setBackgroundColor(Color.argb(A,R,G,B));
        //show the color value
        ShowColor.setText("0x"+String.format("%02x", A)+String.format("%02x", R)
                +String.format("%02x", G)+String.format("%02x", B));
        //some math so text shows (needs improvement for greys)
        ShowColor.setTextColor(Color.argb(0xff,255-R,255-G,255-B));
    }
    public void onStartTrackingTouch(SeekBar seekBar) {
        //Only required due to implements
    }
    public void onStopTrackingTouch(SeekBar seekBar) {
        //Only required due to implements
    }
}

An Android Color Picker Dialog

The Android Software Development Kit (SDK) used to ship with many demo apps. One of the most useful was the API Demos project. The API Demos covers many core Android classes. It features graphics examples and one of those has a color picker dialog.

Reused API Demos Color Picker

To implement it copy ColorPickerDialog.java from the graphics folder of the API Demos project into the same folder as the main Java file (here MainActivity.java). Change the package namespace to match the app's (here package com.example.colorpicker;). Add a Button to the top of the layout and set the first TextView to be below it:

...
<Button
    android:id="@+id/buttonA"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Picker 1"
    android:onClick="Picker1Click" />
<TextView
    android:id="@+id/labelA"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@id/buttonA"
    android:text="A"
    android:textAppearance="?android:attr/textAppearanceMedium" />
...

Add code to handle the button and invoke the ColorPickerDialog:

//Handle button
public void Picker1Click(View arg0) {
    //no direct way to get background color as it could be a drawable
    if (ShowColor.getBackground() instanceof ColorDrawable) {
        ColorDrawable cd = (ColorDrawable) ShowColor.getBackground();
        int colorCode = cd.getColor();
        //pick a color (changed in the UpdateColor listener)
        new ColorPickerDialog(MainActivity.this, new UpdateColor(), colorCode).show();
    }
}

And code to handle the color picker dialog return:

public class UpdateColor implements ColorPickerDialog.OnColorChangedListener {
    public void colorChanged(int color) {
        //ShowColor.setBackgroundColor(color);
        //show the color value
        ShowColor.setText("0x"+String.format("%08x", color));
        SeekA.setProgress(Color.alpha(color));
        SeekR.setProgress(Color.red(color));
        SeekG.setProgress(Color.green(color));
        SeekB.setProgress(Color.blue(color));
    }
}

The ColorPickerDialog is showing its age as it has hard coded values for when screens were much smaller. Here some simple scaling was added based on density pixels (dp) and the values tweeked. It'll need further changes to correctly handle different screen sizes if it goes into production code. Here is the code for the ColorPickerDialog.java used in the example:

package com.example.colorpicker;

import android.os.Bundle;
import android.app.Dialog;
import android.content.Context;
import android.graphics.*;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;

public class ColorPickerDialog extends Dialog {

    public interface OnColorChangedListener {
        void colorChanged(int color);
    }

    private OnColorChangedListener mListener;
    private int mInitialColor;

    private static class ColorPickerView extends View {
        private Paint mPaint;
        private Paint mCenterPaint;
        private final int[] mColors;
        private OnColorChangedListener mListener;

        ColorPickerView(Context c, OnColorChangedListener l, int color) {
            super(c);
            mListener = l;
            mColors = new int[] {
                0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00,
                0xFFFFFF00, 0xFFFF0000
            };
            Shader s = new SweepGradient(0, 0, mColors, null);

            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setShader(s);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeWidth(dpToPx(64));

            mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mCenterPaint.setColor(color);
            mCenterPaint.setStrokeWidth(5);
        }

        private boolean mTrackingCenter;
        private boolean mHighlightCenter;

        @Override
        protected void onDraw(Canvas canvas) {
            float r = CENTER_X - mPaint.getStrokeWidth()*0.5f;

            canvas.translate(CENTER_X, CENTER_X);

            canvas.drawOval(new RectF(-r, -r, r, r), mPaint);
            canvas.drawCircle(0, 0, CENTER_RADIUS, mCenterPaint);

            if (mTrackingCenter) {
                int c = mCenterPaint.getColor();
                mCenterPaint.setStyle(Paint.Style.STROKE);

                if (mHighlightCenter) {
                    mCenterPaint.setAlpha(0xFF);
                } else {
                    mCenterPaint.setAlpha(0x80);
                }
                canvas.drawCircle(0, 0,
                                  CENTER_RADIUS + mCenterPaint.getStrokeWidth(),
                                  mCenterPaint);

                mCenterPaint.setStyle(Paint.Style.FILL);
                mCenterPaint.setColor(c);
            }
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(CENTER_X*2, CENTER_Y*2);
        }

        private int CENTER_X = dpToPx(132);
        private int CENTER_Y = dpToPx(132);
        private int CENTER_RADIUS = dpToPx(40);

        private int floatToByte(float x) {
            int n = java.lang.Math.round(x);
            return n;
        }
        private int pinToByte(int n) {
            if (n < 0) {
                n = 0;
            } else if (n > 255) {
                n = 255;
            }
            return n;
        }
        private int dpToPx(int dp) {
            DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
            return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
        }

        private int ave(int s, int d, float p) {
            return s + java.lang.Math.round(p * (d - s));
        }

        private int interpColor(int colors[], float unit) {
            if (unit <= 0) {
                return colors[0];
            }
            if (unit >= 1) {
                return colors[colors.length - 1];
            }

            float p = unit * (colors.length - 1);
            int i = (int)p;
            p -= i;

            // now p is just the fractional part [0...1) and i is the index
            int c0 = colors[i];
            int c1 = colors[i+1];
            int a = ave(Color.alpha(c0), Color.alpha(c1), p);
            int r = ave(Color.red(c0), Color.red(c1), p);
            int g = ave(Color.green(c0), Color.green(c1), p);
            int b = ave(Color.blue(c0), Color.blue(c1), p);

            return Color.argb(a, r, g, b);
        }

        private int rotateColor(int color, float rad) {
            float deg = rad * 180 / 3.1415927f;
            int r = Color.red(color);
            int g = Color.green(color);
            int b = Color.blue(color);

            ColorMatrix cm = new ColorMatrix();
            ColorMatrix tmp = new ColorMatrix();

            cm.setRGB2YUV();
            tmp.setRotate(0, deg);
            cm.postConcat(tmp);
            tmp.setYUV2RGB();
            cm.postConcat(tmp);

            final float[] a = cm.getArray();

            int ir = floatToByte(a[0] * r +  a[1] * g +  a[2] * b);
            int ig = floatToByte(a[5] * r +  a[6] * g +  a[7] * b);
            int ib = floatToByte(a[10] * r + a[11] * g + a[12] * b);

            return Color.argb(Color.alpha(color), pinToByte(ir),
                              pinToByte(ig), pinToByte(ib));
        }

        private static final float PI = 3.1415926f;

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            float x = event.getX() - CENTER_X;
            float y = event.getY() - CENTER_Y;
            boolean inCenter = java.lang.Math.hypot(x, y) <= CENTER_RADIUS;

            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mTrackingCenter = inCenter;
                    if (inCenter) {
                        mHighlightCenter = true;
                        invalidate();
                        break;
                    }
                case MotionEvent.ACTION_MOVE:
                    if (mTrackingCenter) {
                        if (mHighlightCenter != inCenter) {
                            mHighlightCenter = inCenter;
                            invalidate();
                        }
                    } else {
                        float angle = (float)java.lang.Math.atan2(y, x);
                        // need to turn angle [-PI ... PI] into unit [0....1]
                        float unit = angle/(2*PI);
                        if (unit < 0) {
                            unit += 1;
                        }
                        mCenterPaint.setColor(interpColor(mColors, unit));
                        invalidate();
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    if (mTrackingCenter) {
                        if (inCenter) {
                            mListener.colorChanged(mCenterPaint.getColor());
                        }
                        mTrackingCenter = false;    // so we draw w/o halo
                        invalidate();
                    }
                    break;
            }
            return true;
        }
    }

    public ColorPickerDialog(Context context,
                             OnColorChangedListener listener,
                             int initialColor) {
        super(context);

        mListener = listener;
        mInitialColor = initialColor;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        OnColorChangedListener l = new OnColorChangedListener() {
            public void colorChanged(int color) {
                mListener.colorChanged(color);
                dismiss();
            }
        };

        setContentView(new ColorPickerView(getContext(), l, mInitialColor));
        setTitle("Pick Color, Click the Dot");
    }
}

Lots of Color Pickers

There are lots of free color pickers available for Android. Many with more features than the ones shown here. Browse for an Android color picker on GitHub to find one suitable for your project, if the simple ones shown here are not up to the job.

See Also

Author:  Published:  

ShareSubmit to TwitterSubmit to FacebookSubmit to Google+Submit to LinkedInSubmit to redditPrint Page

markdown CMS Small Logo Icon ↓markdown↓ CMS is fast and simple. Build websites quickly and publish easily. For beginner to expert.



Articles on:

Android, HTML, VPS, Computing, IT, Computer History, ↓markdown↓ CMS



Free Android Projects and Samples:

Android Examples, Android List Examples, Android UI Examples