diff --git a/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/AHSVColor.java b/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/AHSVColor.java index 1b073b8..4a59f45 100644 --- a/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/AHSVColor.java +++ b/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/AHSVColor.java @@ -49,6 +49,23 @@ class AHSVColor { hsv[0] = oldHue; } + /** Set red (0), green (1) or blue (2) color component. */ + void setRGBComponent(int component, int value) { + int c = getARGB(); + switch (component) { + case 0: + c = (c & 0xff00ffff) | (value << 16); + break; + case 1: + c = (c & 0xffff00ff) | (value << 8); + break; + case 2: + c = (c & 0xffffff00) | value; + break; + } + setARGB(c); + } + /** Get hue,sat,val values. */ float[] getHSV() { return new float[]{hsv[0], hsv[1], hsv[2]}; @@ -63,4 +80,15 @@ class AHSVColor { int getARGB() { return Color.HSVToColor(alpha, hsv); } + + /** Get red (0), green (1), or blue (2) color component. */ + int getRGBComponent(int component) { + int c = getARGB(); + switch (component) { + case 0: return Color.red(c); + case 1: return Color.green(c); + case 2: return Color.blue(c); + default: throw new RuntimeException("Internal error"); + } + } } diff --git a/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/ColorPickerDialog.java b/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/ColorPickerDialog.java index fe74111..5864c68 100644 --- a/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/ColorPickerDialog.java +++ b/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/ColorPickerDialog.java @@ -18,10 +18,13 @@ package net.margaritov.preference.colorpicker; import org.petero.droidfish.R; +import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.graphics.PixelFormat; import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; import android.widget.LinearLayout; public class ColorPickerDialog @@ -32,7 +35,7 @@ public class ColorPickerDialog View.OnClickListener { private ColorPickerView mColorPicker; - + private EditText colorCode; private ColorPickerPanelView mOldColor; private ColorPickerPanelView mNewColor; @@ -52,9 +55,7 @@ public class ColorPickerDialog } private void init(int color) { - // To fight color banding. getWindow().setFormat(PixelFormat.RGBA_8888); - setUp(color, color); } @@ -71,26 +72,49 @@ public class ColorPickerDialog + additionalInfo + "'"); mColorPicker = findViewById(R.id.color_picker_view); + colorCode = findViewById(R.id.color_code); mOldColor = findViewById(R.id.old_color_panel); mNewColor = findViewById(R.id.new_color_panel); - ((LinearLayout) mOldColor.getParent()).setPadding( - Math.round(mColorPicker.getDrawingOffset()), - 0, - Math.round(mColorPicker.getDrawingOffset()), - 0 - ); - + int offs = Math.round(mColorPicker.getDrawingOffset()); + ((LinearLayout) mOldColor.getParent()).setPadding(offs, 0, offs, 0); + mOldColor.setOnClickListener(this); mNewColor.setOnClickListener(this); mColorPicker.setOnColorChangedListener(this); mOldColor.setColor(oldColor); + + colorCode.setOnFocusChangeListener((view, hasFocus) -> { + if (!hasFocus) + applyColorCode(); + }); + colorCode.setOnEditorActionListener((v, id, event) -> { + colorCode.clearFocus(); + String ims = Activity.INPUT_METHOD_SERVICE; + InputMethodManager imm = (InputMethodManager)getContext().getSystemService(ims); + imm.hideSoftInputFromWindow(v.getWindowToken(), 0); + return true; + }); + mColorPicker.setColor(newColor, true); } @Override public void onColorChanged(int color) { mNewColor.setColor(color); + colorCode.setText(String.format("%08x", color)); + } + + private void applyColorCode() { + String txt = colorCode.getText().toString().trim().toLowerCase(); + if (txt.length() != 8) // Format must be AARRGGBB + return; + try { + long longVal = Long.parseLong(txt, 16); + int val = (int)longVal; + if (val != mColorPicker.getColor()) + mColorPicker.setColor(val, true); + } catch (NumberFormatException ignore) {} } /** diff --git a/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/ColorPickerPanelView.java b/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/ColorPickerPanelView.java index 1a48a25..418a8fa 100644 --- a/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/ColorPickerPanelView.java +++ b/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/ColorPickerPanelView.java @@ -30,19 +30,18 @@ import android.view.View; * @author Daniel Nilsson */ public class ColorPickerPanelView extends View { - /** The width in pixels of the border surrounding the color panel. */ - private final static float BORDER_WIDTH_PX = 1; + private final static float BORDER_WIDTH_PX = 1; private float mDensity = 1f; - private int mColor = 0xff000000; + private int mColor = 0xff000000; - private Paint mBorderPaint = new Paint(); - private Paint mColorPaint = new Paint(); + private Paint mBorderPaint = new Paint(); + private Paint mColorPaint = new Paint(); - private RectF mDrawingRect; - private RectF mColorRect; + private RectF mDrawingRect; + private RectF mColorRect; private AlphaPatternDrawable mAlphaPattern; @@ -80,37 +79,45 @@ public class ColorPickerPanelView extends View { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); - + width = chooseSize(widthMode, width, Math.round(120f * mDensity)); + height = chooseSize(heightMode, height, 0); setMeasuredDimension(width, height); } + int chooseSize(int mode, int size, int preferred) { + if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) + return size; + return preferred; // MeasureSpec.UNSPECIFIED + } + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mDrawingRect = new RectF(); - mDrawingRect.left = getPaddingLeft(); + mDrawingRect.left = getPaddingLeft(); mDrawingRect.right = w - getPaddingRight(); - mDrawingRect.top = getPaddingTop(); + mDrawingRect.top = getPaddingTop(); mDrawingRect.bottom = h - getPaddingBottom(); setUpColorRect(); } private void setUpColorRect() { - final RectF dRect = mDrawingRect; + final RectF dRect = mDrawingRect; - float left = dRect.left + BORDER_WIDTH_PX; - float top = dRect.top + BORDER_WIDTH_PX; + float left = dRect.left + BORDER_WIDTH_PX; + float top = dRect.top + BORDER_WIDTH_PX; float bottom = dRect.bottom - BORDER_WIDTH_PX; - float right = dRect.right - BORDER_WIDTH_PX; + float right = dRect.right - BORDER_WIDTH_PX; mColorRect = new RectF(left,top, right, bottom); mAlphaPattern = new AlphaPatternDrawable((int)(5 * mDensity)); - mAlphaPattern.setBounds(Math.round(mColorRect.left), Math.round(mColorRect.top), Math.round(mColorRect.right), diff --git a/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/ColorPickerView.java b/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/ColorPickerView.java index f52cd85..e887aea 100644 --- a/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/ColorPickerView.java +++ b/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/ColorPickerView.java @@ -18,6 +18,7 @@ package net.margaritov.preference.colorpicker; import android.annotation.SuppressLint; import android.content.Context; +import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Point; import android.graphics.RectF; @@ -40,6 +41,9 @@ public class ColorPickerView extends View { /** The height in dp of the alpha panel */ private float ALPHA_PANEL_HEIGHT = 20f; + /** The width or height in dp of one of the red/green/blue panels. */ + private float RGB_PANEL_SIZE = 30f; + /** The distance in dp between the different color panels. */ private float PANEL_SPACING = 10f; @@ -56,9 +60,13 @@ public class ColorPickerView extends View { /** Distance form the edges of the view of where we are allowed to draw. */ private RectF mDrawingRect; + /** Side of the satValPanel square. */ + private float satValSide; + private GradientPanel satValPanel; private GradientPanel huePanel; private GradientPanel alphaPanel; + private GradientPanel[] rgbPanel = new GradientPanel[3]; private Point mStartTouchPoint = null; @@ -83,6 +91,7 @@ public class ColorPickerView extends View { mDensity = getContext().getResources().getDisplayMetrics().density; HUE_PANEL_WIDTH *= mDensity; ALPHA_PANEL_HEIGHT *= mDensity; + RGB_PANEL_SIZE *= mDensity; PANEL_SPACING *= mDensity; mDrawingOffset = Math.max(5, BORDER_WIDTH_PX) * mDensity * 1.5f; @@ -93,6 +102,11 @@ public class ColorPickerView extends View { setFocusableInTouchMode(true); } + /** Return true if the current orientation is landscape. */ + private boolean landScapeView() { + return getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + } + @Override protected void onDraw(Canvas canvas) { if (mDrawingRect.width() <= 0 || mDrawingRect.height() <= 0) @@ -104,6 +118,9 @@ public class ColorPickerView extends View { huePanel.draw(canvas); if (alphaPanel != null) alphaPanel.draw(canvas); + for (int i = 0; i < 3; i++) + if (rgbPanel[i] != null) + rgbPanel[i].draw(canvas); } @Override @@ -137,7 +154,8 @@ public class ColorPickerView extends View { if (mStartTouchPoint == null) return false; - for (GradientPanel pnl : new GradientPanel[]{satValPanel, huePanel, alphaPanel}) { + for (GradientPanel pnl : new GradientPanel[]{satValPanel, huePanel, alphaPanel, + rgbPanel[0], rgbPanel[1], rgbPanel[2]}) { if (pnl != null && pnl.contains(mStartTouchPoint)) { Point curPnt = new Point((int)event.getX(), (int)event.getY()); @@ -153,51 +171,56 @@ public class ColorPickerView extends View { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int widthAllowed = MeasureSpec.getSize(widthMeasureSpec); int heightAllowed = MeasureSpec.getSize(heightMeasureSpec); - - widthAllowed = chooseWidth(widthMode, widthAllowed); - heightAllowed = chooseHeight(heightMode, heightAllowed); - int width = (int) (heightAllowed - ALPHA_PANEL_HEIGHT + HUE_PANEL_WIDTH); - int height; - if (width > widthAllowed) { - width = widthAllowed; - height = (int) (widthAllowed - HUE_PANEL_WIDTH + ALPHA_PANEL_HEIGHT); - } else { - height = heightAllowed; - } + widthAllowed = chooseSize(widthMode, widthAllowed, getPreferredWidth()); + heightAllowed = chooseSize(heightMode, heightAllowed, getPreferredHeight()); - setMeasuredDimension(width, height); - } + float side = getSatValSide(widthAllowed, heightAllowed); + float width = side + getExtraWidth(); + float height = side + getExtraHeight(); - private int chooseWidth(int mode, int size) { - if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) { - return size; - } else { // (mode == MeasureSpec.UNSPECIFIED) - return getPreferredWidth(); - } - } - - private int chooseHeight(int mode, int size) { - if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) { - return size; - } else { // (mode == MeasureSpec.UNSPECIFIED) - return getPreferredHeight(); - } + int newWidth = widthMode == MeasureSpec.EXACTLY ? widthAllowed : (int)width; + int newHeight = heightMode == MeasureSpec.EXACTLY ? heightAllowed : (int)height; + setMeasuredDimension(newWidth, newHeight); } private int getPreferredWidth() { - int width = getPreferredHeight(); - width -= PANEL_SPACING + ALPHA_PANEL_HEIGHT; - return (int) (width + HUE_PANEL_WIDTH + PANEL_SPACING); + return (int)(200 * mDensity + getExtraWidth()); } private int getPreferredHeight() { - int height = (int)(200 * mDensity); - height += PANEL_SPACING + ALPHA_PANEL_HEIGHT; - return height; + return (int)(200 * mDensity + getExtraHeight()); + } + + private int chooseSize(int mode, int size, int preferred) { + if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) + return size; + return preferred; // MeasureSpec.UNSPECIFIED + } + + /** Compute side of satValPanel given total available width/height. */ + private float getSatValSide(float width, float height) { + float side1 = width - getExtraWidth(); + float side2 = height - getExtraHeight(); + return Math.min(side1, side2); + } + + /** Amount of space to the right of the satVal panel. */ + private float getExtraWidth() { + float ret = PANEL_SPACING + HUE_PANEL_WIDTH; + if (landScapeView()) + ret += 3 * (PANEL_SPACING + RGB_PANEL_SIZE); + return ret; + } + + /** Amount of space below the satVal panel. */ + private float getExtraHeight() { + float ret = PANEL_SPACING + ALPHA_PANEL_HEIGHT; + if (!landScapeView()) + ret += 3 * (PANEL_SPACING + RGB_PANEL_SIZE); + return ret; } @Override @@ -210,20 +233,23 @@ public class ColorPickerView extends View { mDrawingRect.top = mDrawingOffset + getPaddingTop(); mDrawingRect.bottom = h - mDrawingOffset - getPaddingBottom(); + satValSide = getSatValSide(mDrawingRect.width(), + mDrawingRect.height()); + setUpSatValPanel(); setUpHuePanel(); setUpAlphaPanel(); + setUpRGBPanels(); } private void setUpSatValPanel() { RectF dRect = mDrawingRect; - float panelSide = dRect.height() - BORDER_WIDTH_PX * 2; - panelSide -= PANEL_SPACING + ALPHA_PANEL_HEIGHT; + float b = BORDER_WIDTH_PX; - float left = dRect.left + BORDER_WIDTH_PX; - float right = left + panelSide; - float top = dRect.top + BORDER_WIDTH_PX; - float bottom = top + panelSide; + float left = dRect.left + b; + float right = left + satValSide - 2 * b; + float top = dRect.top + b; + float bottom = top + satValSide - 2 * b; RectF satValRect = new RectF(left,top, right, bottom); satValPanel = new SatValGradientPanel(satValRect, color, mDensity); @@ -231,11 +257,12 @@ public class ColorPickerView extends View { private void setUpHuePanel() { RectF dRect = mDrawingRect; + float b = BORDER_WIDTH_PX; - float left = dRect.right - HUE_PANEL_WIDTH + BORDER_WIDTH_PX; - float right = dRect.right - BORDER_WIDTH_PX; - float top = dRect.top + BORDER_WIDTH_PX; - float bottom = dRect.bottom - BORDER_WIDTH_PX - (PANEL_SPACING + ALPHA_PANEL_HEIGHT); + float left = dRect.left + satValSide + PANEL_SPACING + b; + float right = left + HUE_PANEL_WIDTH - 2 * b; + float top = dRect.top + b; + float bottom = top + satValSide - 2 * b; RectF hueRect = new RectF(left, top, right, bottom); huePanel = new HueGradientPanel(hueRect, color, mDensity); @@ -243,16 +270,45 @@ public class ColorPickerView extends View { private void setUpAlphaPanel() { RectF dRect = mDrawingRect; + float b = BORDER_WIDTH_PX; - float left = dRect.left + BORDER_WIDTH_PX; - float right = dRect.right - BORDER_WIDTH_PX; - float top = dRect.bottom - ALPHA_PANEL_HEIGHT + BORDER_WIDTH_PX; - float bottom = dRect.bottom - BORDER_WIDTH_PX; + float left = dRect.left + b; + float right = dRect.right - b; + float top = dRect.top + satValSide + PANEL_SPACING + b; + float bottom = top + ALPHA_PANEL_HEIGHT - 2 * b; RectF alphaRect = new RectF(left, top, right, bottom); alphaPanel = new AlphaGradientPanel(alphaRect, color, mDensity); } + private void setUpRGBPanels() { + RectF dRect = mDrawingRect; + float b = BORDER_WIDTH_PX; + float w = RGB_PANEL_SIZE; + float s = PANEL_SPACING; + if (!landScapeView()) { + float offs = dRect.top + satValSide + s + ALPHA_PANEL_HEIGHT; + for (int i = 0; i < 3; i++) { + float left = dRect.left + b; + float right = dRect.right - b; + float top = offs + i * (s + w) + s + b; + float bottom = top + w - 2 * b; + RectF rgbRect = new RectF(left, top, right, bottom); + rgbPanel[i] = new RGBGradientPanel(i, rgbRect, color, mDensity, true); + } + } else { + float offs = dRect.left + satValSide + s + HUE_PANEL_WIDTH; + for (int i = 0; i < 3; i++) { + float left = offs + i * (s + w) + s + b; + float right = left + w - 2 * b; + float top = dRect.top + b; + float bottom = top + satValSide - 2 * b; + RectF rgbRect = new RectF(left, top, right, bottom); + rgbPanel[i] = new RGBGradientPanel(i, rgbRect, color, mDensity, false); + } + } + } + /** * Set a OnColorChangedListener to get notified when the color * selected by the user has changed. diff --git a/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/RGBGradientPanel.java b/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/RGBGradientPanel.java new file mode 100644 index 0000000..a1f16d1 --- /dev/null +++ b/DroidFishApp/src/main/java/net/margaritov/preference/colorpicker/RGBGradientPanel.java @@ -0,0 +1,80 @@ +package net.margaritov.preference.colorpicker; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.LinearGradient; +import android.graphics.Point; +import android.graphics.RectF; +import android.graphics.Shader; + +public class RGBGradientPanel extends GradientPanel { + private final int component; // 0=red, 1=green, 2=blue + private final int colorMask; + private final boolean horizontal; + + /** Constructor. */ + RGBGradientPanel(int component, RectF rect, AHSVColor color, float density, + boolean horizontal) { + super(rect, color, density, null); + this.component = component; + switch (component) { + case 0: colorMask = 0x00ff0000; break; + case 1: colorMask = 0x0000ff00; break; + case 2: colorMask = 0x000000ff; break; + default: colorMask = 0; break; + } + this.horizontal = horizontal; + } + + @Override + protected void setGradientPaint() { + int rgb = color.getARGB(); + int color00 = (rgb & ~colorMask) | 0xff000000; + int colorFF = (rgb | colorMask) | 0xff000000; + Shader rgbShader; + if (horizontal) { + rgbShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top, + color00, colorFF, Shader.TileMode.CLAMP); + } else { + rgbShader = new LinearGradient(rect.left, rect.bottom, rect.left, rect.top, + color00, colorFF, Shader.TileMode.CLAMP); + } + gradientPaint.setShader(rgbShader); + } + + protected void drawTracker(Canvas canvas) { + int val = color.getRGBComponent(component); + Point p = rgbComponentToPoint(val); + drawRectangleTracker(canvas, p, horizontal); + } + + @Override + void updateColor(Point point) { + int rgbVal = pointToRgbComponent(point); + color.setRGBComponent(component, rgbVal); + } + + private Point rgbComponentToPoint(int val) { + if (horizontal) { + float width = rect.width(); + return new Point((int)((val * width / 0xff) + rect.left), + (int)rect.top); + } else { + float height = rect.height(); + return new Point((int)rect.left, + (int)(rect.bottom - (val * height / 0xff))); + } + } + + private int pointToRgbComponent(Point p) { + if (horizontal) { + int width = (int)rect.width(); + int x = Math.min(Math.max(p.x - (int)rect.left, 0), width); + return x * 0xff / width; + } else { + int height = (int)rect.height(); + int y = Math.min(Math.max((int)rect.bottom - p.y, 0), height); + return y * 0xff / height; + } + } +} diff --git a/DroidFishApp/src/main/res/layout-land/dialog_color_picker.xml b/DroidFishApp/src/main/res/layout-land/dialog_color_picker.xml index 8548229..50f18b6 100644 --- a/DroidFishApp/src/main/res/layout-land/dialog_color_picker.xml +++ b/DroidFishApp/src/main/res/layout-land/dialog_color_picker.xml @@ -30,8 +30,19 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" + android:layout_weight="1.0" android:layout_marginBottom="10dp"> + + + android:layout_height="40dp" /> + android:layout_height="40dp" /> diff --git a/DroidFishApp/src/main/res/layout/dialog_color_picker.xml b/DroidFishApp/src/main/res/layout/dialog_color_picker.xml index bb63b6b..7944fac 100644 --- a/DroidFishApp/src/main/res/layout/dialog_color_picker.xml +++ b/DroidFishApp/src/main/res/layout/dialog_color_picker.xml @@ -24,9 +24,18 @@ android:id="@+id/color_picker_view" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_centerHorizontal="true" android:tag="portrait" /> + +