|
import android.annotation.TargetApi; |
|
import android.content.Context; |
|
import android.content.res.TypedArray; |
|
import android.graphics.drawable.Drawable; |
|
import android.os.Build; |
|
import android.os.Bundle; |
|
import android.os.Parcelable; |
|
import android.support.v4.content.ContextCompat; |
|
import android.text.TextUtils; |
|
import android.util.AttributeSet; |
|
import android.util.Log; |
|
import android.view.MotionEvent; |
|
import android.widget.ImageView; |
|
import android.widget.LinearLayout; |
|
import android.widget.TextView; |
|
|
|
import com.squareup.picasso.Picasso; |
|
|
|
|
|
/** |
|
* Created by ekuivi on 8/2/17. |
|
*/ |
|
|
|
public class ToggleableButton extends LinearLayout{ |
|
|
|
private TextView mCountTv; |
|
private ImageView mIcon; |
|
|
|
private Drawable mInactiveIcon; |
|
private Drawable mActiveIcon; |
|
|
|
private boolean mClickToggleEnable = false; |
|
private boolean mActive = false; |
|
|
|
private OnClickToggleListener mOnClickToggleListener; |
|
|
|
public interface OnClickToggleListener { |
|
|
|
void onClicked(); |
|
void onUnclicked(); |
|
} |
|
|
|
private class ToggleableButtonAttributes { |
|
int mCount = 0; |
|
|
|
ToggleableButtonAttributes(int count) { |
|
mCount = count; |
|
} |
|
|
|
ToggleableButtonAttributes(){} |
|
} |
|
|
|
|
|
public ToggleableButton(Context context) { |
|
this(context, null); |
|
} |
|
|
|
public ToggleableButton(Context context, AttributeSet attrs) { |
|
this(context, attrs, 0); |
|
} |
|
|
|
public ToggleableButton(Context context, AttributeSet attrs, int defStyleAttr) { |
|
super(context, attrs, defStyleAttr); |
|
|
|
ToggleableButtonAttributes toggleableButtonAttributes = setupAttributes(context, attrs); |
|
init(toggleableButtonAttributes); |
|
} |
|
|
|
private ToggleableButtonAttributes setupAttributes(Context context, AttributeSet attrs) { |
|
ToggleableButtonAttributes result = new ToggleableButtonAttributes(); |
|
|
|
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ToggleableButton); |
|
|
|
try{ |
|
int count = a.getInt(R.styleable.ToggleableButton_count, 0); |
|
|
|
mActiveIcon = a.getDrawable(R.styleable.ToggleableButton_active_icon); |
|
mInactiveIcon = a.getDrawable(R.styleable.ToggleableButton_icon); |
|
mActive = a.getBoolean(R.styleable.ToggleableButton_active, false); |
|
|
|
result.mCount = count; |
|
} |
|
finally { |
|
a.recycle(); |
|
} |
|
|
|
return result; |
|
} |
|
|
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
|
public ToggleableButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { |
|
super(context, attrs, defStyleAttr, defStyleRes); |
|
|
|
ToggleableButtonAttributes toggleableButtonAttributes = setupAttributes(context, attrs); |
|
init(toggleableButtonAttributes); |
|
} |
|
|
|
/** |
|
* Initialize view |
|
*/ |
|
private void init(ToggleableButtonAttributes toggleableButtonAttributes) { |
|
|
|
inflate(getContext(), R.layout.customview_toggleabblebutton, this); |
|
setSaveEnabled(true); |
|
|
|
mCountTv = (TextView) findViewById(R.id.customview_toggleablebutton_tv_count); |
|
mIcon = (ImageView) findViewById(R.id.customview_toggleablebutton_iv_icon); |
|
|
|
setCountValue(toggleableButtonAttributes.mCount); |
|
setIconBasedOnActive(mActive); |
|
|
|
Log.d("init", "init called"); |
|
} |
|
|
|
|
|
public int getCountValue() { |
|
String countStr = mCountTv.getText().toString(); |
|
|
|
if(TextUtils.isEmpty(countStr) || !TextUtils.isDigitsOnly(countStr)) |
|
return 0; |
|
|
|
return Integer.parseInt(countStr); |
|
|
|
} |
|
|
|
public void setCountValue(int count) { |
|
|
|
String str = String.valueOf(count); |
|
mCountTv.setText(str); |
|
} |
|
|
|
public void incrementCount() { |
|
|
|
int count = getCountValue() + 1; |
|
setCountValue(count); |
|
} |
|
|
|
public void decrementCount() { |
|
|
|
int count = getCountValue() - 1; |
|
setCountValue(count); |
|
} |
|
|
|
public void setIcon(Drawable drawable) { |
|
|
|
if(drawable == null) { |
|
Drawable defaultIcon = ContextCompat.getDrawable(getContext(), R.drawable.ic_favorite_border); // use default icon of your choice |
|
mIcon.setImageDrawable(defaultIcon); |
|
} |
|
|
|
mIcon.setImageDrawable(drawable); |
|
} |
|
|
|
public void setIcon(int id) { |
|
|
|
Picasso.with(getContext()).load(id).placeholder(R.drawable.ic_favorite_border).error(R.drawable.ic_favorite_border).into(mIcon); // use default and error icons of your choice |
|
} |
|
|
|
public void setActive(boolean active) { |
|
mActive = active; |
|
setIconBasedOnActive(active); |
|
} |
|
|
|
public void enableClickToggle(boolean enable) { |
|
mClickToggleEnable = enable; |
|
} |
|
|
|
@Override |
|
public void setOnClickListener(OnClickListener onClickListener) { |
|
super.setOnClickListener(onClickListener); |
|
} |
|
|
|
public void setOnClickToggleListener(OnClickToggleListener onClickToggleListener) { |
|
mOnClickToggleListener = onClickToggleListener; |
|
} |
|
|
|
@Override |
|
public boolean onTouchEvent( MotionEvent event) { |
|
if(event.getAction() == MotionEvent.ACTION_DOWN){ |
|
return performClick(); |
|
} |
|
return true; |
|
} |
|
|
|
@Override |
|
public boolean performClick() { |
|
|
|
if(mClickToggleEnable) { |
|
if(mOnClickToggleListener != null) { |
|
performToggle(); |
|
return true; |
|
} |
|
else |
|
return false; |
|
} |
|
else { |
|
return super.performClick(); |
|
} |
|
} |
|
|
|
public void performToggle() { |
|
mActive = !mActive; |
|
|
|
setIconBasedOnActive(mActive); |
|
if(mActive) |
|
mOnClickToggleListener.onClicked(); |
|
else |
|
mOnClickToggleListener.onUnclicked(); |
|
} |
|
|
|
|
|
private void setIconBasedOnActive(boolean active) { |
|
|
|
if(active && mActiveIcon != null) |
|
setIcon(mActiveIcon); |
|
|
|
else if(!active && mIcon != null) |
|
setIcon(mInactiveIcon); |
|
|
|
} |
|
|
|
@Override |
|
public Parcelable onSaveInstanceState() { |
|
|
|
Bundle bundle = new Bundle(); |
|
|
|
bundle.putParcelable("instanceState", super.onSaveInstanceState()); |
|
bundle.putBoolean("clickToggleEnable", mClickToggleEnable); |
|
bundle.putBoolean("active", mActive); |
|
|
|
return bundle; |
|
} |
|
|
|
@Override |
|
public void onRestoreInstanceState(Parcelable state) { |
|
|
|
if(state instanceof Bundle) { |
|
Bundle bundle = (Bundle) state; |
|
|
|
mClickToggleEnable = bundle.getBoolean("clickToggleEnable"); |
|
mActive = bundle.getBoolean("active"); |
|
} |
|
|
|
super.onRestoreInstanceState(state); |
|
} |
|
|
|
} |
ToggleableButton

ToggleableButton is a library that allows you to create a button similar to:
Basic Usage
Copy all the files above
Attributes
The attributes that can be used to configure the button's behavior and appearance are:
Button State
To set the initial active state of the button you simply use the setActive() functionality via XML or Java. This will show the button in the click/unclicked state(depending on the active state) with the drawable that you selected.
XML
app:active="true"Java
button.setActive(true)You can also set the count of the button using the setCountValue() functionality via XML or Java:
XML
app:count="3"Java
button.setCountValue(5)OR
To increment the count use:
button.incrementCount()To decrement the count use:
button.decrementCount()Button Event Listener
To make the button toggleable and listen for click/unclick events:
You can also set the button to behave like a normal button, i.e. listen for only click events. To make the button behave like a normal button:
Example

To create this button:
XML
Icons can be found at Icons8
Java
Dependencies
Picasso