Skip to content

Instantly share code, notes, and snippets.

@arturgaleno
Last active October 22, 2015 13:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arturgaleno/5cca1934ed324bd2d9a7 to your computer and use it in GitHub Desktop.
Save arturgaleno/5cca1934ed324bd2d9a7 to your computer and use it in GitHub Desktop.
Workarround for FloatingActionButton bug on design library - version 23.0.0.
package com.materialcontentoverflow;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.view.TintableBackgroundView;
import android.support.v7.internal.widget.TintInfo;
import android.support.v7.internal.widget.TintManager;
import android.support.v7.internal.widget.TintTypedArray;
import android.util.AttributeSet;
/**
* Created by arturgaleno on 26/08/15.
*/
public class TintFloatingActionButton extends FloatingActionButton implements TintableBackgroundView {
static final int[] PRESSED_ENABLED_STATE_SET = {android.R.attr.state_pressed,
android.R.attr.state_enabled};
static final int[] FOCUSED_ENABLED_STATE_SET = {android.R.attr.state_focused,
android.R.attr.state_enabled};
private static final int[] TINT_ATTRS = {
android.R.attr.background
};
private TintInfo mBackgroundTint;
public TintFloatingActionButton(Context context) {
super(context);
}
public TintFloatingActionButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TintFloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (TintManager.SHOULD_BE_USED) {
TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
TINT_ATTRS, defStyleAttr, 0);
if (a.hasValue(0)) {
setSupportBackgroundTintList(createColorStateList(a.getResourceId(0, -1)));
}
a.recycle();
}
}
private static ColorStateList createColorStateList(int selectedColor) {
final int[][] states = new int[3][];
final int[] colors = new int[3];
int i = 0;
states[i] = FOCUSED_ENABLED_STATE_SET;
colors[i] = selectedColor;
i++;
states[i] = PRESSED_ENABLED_STATE_SET;
colors[i] = selectedColor;
i++;
// Default enabled state
states[i] = new int[0];
colors[i] = Color.TRANSPARENT;
i++;
return new ColorStateList(states, colors);
}
@Override
public void setSupportBackgroundTintList(ColorStateList tint) {
if (mBackgroundTint == null) {
mBackgroundTint = new TintInfo();
}
mBackgroundTint.mTintList = tint;
mBackgroundTint.mHasTintList = tint != null;
applySupportBackgroundTint();
}
@Nullable
@Override
public ColorStateList getSupportBackgroundTintList() {
return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
}
@Override
public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
if (mBackgroundTint == null) {
mBackgroundTint = new TintInfo();
}
mBackgroundTint.mTintMode = tintMode;
mBackgroundTint.mHasTintMode = tintMode != null;
applySupportBackgroundTint();
}
@Nullable
@Override
public PorterDuff.Mode getSupportBackgroundTintMode() {
return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
}
private void applySupportBackgroundTint() {
if (getBackground() != null && mBackgroundTint != null) {
TintManager.tintViewBackground(this, mBackgroundTint);
}
}
}
@arturgaleno
Copy link
Author

Then, I call:

fab = new TintFloatingActionButton(context);
ViewCompat.setBackgroundTintList(fab, ContextCompat.getColorStateList(context, buttonColor));

@mhrst
Copy link

mhrst commented Oct 3, 2015

Thanks, this mostly worked for me. However, the FAB was still going transparent after receiving a touch event. It seemed like it was caused by the reverse ripple affect that triggers on MotionEvent.ACTION_UP or when you held down on the button and then dragged your finger off.

I tried to just remove the ripple effect. Or somehow not trigger that animation but not knowing much about it I settled on this hack that re-tints the background, using your above code, whenever either of those things happen:

fab_newnote.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Rect rect;
        if(event.getAction() == MotionEvent.ACTION_DOWN){
            // Construct a rect of the view's bounds
            fabRect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());

        } else if(event.getAction() == MotionEvent.ACTION_UP) {
            fixFabBackgroundTint();

        } else if(event.getAction() == MotionEvent.ACTION_MOVE){
            if(!fabRect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){
                // User moved outside bounds
                fixFabBackgroundTint();
            }
        }
        return false;
    }
});

My fixFabBackgroundTint() method:

private void fixFabBackgroundTint() {
    // A fix for https://code.google.com/p/android/issues/detail?id=183315
    if(Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) return;
    // Gingerbread only
    ViewCompat.setBackgroundTintList(fab_newnote, ColorStateList.valueOf(getResources().getColor(R.color.fab_background_tint)));

}

@arturgaleno
Copy link
Author

@nyanmatt Your use case isn't in my initial case, probably its related to the ColorStateList. I think if you change:

states[i] = new int[0];
colors[i] = Color.TRANSPARENT;
i++;

to:

states[i] = new int[0];
colors[i] = selectedColor;
i++;

It's could work. If you can test this, will be nice, because the issue still open, until now.

see: https://code.google.com/p/android/issues/detail?id=183315

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