package com.roboo.like.google.view.floatingbutton;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.StateListDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.AbsListView;
import android.widget.ImageButton;
import com.roboo.like.google.R;
/**
* Android Google+ like floating action button which reacts on the attached list view scrolling events.
*
* @author Oleksandr Melnykov
*/
public class FloatingImageButton extends ImageButton
{
public static final int TYPE_NORMAL = 0;
public static final int TYPE_MINI = 1;
private StateListDrawable mDrawable;
private AbsListView mListView;
private int mScrollY;
private boolean mVisible;
private int mColorNormal;
private int mColorPressed;
private boolean mShadow;
private int mType;
private final ScrollSettleHandler mScrollSettleHandler = new ScrollSettleHandler();
private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
public FloatingImageButton(Context context)
{
super(context);
init(context, null);
}
public FloatingImageButton(Context context, AttributeSet attrs)
{
super(context, attrs);
init(context, attrs);
}
public FloatingImageButton(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int size = getDimension(mType == TYPE_NORMAL ? R.dimen.fab_size_normal : R.dimen.fab_size_mini);
if (mShadow)
{
int shadowSize = getDimension(R.dimen.fab_shadow_size);
size += shadowSize * 2;
}
setMeasuredDimension(size, size);
}
@Override
public Parcelable onSaveInstanceState()
{
Parcelable superState = super.onSaveInstanceState();
SavedState savedState = new SavedState(superState);
savedState.mScrollY = mScrollY;
return savedState;
}
@Override
public void onRestoreInstanceState(Parcelable state)
{
if (state instanceof SavedState)
{
SavedState savedState = (SavedState) state;
mScrollY = savedState.mScrollY;
super.onRestoreInstanceState(savedState.getSuperState());
}
else
{
super.onRestoreInstanceState(state);
}
}
private void init(Context context, AttributeSet attributeSet)
{
mVisible = true;
mColorNormal = getColor(android.R.color.holo_blue_dark);
mColorPressed = getColor(android.R.color.holo_blue_light);
mType = TYPE_NORMAL;
mShadow = true;
if (attributeSet != null)
{
initAttributes(context, attributeSet);
}
updateBackground();
}
private void initAttributes(Context context, AttributeSet attributeSet)
{
TypedArray attr = getTypedArray(context, attributeSet, R.styleable.FloatingImageButton);
if (attr != null)
{
try
{
mColorNormal = attr.getColor(R.styleable.FloatingImageButton_fab_colorNormal, getColor(android.R.color.holo_blue_dark));
mColorPressed = attr.getColor(R.styleable.FloatingImageButton_fab_colorPressed, getColor(android.R.color.holo_blue_light));
mShadow = attr.getBoolean(R.styleable.FloatingImageButton_fab_shadow, true);
mType = attr.getInt(R.styleable.FloatingImageButton_fab_type, TYPE_NORMAL);
}
finally
{
attr.recycle();
}
}
}
private void updateBackground()
{
mDrawable = new StateListDrawable();
mDrawable.addState(new int[] { android.R.attr.state_pressed }, createDrawable(mColorPressed));
mDrawable.addState(new int[] {}, createDrawable(mColorNormal));
setBackgroundCompat(mDrawable);
}
private Drawable createDrawable(int color)
{
OvalShape ovalShape = new OvalShape();
ShapeDrawable shapeDrawable = new ShapeDrawable(ovalShape);
shapeDrawable.getPaint().setColor(color);
if (mShadow)
{
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[] { getResources().getDrawable(R.drawable.ic_shadow), shapeDrawable });
int shadowSize = getDimension(mType == TYPE_NORMAL ? R.dimen.fab_shadow_size : R.dimen.fab_mini_shadow_size);
layerDrawable.setLayerInset(1, shadowSize, shadowSize, shadowSize, shadowSize);
return layerDrawable;
}
else
{
return shapeDrawable;
}
}
private TypedArray getTypedArray(Context context, AttributeSet attributeSet, int[] attr)
{
return context.obtainStyledAttributes(attributeSet, attr, 0, 0);
}
private int getColor(int id)
{
return getResources().getColor(id);
}
private int getDimension(int id)
{
return getResources().getDimensionPixelSize(id);
}
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
private void setBackgroundCompat(Drawable drawable)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
{
setBackground(drawable);
}
else
{
setBackgroundDrawable(drawable);
}
}
private int getListViewScrollY()
{
View topChild = mListView.getChildAt(0);
return topChild == null ? 0 : mListView.getFirstVisiblePosition() * topChild.getHeight() - topChild.getTop();
}
private int getMarginBottom()
{
int marginBottom = 0;
final ViewGroup.LayoutParams layoutParams = getLayoutParams();
if (layoutParams instanceof ViewGroup.MarginLayoutParams)
{
marginBottom = ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin;
}
return marginBottom;
}
private class ScrollSettleHandler extends Handler
{
private static final int TRANSLATE_DURATION_MILLIS = 200;
private int mSettledScrollY;
public void onScroll(int scrollY)
{
if (mSettledScrollY != scrollY)
{
mSettledScrollY = scrollY;
removeMessages(0);
sendEmptyMessage(0);
}
}
@Override
public void handleMessage(Message msg)
{
animate().setInterpolator(mInterpolator).setDuration(TRANSLATE_DURATION_MILLIS).translationY(mSettledScrollY);
}
}
public void setColorNormal(int color)
{
if (color != mColorNormal)
{
mColorNormal = color;
updateBackground();
}
}
public int getColorNormal()
{
return mColorNormal;
}
public void setColorPressed(int color)
{
if (color != mColorPressed)
{
mColorPressed = color;
updateBackground();
}
}
public int getColorPressed()
{
return mColorPressed;
}
public void setShadow(boolean shadow)
{
if (shadow != mShadow)
{
mShadow = shadow;
updateBackground();
}
}
public boolean hasShadow()
{
return mShadow;
}
public void setType(int type)
{
if (type != mType)
{
mType = type;
updateBackground();
}
}
public int getType()
{
return mType;
}
public void attachToListView(AbsListView listView)
{
if (listView == null)
{
throw new NullPointerException("AbsListView cannot be null.");
}
mListView = listView;
mListView.setOnScrollListener(new AbsListView.OnScrollListener()
{
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
{}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
int newScrollY = getListViewScrollY();
if (newScrollY == mScrollY)
{
return;
}
if (newScrollY > mScrollY && mVisible)
{
// Scrolling up
mVisible = false;
mScrollSettleHandler.onScroll(getHeight() + getMarginBottom());
}
else if (newScrollY < mScrollY && !mVisible)
{
// Scrolling down
mVisible = true;
mScrollSettleHandler.onScroll(0);
}
mScrollY = newScrollY;
}
});
}
/**
* A {@link android.os.Parcelable} representing the {@link com.FloatingImageButton.fab.FloatingActionButton}'s state.
*/
public static class SavedState extends BaseSavedState
{
private int mScrollY;
public SavedState(Parcelable parcel)
{
super(parcel);
}
private SavedState(Parcel in)
{
super(in);
mScrollY = in.readInt();
}
@Override
public void writeToParcel(Parcel out, int flags)
{
super.writeToParcel(out, flags);
out.writeInt(mScrollY);
}
public static final Creator<SavedState> CREATOR = new Creator<SavedState>()
{
@Override
public SavedState createFromParcel(Parcel in)
{
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size)
{
return new SavedState[size];
}
};
}
}