Skip to content

Instantly share code, notes, and snippets.

@kkuivi
Last active September 22, 2017 00:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kkuivi/2f886ca1a8f8adf608aeefa5db0eb6ac to your computer and use it in GitHub Desktop.
Save kkuivi/2f886ca1a8f8adf608aeefa5db0eb6ac to your computer and use it in GitHub Desktop.
ToggleableButton - Easy way to create button with click/unclick functionality for Android.
<!-- This should be in res/values/attrs.xml -->
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ToggleableButton">
<attr name="count" format="integer" />
<attr name="icon" format="reference" />
<attr name="active_icon" format="reference" />
<attr name="active" format="boolean" />
</declare-styleable>
</resources>
<!-- This should be in your res/layout directory -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/container_toggleablebutton"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/customview_toggleablebutton_iv_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"
android:padding="5dp"/>
<TextView
android:id="@+id/customview_toggleablebutton_tv_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:text="100"
android:paddingTop="12dp"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textStyle="bold"/>
</LinearLayout>
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);
}
}
@kkuivi
Copy link
Author

kkuivi commented Sep 21, 2017

ToggleableButton
ToggleableButton is a library that allows you to create a button similar to:
this

Basic Usage
Copy all the files above

Attributes
The attributes that can be used to configure the button's behavior and appearance are:

          <com.example.app.ToggleableButton
            app:icon="@drawable/ic_favorite_border"
            app:active_icon="@drawable/ic_favorite_active"
            app:active="true"
            app:count="5"/>

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:

  button.enableClickToggle(true);
  button.setOnClickToggleListener(new OnClickToggleListener {
        
       @Override
        public void onClicked(){
               // implement code
        }

        @Override
        public void onUnclicked(){
               // implement code
        }
    });

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:

  button.enableClickToggle(false);
  button.setOnClickListener(new OnClickListener {
         @Override
         public void onClick() {
             //implement code
         }
    });

Example
To create this button:
this

XML

    <com.example.app.ToggleableButton`
            android:id="@+id/item_post_ib_like"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:icon="@drawable/icons8_heart_inactive"
            app:active_icon="@drawable/icons8_love_active"
            android:background="@android:color/transparent"
            android:layout_marginRight="7dp"/>

Icons can be found at Icons8

Java

        button.enableClickToggle(true);
        button.setOnClickToggleListener(new ToggleableButton.OnClickToggleListener() {
               @Override
               public void onClicked() {
                   button.incrementCount();
               }

               @Override
               public void onUnclicked() {
                   button.decrementCount();
               }
           });

Dependencies
Picasso

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment