Home | History | Annotate | Download | only in com.example.android.floatingactionbuttonbasic
      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          */
     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 
     63     public FloatingActionButton(Context context) {
     64         this(context, null, 0, 0);
     65     }
     66 
     67     public FloatingActionButton(Context context, AttributeSet attrs) {
     68         this(context, attrs, 0, 0);
     69     }
     70 
     71     public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
     72         this(context, attrs, defStyleAttr, 0);
     73     }
     74 
     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      */
     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      */
    120     public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
    121         mOnCheckedChangeListener = listener;
    122     }
    123 
    124     @Override
    125     public boolean isChecked() {
    126         return mChecked;
    127     }
    128 
    129     @Override
    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
    138     public boolean performClick() {
    139         toggle();
    140         return super.performClick();
    141     }
    142 
    143     @Override
    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
    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