1 /* 2 * Copyright 2014, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.floatingactionbuttonbasic; 18 19 import android.content.Context; 20 import android.graphics.Outline; 21 import android.util.AttributeSet; 22 import android.view.View; 23 import android.view.ViewOutlineProvider; 24 import android.widget.Checkable; 25 import android.widget.FrameLayout; 26 27 /** 28 * A Floating Action Button is a {@link android.widget.Checkable} view distinguished by a circled 29 * icon floating above the UI, with special motion behaviors. 30 */ 31 public class FloatingActionButton extends FrameLayout implements Checkable { 32 33 /** 34 * Interface definition for a callback to be invoked when the checked state 35 * of a compound button changes. 36 */ 37 public static interface OnCheckedChangeListener { 38 39 /** 40 * Called when the checked state of a FAB has changed. 41 * 42 * @param fabView The FAB view whose state has changed. 43 * @param isChecked The new checked state of buttonView. 44 */ onCheckedChanged(FloatingActionButton fabView, boolean isChecked)45 void onCheckedChanged(FloatingActionButton fabView, boolean isChecked); 46 } 47 48 /** 49 * An array of states. 50 */ 51 private static final int[] CHECKED_STATE_SET = { 52 android.R.attr.state_checked 53 }; 54 55 private static final String TAG = "FloatingActionButton"; 56 57 // A boolean that tells if the FAB is checked or not. 58 private boolean mChecked; 59 60 // A listener to communicate that the FAB has changed it's state 61 private OnCheckedChangeListener mOnCheckedChangeListener; 62 FloatingActionButton(Context context)63 public FloatingActionButton(Context context) { 64 this(context, null, 0, 0); 65 } 66 FloatingActionButton(Context context, AttributeSet attrs)67 public FloatingActionButton(Context context, AttributeSet attrs) { 68 this(context, attrs, 0, 0); 69 } 70 FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr)71 public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) { 72 this(context, attrs, defStyleAttr, 0); 73 } 74 FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)75 public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr, 76 int defStyleRes) { 77 super(context, attrs, defStyleAttr); 78 79 setClickable(true); 80 81 // Set the outline provider for this view. The provider is given the outline which it can 82 // then modify as needed. In this case we set the outline to be an oval fitting the height 83 // and width. 84 setOutlineProvider(new ViewOutlineProvider() { 85 @Override 86 public void getOutline(View view, Outline outline) { 87 outline.setOval(0, 0, getWidth(), getHeight()); 88 } 89 }); 90 91 // Finally, enable clipping to the outline, using the provider we set above 92 setClipToOutline(true); 93 } 94 95 /** 96 * Sets the checked/unchecked state of the FAB. 97 * @param checked 98 */ setChecked(boolean checked)99 public void setChecked(boolean checked) { 100 // If trying to set the current state, ignore. 101 if (checked == mChecked) { 102 return; 103 } 104 mChecked = checked; 105 106 // Now refresh the drawable state (so the icon changes) 107 refreshDrawableState(); 108 109 if (mOnCheckedChangeListener != null) { 110 mOnCheckedChangeListener.onCheckedChanged(this, checked); 111 } 112 } 113 114 /** 115 * Register a callback to be invoked when the checked state of this button 116 * changes. 117 * 118 * @param listener the callback to call on checked state change 119 */ setOnCheckedChangeListener(OnCheckedChangeListener listener)120 public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { 121 mOnCheckedChangeListener = listener; 122 } 123 124 @Override isChecked()125 public boolean isChecked() { 126 return mChecked; 127 } 128 129 @Override toggle()130 public void toggle() { 131 setChecked(!mChecked); 132 } 133 134 /** 135 * Override performClick() so that we can toggle the checked state when the view is clicked 136 */ 137 @Override performClick()138 public boolean performClick() { 139 toggle(); 140 return super.performClick(); 141 } 142 143 @Override onSizeChanged(int w, int h, int oldw, int oldh)144 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 145 super.onSizeChanged(w, h, oldw, oldh); 146 147 // As we have changed size, we should invalidate the outline so that is the the 148 // correct size 149 invalidateOutline(); 150 } 151 152 @Override onCreateDrawableState(int extraSpace)153 protected int[] onCreateDrawableState(int extraSpace) { 154 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); 155 if (isChecked()) { 156 mergeDrawableStates(drawableState, CHECKED_STATE_SET); 157 } 158 return drawableState; 159 } 160 } 161