package de.jeisfeld.augendiagnoselib.components.colorpicker;
import android.content.Context;
import android.content.res.Resources;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TableLayout;
import android.widget.TableRow;
import de.jeisfeld.augendiagnoselib.R;
import de.jeisfeld.augendiagnoselib.components.colorpicker.ColorPickerSwatch.OnColorSelectedListener;
/**
* A color picker custom view which creates an grid of color squares. The number of squares per row (and the padding
* between the squares) is determined by the user.
*/
public class ColorPickerPalette extends TableLayout {
/**
* The listener called after a color has been selected.
*/
private OnColorSelectedListener mOnColorSelectedListener;
/**
* The description of the non-selected swatches.
*/
private String mDescription;
/**
* The description of the selected swatches.
*/
private String mDescriptionSelected;
/**
* The size of the swatches.
*/
private int mSwatchLength;
/**
* The size of the margin around the swatches.
*/
private int mMarginSize;
/**
* The number of columns.
*/
private int mNumColumns;
/**
* Constructor passing attributes.
*
* @param context The context.
* @param attrs The attributes.
*/
public ColorPickerPalette(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
/**
* Standard constructor.
*
* @param context The context.
*/
public ColorPickerPalette(final Context context) {
super(context);
}
/**
* Initialize the size, columns, and listener. Size should be a pre-defined size (SIZE_LARGE or SIZE_SMALL) from
* ColorPickerDialogFragment.
*
* @param size The size of the palette (SIZE_LARGE or SIZE_SMALL)
* @param columns The number of columns
* @param listener The listener to be called when a color is selected.
*/
public final void init(final int size, final int columns, final OnColorSelectedListener listener) {
mNumColumns = columns;
Resources res = getResources();
if (size == ColorPickerDialog.SIZE_LARGE) {
mSwatchLength = res.getDimensionPixelSize(R.dimen.color_swatch_large);
mMarginSize = res.getDimensionPixelSize(R.dimen.color_swatch_margins_large);
}
else {
mSwatchLength = res.getDimensionPixelSize(R.dimen.color_swatch_small);
mMarginSize = res.getDimensionPixelSize(R.dimen.color_swatch_margins_small);
}
mOnColorSelectedListener = listener;
mDescription = res.getString(R.string.color_swatch_description);
mDescriptionSelected = res.getString(R.string.color_swatch_description_selected);
}
/**
* Helper method to create a row of the table of colors.
*
* @return The row.
*/
@NonNull
private TableRow createTableRow() {
TableRow row = new TableRow(getContext());
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
row.setLayoutParams(params);
return row;
}
/**
* Adds swatches to table in a serpentine format.
*
* @param colors The colors to be added.
* @param selectedColor The preselected color.
*/
public final void drawPalette(@Nullable final int[] colors, final int selectedColor) {
if (colors == null) {
return;
}
this.removeAllViews();
int tableElements = 0;
int rowElements = 0;
int rowNumber = 0;
// Fills the table with swatches based on the array of colors.
TableRow row = createTableRow();
for (int color : colors) {
tableElements++;
View colorSwatch = createColorSwatch(color, selectedColor);
setSwatchDescription(rowNumber, tableElements, rowElements, color == selectedColor,
colorSwatch);
addSwatchToRow(row, colorSwatch);
rowElements++;
if (rowElements == mNumColumns) {
addView(row);
row = createTableRow();
rowElements = 0;
rowNumber++;
}
}
// Create blank views to fill the row if the last row has not been filled.
if (rowElements > 0) {
while (rowElements != mNumColumns) {
addSwatchToRow(row, createBlankSpace());
rowElements++;
}
addView(row);
}
}
/**
* Appends a swatch to the end of the row.
*
* @param row The row.
* @param swatch The swatch to be added.
*/
private static void addSwatchToRow(@NonNull final TableRow row, final View swatch) {
row.addView(swatch);
}
/**
* Add a content description to the specified swatch view. Because the colors get added in a snaking form, every
* other row will need to compensate for the fact that the colors are added in an opposite direction from their
* left to right/top to bottom order, which is how the system will arrange them for accessibility purposes.
*
* @param rowNumber The row number.
* @param index The index of the view.
* @param rowElements The number of elements in the row.
* @param selected Flag indicating if the swatch is selected.
* @param swatch The swatch to be affected.
*/
private void setSwatchDescription(final int rowNumber, final int index, final int rowElements,
final boolean selected,
@NonNull final View swatch) {
int accessibilityIndex;
if (rowNumber % 2 == 0) {
// We're in a regular-ordered row
accessibilityIndex = index;
}
else {
// We're in a backwards-ordered row.
int rowMax = (rowNumber + 1) * mNumColumns;
accessibilityIndex = rowMax - rowElements;
}
String description;
if (selected) {
description = String.format(mDescriptionSelected, accessibilityIndex);
}
else {
description = String.format(mDescription, accessibilityIndex);
}
swatch.setContentDescription(description);
}
/**
* Creates a blank space to fill the row.
*
* @return the view with the blank space.
*/
@NonNull
private ImageView createBlankSpace() {
ImageView view = new ImageView(getContext());
TableRow.LayoutParams params = new TableRow.LayoutParams(mSwatchLength, mSwatchLength);
params.setMargins(mMarginSize, mMarginSize, mMarginSize, mMarginSize);
view.setLayoutParams(params);
return view;
}
/**
* Creates a color swatch.
*
* @param color The color of the swatch.
* @param selectedColor The selected color.
* @return the created color swatch.
*/
@NonNull
private ColorPickerSwatch createColorSwatch(final int color, final int selectedColor) {
ColorPickerSwatch view = new ColorPickerSwatch(getContext(), color,
color == selectedColor, mOnColorSelectedListener);
TableRow.LayoutParams params = new TableRow.LayoutParams(mSwatchLength, mSwatchLength);
params.setMargins(mMarginSize, mMarginSize, mMarginSize, mMarginSize);
view.setLayoutParams(params);
return view;
}
}