Last active
November 13, 2017 13:25
-
-
Save MatheusAmelco/6fdb826ea226c92ee8a9e6e7ef968e54 to your computer and use it in GitHub Desktop.
Custom number picker for Android
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<resources> | |
<declare-styleable name="NumberPicker"> | |
<attr name="numberpicker_value" format="integer" /> | |
<attr name="numberpicker_maxValue" format="integer" /> | |
<attr name="numberpicker_minValue" format="integer" /> | |
<attr name="numberpicker_textColor" format="color" /> | |
<attr name="numberpicker_textSize" format="dimension" /> | |
</declare-styleable> | |
</resources> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<ripple xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:color="#640606" | |
tools:ignore="NewApi"> | |
<item> | |
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | |
<item> | |
<shape xmlns:android="http://schemas.android.com/apk/res/android" | |
android:shape="oval"> | |
<solid android:color="@color/corVermelho" /> | |
<size | |
android:width="40dp" | |
android:height="40dp" /> | |
</shape> | |
</item> | |
<item | |
android:left="13dp" | |
android:right="13dp"> | |
<shape android:shape="line"> | |
<stroke | |
android:width="1.5dp" | |
android:color="@color/corBranco" /> | |
</shape> | |
</item> | |
</layer-list> | |
</item> | |
</ripple> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<ripple xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:color="#055908" | |
tools:ignore="NewApi"> | |
<item> | |
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | |
<item> | |
<shape xmlns:android="http://schemas.android.com/apk/res/android" | |
android:shape="oval"> | |
<solid android:color="@color/corDestaque" /> | |
<size | |
android:width="40dp" | |
android:height="40dp" /> | |
</shape> | |
</item> | |
<item | |
android:left="13dp" | |
android:right="13dp"> | |
<shape android:shape="line"> | |
<stroke | |
android:width="1.5dp" | |
android:color="@color/corBranco" /> | |
</shape> | |
</item> | |
<item | |
android:left="13dp" | |
android:right="13dp"> | |
<rotate android:fromDegrees="90"> | |
<shape android:shape="line"> | |
<stroke | |
android:width="1.5dp" | |
android:color="@color/corBranco" /> | |
</shape> | |
</rotate> | |
</item> | |
</layer-list> | |
</item> | |
</ripple> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<resources> | |
<!-- Cores Principais --> | |
<color name="corDestaque">#4CAF50</color> | |
<color name="corTextoPrincipal">#212121</color> | |
<!-- Outras Cores --> | |
<color name="corBranco">#FFFFFF</color> | |
<color name="corVermelho">#D32F2F</color> | |
</resources> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<merge xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content"> | |
<LinearLayout | |
android:id="@+id/number_picker_layout" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:clipToPadding="false" | |
android:orientation="horizontal" | |
android:padding="8dp"> | |
<Button | |
android:id="@+id/number_picker_menos" | |
android:layout_width="40dp" | |
android:layout_height="40dp" | |
android:background="@drawable/btn_circle_negative" | |
android:elevation="2dp" | |
tools:ignore="UnusedAttribute" /> | |
<TextView | |
android:id="@+id/number_picker_texto" | |
android:layout_width="40dp" | |
android:layout_height="40dp" | |
android:layout_gravity="center" | |
android:focusable="false" | |
android:gravity="center" | |
android:inputType="none" | |
android:textAlignment="center" | |
android:textColor="@color/corTextoSecundario" | |
android:textSize="16sp" /> | |
<Button | |
android:id="@+id/number_picker_mais" | |
android:layout_width="40dp" | |
android:layout_height="40dp" | |
android:background="@drawable/btn_circle_positive" | |
android:elevation="2dp" | |
tools:ignore="UnusedAttribute" /> | |
</LinearLayout> | |
</merge> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.matheusamelco.android.components; | |
import android.content.Context; | |
import android.content.res.TypedArray; | |
import android.graphics.Rect; | |
import android.util.AttributeSet; | |
import android.view.MotionEvent; | |
import android.view.View; | |
import android.widget.Button; | |
import android.widget.CompoundButton; | |
import android.widget.LinearLayout; | |
import android.widget.TextView; | |
import com.matheusamelco.android.R; | |
/** | |
* Componente personalizado de Stepper. Normalmente utilizado para seleção de quantidades, | |
* permite a definição de valores inteiros, negativos e positivos, com limite mínimo e máximo, | |
* e passo personalizáveis. | |
* | |
* @author Matheus Felipe Amelco | |
* @since 18/10/17 | |
*/ | |
public class NumberPicker extends LinearLayout { | |
// Componentes | |
Button menos; | |
TextView texto; | |
Button mais; | |
// Propriedades | |
int valor; | |
int valorMaximo; | |
int valorMinimo; | |
OnValorChanged listener; | |
/** | |
* Construtor. | |
* | |
* @param context Context - Contexto da atividade. | |
*/ | |
public NumberPicker(Context context) { | |
super(context); | |
init(context, null); | |
} | |
/** | |
* Construtor. | |
* | |
* @param context Context - Contexto da atividade. | |
* @param attrs AttributeSet - Atributos utilizados pelo LayoutInflater. | |
*/ | |
public NumberPicker(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
init(context, attrs); | |
} | |
/** | |
* Construtor. | |
* | |
* @param context Context - Contexto da atividade. | |
* @param attrs AttributeSet - Atributos utilizados pelo LayoutInflater. | |
* @param defStyle int - Estilo do componente, utilizado pelo LayoutInflater. | |
*/ | |
public NumberPicker(Context context, AttributeSet attrs, int defStyle) { | |
super(context, attrs, defStyle); | |
init(context, attrs); | |
} | |
/** | |
* Inicializa o componente com suas ações padrões. | |
* | |
* @param context Context - Contexto da atividade. | |
*/ | |
private void init(Context context, AttributeSet attrs) { | |
View.inflate(context, R.layout.number_stepper, this); | |
setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS); | |
// Pega os componentes definidos no XML. | |
menos = this.findViewById(R.id.number_picker_menos); | |
texto = this.findViewById(R.id.number_picker_texto); | |
mais = this.findViewById(R.id.number_picker_mais); | |
// HitBox | |
final Rect hitRectMenos = new Rect(menos.getLeft(), menos.getTop(), menos.getRight(), menos.getBottom()); | |
final Rect hitRectMais = new Rect(mais.getLeft(), mais.getTop(), mais.getRight(), mais.getBottom()); | |
// Define o efeito visual de toque nos componentes. | |
setOnTouchListener(new OnTouchListener() { | |
@Override | |
public boolean onTouch(View view, MotionEvent motionEvent) { | |
view.getHitRect(hitRectMenos); | |
view.getHitRect(hitRectMais); | |
// Botão Menos | |
if (hitRectMenos.contains((int) motionEvent.getX(), (int) motionEvent.getY())) { | |
motionEvent.setLocation(0.0f, 0.0f); | |
menos.dispatchTouchEvent(motionEvent); | |
view.performClick(); | |
} | |
// Botão Mais | |
if (hitRectMais.contains((int) motionEvent.getX(), (int) motionEvent.getY())) { | |
motionEvent.setLocation(0.0f, 0.0f); | |
mais.dispatchTouchEvent(motionEvent); | |
view.performClick(); | |
} | |
return true; | |
} | |
}); | |
// Define a ação dos botões no clique. | |
setOnClickMenosListener(new OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
int oldVal = getValor(); | |
int newVal = oldVal - 1; | |
setValor(newVal); | |
if (listener != null) { | |
listener.onValorChanged(oldVal, getValor()); | |
} | |
} | |
}); | |
setOnClickMaisListener(new OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
int oldVal = getValor(); | |
int newVal = oldVal + 1; | |
setValor(newVal); | |
if (listener != null) { | |
listener.onValorChanged(oldVal, getValor()); | |
} | |
} | |
}); | |
// Define os atributos personalizados definidos no XML! | |
if (attrs != null) { | |
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.NumberPicker, 0, 0); | |
int value = 0; | |
int maxValue = 100; | |
int minValue = 0; | |
try { | |
value = a.getInteger(R.styleable.NumberStepper_value, 0); | |
maxValue = a.getInteger(R.styleable.NumberStepper_maxValue, 0); | |
minValue = a.getInteger(R.styleable.NumberStepper_minValue, 0); | |
} catch (Exception e) { | |
Logger.error(NumberPicker.class, e); | |
} finally { | |
a.recycle(); | |
} | |
setValor(value); | |
setValorMaximo(maxValue); | |
setValorMinimo(minValue); | |
} | |
} | |
// SET | |
/** | |
* Define o valor exibido no componente. | |
* Seguirá as regras de valor máximo e mínimo padrões, ou as regras que foram | |
* definidas manualmente. | |
* <p> | |
* <b>Regras padrões:</b> | |
* Valor Mínimo = 0 | |
* Valor Máximo = 100 | |
* | |
* @param valor int - Valor a ser definido ao componente. | |
*/ | |
public void setValor(int valor) { | |
if (valor < 0 || valor < valorMinimo) { | |
this.valor = valorMinimo; | |
} else if (valor > valorMaximo) { | |
this.valor = valorMaximo; | |
} else { | |
this.valor = valor; | |
} | |
texto.setText(String.valueOf(this.valor)); | |
} | |
public void setValorMaximo(int valorMaximo) { | |
this.valorMaximo = valorMaximo; | |
} | |
public void setValorMinimo(int valorMinimo) { | |
this.valorMinimo = valorMinimo; | |
} | |
public void setonValorChangedListener(OnValorChanged listener) { | |
this.listener = listener; | |
} | |
/** | |
* Define o listener que será executado quando botão "Menos" for pressionado. | |
* ATENÇÃO: A utilização deste método sobrescreve a ação padrão do componente, | |
* portanto será necessário realizar os tratamentos manualmente! | |
* | |
* @param listener OnClickListener - Listener que será usado no botão. | |
*/ | |
public void setOnClickMenosListener(CompoundButton.OnClickListener listener) { | |
menos.setOnClickListener(listener); | |
} | |
/** | |
* Define o listener que será executado quando botão "Mais" for pressionado. | |
* ATENÇÃO: A utilização deste método sobrescreve a ação padrão do componente, | |
* portanto será necessário realizar os tratamentos manualmente! | |
* | |
* @param listener OnClickListener - Listener que será usado no botão. | |
*/ | |
public void setOnClickMaisListener(CompoundButton.OnClickListener listener) { | |
mais.setOnClickListener(listener); | |
} | |
// GET | |
/** | |
* Retorna o valor atual do componente. | |
* | |
* @return int - Valor do componente. | |
*/ | |
public int getValor() { | |
return this.valor; | |
} | |
/** | |
* Interface para callback do momento em que o valor é alterado. | |
*/ | |
public interface OnValorChanged { | |
void onValorChanged(int valorAntigo, int valorNovo); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment