package com.marshalchen.common.uimodule.materialanimatedswitch; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.View; import com.marshalchen.common.uimodule.materialanimatedswitch.observer.BallFinishObservable; import com.marshalchen.common.uimodule.materialanimatedswitch.observer.BallMoveObservable; import com.marshalchen.common.uimodule.materialanimatedswitch.painter.BallPainter; import com.marshalchen.common.uimodule.materialanimatedswitch.painter.BallShadowPainter; import com.marshalchen.common.uimodule.materialanimatedswitch.painter.BasePainter; import com.marshalchen.common.uimodule.materialanimatedswitch.painter.IconPressPainter; import com.marshalchen.common.uimodule.materialanimatedswitch.painter.IconReleasePainter; import com.marshalchen.ultimateandroiduilollipop.R; import java.util.Observable; import java.util.Observer; /** * @author Adrián García Lomas */ public class MaterialAnimatedSwitch extends View { private int margin; private BasePainter basePainter; private BallPainter ballPainter; private BallShadowPainter ballShadowPainter; private MaterialAnimatedSwitchState actualState; private IconPressPainter iconPressPainter; private IconReleasePainter iconReleasePainter; private int baseColorRelease = Color.parseColor("#3061BE"); private int baseColorPress = Color.parseColor("#D7E7FF"); private int ballColorRelease = Color.parseColor("#5992FB"); private int ballColorPress = Color.parseColor("#FFFFFF"); private int ballShadowColor = Color.parseColor("#99000000"); private Bitmap releaseIcon; private Bitmap pressIcon; private BallFinishObservable ballFinishObservable; private BallMoveObservable ballMoveObservable; private boolean isClickable = true; private OnCheckedChangeListener onCheckedChangeListener; public MaterialAnimatedSwitch(Context context) { super(context); init(); } public MaterialAnimatedSwitch(Context context, AttributeSet attrs) { super(context, attrs); init(attrs); } public MaterialAnimatedSwitch(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs); } private void init() { margin = (int) getContext().getResources().getDimension(R.dimen.margin); initObservables(); initPainters(); actualState = MaterialAnimatedSwitchState.INIT; setState(actualState); setLayerType(View.LAYER_TYPE_SOFTWARE, null); } private void initPainters() { basePainter = new BasePainter(baseColorRelease, baseColorPress, margin, ballMoveObservable); ballPainter = new BallPainter(ballColorRelease, ballColorPress, margin, ballFinishObservable, ballMoveObservable, getContext()); ballShadowPainter = new BallShadowPainter(ballShadowColor, ballShadowColor, margin, ballShadowColor, ballFinishObservable, ballMoveObservable, getContext()); iconPressPainter = new IconPressPainter(getContext(), pressIcon, ballFinishObservable, ballMoveObservable, margin); iconReleasePainter = new IconReleasePainter(getContext(), releaseIcon, ballFinishObservable, margin); } private void init(AttributeSet attrs) { TypedArray attributes = getContext().obtainStyledAttributes(attrs, R.styleable.materialAnimatedSwitch); initAttributes(attributes); init(); } private void initAttributes(TypedArray attributes) { baseColorRelease = attributes.getColor(R.styleable.materialAnimatedSwitch_mas_base_release_color, baseColorRelease); baseColorPress = attributes.getColor(R.styleable.materialAnimatedSwitch_mas_base_press_color, baseColorPress); ballColorRelease = attributes.getColor(R.styleable.materialAnimatedSwitch_mas_ball_release_color, ballColorRelease); ballColorPress = attributes.getColor(R.styleable.materialAnimatedSwitch_mas_ball_press_color, ballColorPress); pressIcon = BitmapFactory.decodeResource(getResources(), attributes.getResourceId(R.styleable.materialAnimatedSwitch_mas_icon_press, R.drawable.mas_tack_save_button_32_blue)); releaseIcon = BitmapFactory.decodeResource(getResources(), attributes.getResourceId(R.styleable.materialAnimatedSwitch_mas_icon_release, R.drawable.mas_tack_save_button_32_white)); } private void initObservables() { ballFinishObservable = new BallFinishObservable(); ballMoveObservable = new BallMoveObservable(); ballFinishObservable.addObserver(new BallStateObserver()); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = Utils.dpToPx(45, getResources()); int height = Utils.dpToPx(28, getResources()); setMeasuredDimension(width, height); basePainter.onSizeChanged(height, width); ballShadowPainter.onSizeChanged(height, width); ballPainter.onSizeChanged(height, width); iconPressPainter.onSizeChanged(height, width); iconReleasePainter.onSizeChanged(height, width); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); basePainter.draw(canvas); ballShadowPainter.draw(canvas); ballPainter.draw(canvas); iconPressPainter.draw(canvas); iconReleasePainter.draw(canvas); invalidate(); } private void setState(MaterialAnimatedSwitchState materialAnimatedSwitchState) { basePainter.setState(materialAnimatedSwitchState); ballPainter.setState(materialAnimatedSwitchState); ballShadowPainter.setState(materialAnimatedSwitchState); iconPressPainter.setState(materialAnimatedSwitchState); iconReleasePainter.setState(materialAnimatedSwitchState); } @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (isClickable) { doActionDown(); } return true; default: return false; } } private void doActionDown() { if (actualState.equals(MaterialAnimatedSwitchState.RELEASE) || actualState.equals( MaterialAnimatedSwitchState.INIT) || actualState == null) { actualState = MaterialAnimatedSwitchState.PRESS; setState(actualState); } else { actualState = MaterialAnimatedSwitchState.RELEASE; setState(actualState); } playSoundEffect(SoundEffectConstants.CLICK); } public boolean isChecked() { return actualState.equals(MaterialAnimatedSwitchState.PRESS); } public void toggle() { if (isClickable) { doActionDown(); } } /** * Avoid click when ball is still in movement * Call listener when state is updated */ private class BallStateObserver implements Observer { @Override public void update(Observable observable, Object data) { BallFinishObservable ballFinishObservable = (BallFinishObservable) observable; isClickable = !ballFinishObservable.getState().equals(BallFinishObservable.BallState.MOVE); if (ballFinishObservable.getState().equals(BallFinishObservable.BallState.PRESS)) { if (onCheckedChangeListener != null) { onCheckedChangeListener.onCheckedChanged(true); } } else if (ballFinishObservable.getState().equals(BallFinishObservable.BallState.RELEASE)) { if (onCheckedChangeListener != null) { onCheckedChangeListener.onCheckedChanged(false); } } } } public void setOnCheckedChangeListener(OnCheckedChangeListener onCheckedChangeListener) { this.onCheckedChangeListener = onCheckedChangeListener; } public interface OnCheckedChangeListener { void onCheckedChanged(boolean isChecked); } }