1

I was looking for how implement a color picker and I found this
wonderful answers of @Raghunandan that nicely works.

The only problem is:

I can't centralize the view on dialog, it always been on top|left on dialog. I can't even set a padding. Anyone knows how I can fix this?

enter image description here

And if possible how can I change the dialog theme to Holo (Black), my entire app use this theme.

[EDITED]

The code is:

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(32);

        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 static final int CENTER_X = 100;
    private static final int CENTER_Y = 100;
    private static final int CENTER_RADIUS = 32;

    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 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.sqrt(x*x + y*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 a Color");
 }
}

and to exib the dialog:

button.setOnClickListener(new OnClickListener(){
    public void OnClick(View v){
          //param - context / listener / inicialColor
          new ColorPickerDialog(MainActivity.this, this, Color.RED).show();
    }  
});
Community
  • 1
  • 1
EmilyR
  • 179
  • 1
  • 11
  • Could you please post your code? Or look at this? http://stackoverflow.com/questions/7365635/how-to-center-layout-inside-of-android-dialog – MaTePe Sep 27 '16 at 14:43
  • When you set your view as content of the dialog it fills all the available space. If you want your view to be centered you should modify your onDraw() logic to draw in the center of the given space (not starting at 0;0). – Veselin Todorov Sep 27 '16 at 15:17
  • @veselin-todorov How? Can you please post a example, I'm new on this. Thanks – EmilyR Sep 27 '16 at 19:48

1 Answers1

1

OPTION 1:

In your custom view you can override onSizeChanged like this:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    width = w;
    height = h;

    // Move your calculation logic here.
    super.onSizeChanged(w, h, oldw, oldh);
}

This way you know the size of your view when it's set as the content of the dialog. Knowing this in your onDraw() method you can easily calculate the center of the view by using width / 2 and height / 2. Your draw calls can be modified to draw in the center:

 int centerX = widht / 2;
 int centerY = height / 2;
 int offset = *Your required view size here* / 2;
 canvas.drawOval(new RectF(centerX - offset, 0, centerX + offset, height), mPaint);
 canvas.drawCircle(centerX, centerY, CENTER_RADIUS, mCenterPaint);

This way the view will draw centered horizontally and will fill all the height. If you need you can also center it vertically the same way as with the left and right coordinates of the rect. Also it's a good practice to avoid allocating objects in the onDraw() method because depending on the view it may be called a lot of times (during animations etc.). You can calculate your RectF as soon as you know the size of the view and that is in onSizeChanged().

OPTION 2:

You can probably avoid modifying your custom view by just setting correct layout parameters when adding it to the dialog. Dialog uses ViewGroup.LayoutParams if i remember correctly. They don't have a gravity field. So In your dialogs onCreate you can just wrap your view in a LinearLayout or any other that supports gravity and set center gravity to your view:

LinearLayout layout = new LinearLayout(getContext());

YourView view = new ColorPickerView(getContext(), l, mInitialColor);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(*Width of you view or wrap_content*,*height of your view or wrap_content*);
params.gravity = Gravity.CENTER_HORIZONTAL;
view.setLayoutParams(params);

layout.addView(view)
setContentView(layout);
Veselin Todorov
  • 301
  • 3
  • 9