Skip to content

Instantly share code, notes, and snippets.

@gnumilanix
Created August 20, 2017 02:29
Show Gist options
  • Save gnumilanix/dca24d53c1517ba07a529dbbf677b888 to your computer and use it in GitHub Desktop.
Save gnumilanix/dca24d53c1517ba07a529dbbf677b888 to your computer and use it in GitHub Desktop.
View that allows incrementing or decrementing values
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="StepperView">
<attr name="incrementIcon" format="reference" />
<attr name="decrementIcon" format="reference" />
<attr name="stepperMin" format="integer" />
<attr name="stepperMax" format="integer" />
<attr name="stepperMargin" format="dimension" />
</declare-styleable>
</resources>
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.milanix.view.R;
import java.util.List;
/**
* View that allows incrementing or decrementing values
*
* @author milan
*/
public class StepperView extends LinearLayout {
private List<Integer> values;
private Drawable incrementDrawable;
private Drawable decrementDrawable;
private ImageView decrementView;
private ImageView incrementView;
private OnStepChangeListener onStepChangeListener;
private OnValueChangeListener onValueChangeListener;
private int stepperMargin;
private int index = 0;
private int min;
private int max;
public StepperView(Context context) {
this(context, null);
}
public StepperView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, -1);
}
public StepperView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setAttributes(context, attrs);
init();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public StepperView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setAttributes(context, attrs);
init();
}
private void setAttributes(Context context, AttributeSet attrs) {
if (attrs != null) {
final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.StepperView);
incrementDrawable = array.getDrawable(R.styleable.StepperView_incrementIcon);
decrementDrawable = array.getDrawable(R.styleable.StepperView_decrementIcon);
min = array.getInt(R.styleable.StepperView_stepperMin, 0);
max = array.getInt(R.styleable.StepperView_stepperMax, 0);
stepperMargin = (int) array.getDimension(R.styleable.StepperView_stepperMargin, 0);
array.recycle();
}
validateValues();
}
private void init() {
addDecrementView();
addIncrementView();
index = min;
}
/**
* Adds Decrement view
*/
private void addDecrementView() {
decrementView = new ImageView(getContext());
decrementView.setLayoutParams(getControlLayoutParams());
decrementView.setImageDrawable(decrementDrawable);
decrementView.setOnClickListener(view -> decreaseStep());
addView(decrementView);
}
/**
* Returns layout params for control view
*
* @return layout params
*/
@NonNull
private LayoutParams getControlLayoutParams() {
final LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT, 1);
layoutParams.bottomMargin = stepperMargin;
layoutParams.topMargin = stepperMargin;
layoutParams.leftMargin = stepperMargin;
layoutParams.rightMargin = stepperMargin;
return layoutParams;
}
/**
* Decreases step
*/
private void decreaseStep() {
if (index > min) {
index--;
notifyValueChange();
notifyDecrement();
}
}
/**
* Notifies about the decrement
*/
private void notifyDecrement() {
if (null != onStepChangeListener) {
onStepChangeListener.onDecrement(StepperView.this, getSelectedValue());
}
}
/**
* Adds Increment view
*/
private void addIncrementView() {
incrementView = new ImageView(getContext());
incrementView.setImageDrawable(incrementDrawable);
incrementView.setLayoutParams(getControlLayoutParams());
incrementView.setOnClickListener(view -> increaseStep());
addView(incrementView);
}
/**
* Increases step
*/
private void increaseStep() {
if (index < max) {
index++;
notifyValueChange();
notifyIncrement();
}
}
/**
* Notifies about the increment
*/
private void notifyIncrement() {
if (null != onStepChangeListener) {
onStepChangeListener.onIncrement(StepperView.this, index);
}
}
/**
* Sets a step onChange listener
*
* @param onStepChangeListener to set
*/
public void setOnStepChangeListener(OnStepChangeListener onStepChangeListener) {
this.onStepChangeListener = onStepChangeListener;
}
public void setOnValueChangeListener(OnValueChangeListener onValueChangeListener) {
this.onValueChangeListener = onValueChangeListener;
}
/**
* Sets selected step index
*
* @param index to set
*/
public void setIndex(int index) {
this.index = index;
if (index < min || index > max) {
throw new IllegalArgumentException("index must be between min and max");
}
notifyValueChange();
}
/**
* Sets min value
*
* @param min value to set
*/
public void setMin(int min) {
this.min = min;
this.index = min;
validateValues();
notifyValueChange();
}
/**
* Sets max value
*
* @param max value to set
*/
public void setMax(int max) {
this.max = max;
validateValues();
}
/**
* Sets values from given integer list. Will sort then use the min and max
*
* @param valueList to set
*/
public void setValues(List<Integer> valueList) {
this.values = valueList;
this.index = min = 0;
this.max = values.size() - 1;
validateValues();
notifyValueChange();
}
/**
* Returns selected value
*
* @return selected value
*/
public int getSelectedValue() {
if (null != values) {
return values.get(index);
} else {
return index;
}
}
/**
* Notifies about value changes
*/
private void notifyValueChange() {
decrementView.setEnabled(index > min);
incrementView.setEnabled(index < max);
if (null != onValueChangeListener) {
onValueChangeListener.onChange(this, getSelectedValue());
}
}
/**
* Validates stepper values
*/
private void validateValues() {
if (min > max) {
throw new IllegalArgumentException("min must be less than max");
}
}
/**
* Interface to be implemented for listening to changes in the stepper
*/
public interface OnStepChangeListener {
/**
* Invoked when stepper has incremented
*
* @param view current stepper view
* @param value that was incremented to
*/
void onIncrement(StepperView view, int value);
/**
* Invoked when stepper has decremented
*
* @param view current stepper view
* @param value that was decremented to
*/
void onDecrement(StepperView view, int value);
}
/**
* Interface to be implemented for listening to listen to changes in the stepper
*/
public interface OnValueChangeListener {
/**
* Invoked when stepper value has changed
*
* @param view current stepper view
* @param value that was updated to
*/
void onChange(StepperView view, int value);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment