Skip to content

Instantly share code, notes, and snippets.

@samskiter
Forked from chrisbanes/FloatLabelLayout.java
Last active August 29, 2015 14:22
Show Gist options
  • Select an option

  • Save samskiter/e1e760cfdbb26e1a337a to your computer and use it in GitHub Desktop.

Select an option

Save samskiter/e1e760cfdbb26e1a337a to your computer and use it in GitHub Desktop.

Revisions

  1. @chrisbanes chrisbanes revised this gist Jan 6, 2015. 5 changed files with 139 additions and 76 deletions.
    201 changes: 131 additions & 70 deletions FloatLabelLayout.java
    Original file line number Diff line number Diff line change
    @@ -1,11 +1,11 @@
    /*
    * Copyright (C) 2014 Chris Banes
    * Copyright 2014 Chris Banes
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * http://www.apache.org/licenses/LICENSE-2.0
    * http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    @@ -14,20 +14,22 @@
    * limitations under the License.
    */

    import android.animation.Animator;
    import android.animation.AnimatorListenerAdapter;
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.os.Build;
    import android.support.v4.view.ViewCompat;
    import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
    import android.text.Editable;
    import android.text.TextUtils;
    import android.text.TextWatcher;
    import android.util.AttributeSet;
    import android.util.TypedValue;
    import android.view.Gravity;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.animation.AnimationUtils;
    import android.view.animation.Interpolator;
    import android.widget.EditText;
    import android.widget.FrameLayout;
    import android.widget.LinearLayout;
    import android.widget.TextView;

    /**
    @@ -37,15 +39,21 @@
    * @see <a href="https://dribbble.com/shots/1254439--GIF-Mobile-Form-Interaction">Matt D. Smith on Dribble</a>
    * @see <a href="http://bradfrostweb.com/blog/post/float-label-pattern/">Brad Frost's blog post</a>
    */
    public final class FloatLabelLayout extends FrameLayout {
    public class FloatLabelLayout extends LinearLayout {

    private static final long ANIMATION_DURATION = 150;

    private static final float DEFAULT_PADDING_LEFT_RIGHT_DP = 4f;
    private static final float DEFAULT_LABEL_PADDING_LEFT = 3f;
    private static final float DEFAULT_LABEL_PADDING_TOP = 4f;
    private static final float DEFAULT_LABEL_PADDING_RIGHT = 3f;
    private static final float DEFAULT_LABEL_PADDING_BOTTOM = 4f;

    private EditText mEditText;
    private TextView mLabel;

    private CharSequence mHint;
    private Interpolator mInterpolator;

    public FloatLabelLayout(Context context) {
    this(context, null);
    }
    @@ -57,40 +65,47 @@ public FloatLabelLayout(Context context, AttributeSet attrs) {
    public FloatLabelLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    final TypedArray a = context
    .obtainStyledAttributes(attrs, R.styleable.FloatLabelLayout);
    setOrientation(VERTICAL);

    final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FloatLabelLayout);

    int leftPadding = a.getDimensionPixelSize(
    R.styleable.FloatLabelLayout_floatLabelPaddingLeft,
    dipsToPix(DEFAULT_LABEL_PADDING_LEFT));
    int topPadding = a.getDimensionPixelSize(
    R.styleable.FloatLabelLayout_floatLabelPaddingTop,
    dipsToPix(DEFAULT_LABEL_PADDING_TOP));
    int rightPadding = a.getDimensionPixelSize(
    R.styleable.FloatLabelLayout_floatLabelPaddingRight,
    dipsToPix(DEFAULT_LABEL_PADDING_RIGHT));
    int bottomPadding = a.getDimensionPixelSize(
    R.styleable.FloatLabelLayout_floatLabelPaddingBottom,
    dipsToPix(DEFAULT_LABEL_PADDING_BOTTOM));
    mHint = a.getText(R.styleable.FloatLabelLayout_floatLabelHint);

    final int sidePadding = a.getDimensionPixelSize(
    R.styleable.FloatLabelLayout_floatLabelSidePadding,
    dipsToPix(DEFAULT_PADDING_LEFT_RIGHT_DP));
    mLabel = new TextView(context);
    mLabel.setPadding(sidePadding, 0, sidePadding, 0);
    mLabel.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
    mLabel.setVisibility(INVISIBLE);
    mLabel.setText(mHint);
    ViewCompat.setPivotX(mLabel, 0f);
    ViewCompat.setPivotY(mLabel, 0f);

    mLabel.setTextAppearance(context,
    a.getResourceId(R.styleable.FloatLabelLayout_floatLabelTextAppearance,
    android.R.style.TextAppearance_Small));
    a.recycle();

    addView(mLabel, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

    a.recycle();
    mInterpolator = AnimationUtils.loadInterpolator(context,
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
    ? android.R.interpolator.fast_out_slow_in
    : android.R.anim.decelerate_interpolator);
    }

    @Override
    public final void addView(View child, int index, ViewGroup.LayoutParams params) {
    if (child instanceof EditText) {
    // If we already have an EditText, throw an exception
    if (mEditText != null) {
    throw new IllegalArgumentException("We already have an EditText, can only have one");
    }

    // Update the layout params so that the EditText is at the bottom, with enough top
    // margin to show the label
    final LayoutParams lp = new LayoutParams(params);
    lp.gravity = Gravity.BOTTOM;
    lp.topMargin = (int) mLabel.getTextSize();
    params = lp;

    setEditText((EditText) child);
    }

    @@ -99,46 +114,61 @@ public final void addView(View child, int index, ViewGroup.LayoutParams params)
    }

    private void setEditText(EditText editText) {
    // If we already have an EditText, throw an exception
    if (mEditText != null) {
    throw new IllegalArgumentException("We already have an EditText, can only have one");
    }
    mEditText = editText;

    // Update the label visibility with no animation
    updateLabelVisibility(false);

    // Add a TextWatcher so that we know when the text input has changed
    mEditText.addTextChangedListener(new TextWatcher() {

    @Override
    public void afterTextChanged(Editable s) {
    if (TextUtils.isEmpty(s)) {
    // The text is empty, so hide the label if it is visible
    if (mLabel.getVisibility() == View.VISIBLE) {
    hideLabel();
    }
    } else {
    // The text is not empty, so show the label if it is not visible
    if (mLabel.getVisibility() != View.VISIBLE) {
    showLabel();
    }
    }
    updateLabelVisibility(true);
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
    }

    public void onTextChanged(CharSequence s, int start, int before, int count) {}
    });

    // Add focus listener to the EditText so that we can notify the label that it is activated.
    // Allows the use of a ColorStateList for the text color on the label
    mEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
    @Override
    public void onFocusChange(View view, boolean focused) {
    mLabel.setActivated(focused);
    updateLabelVisibility(true);
    }
    });

    mLabel.setText(mEditText.getHint());
    // If we do not have a valid hint, try and retrieve it from the EditText
    if (TextUtils.isEmpty(mHint)) {
    setHint(mEditText.getHint());
    }
    }

    private void updateLabelVisibility(boolean animate) {
    boolean hasText = !TextUtils.isEmpty(mEditText.getText());
    boolean isFocused = mEditText.isFocused();

    mLabel.setActivated(isFocused);

    if (hasText || isFocused) {
    // We should be showing the label so do so if it isn't already
    if (mLabel.getVisibility() != VISIBLE) {
    showLabel(animate);
    }
    } else {
    // We should not be showing the label so hide it
    if (mLabel.getVisibility() == VISIBLE) {
    hideLabel(animate);
    }
    }
    }

    /**
    @@ -156,35 +186,66 @@ public TextView getLabel() {
    }

    /**
    * Show the label using an animation
    * Set the hint to be displayed in the floating label
    */
    public void setHint(CharSequence hint) {
    mHint = hint;
    mLabel.setText(hint);
    }

    /**
    * Show the label
    */
    private void showLabel() {
    mLabel.setVisibility(View.VISIBLE);
    mLabel.setAlpha(0f);
    mLabel.setTranslationY(mLabel.getHeight());
    mLabel.animate()
    .alpha(1f)
    .translationY(0f)
    .setDuration(ANIMATION_DURATION)
    .setListener(null).start();
    private void showLabel(boolean animate) {
    if (animate) {
    mLabel.setVisibility(View.VISIBLE);
    ViewCompat.setTranslationY(mLabel, mLabel.getHeight());

    float scale = mEditText.getTextSize() / mLabel.getTextSize();
    ViewCompat.setScaleX(mLabel, scale);
    ViewCompat.setScaleY(mLabel, scale);

    ViewCompat.animate(mLabel)
    .translationY(0f)
    .scaleY(1f)
    .scaleX(1f)
    .setDuration(ANIMATION_DURATION)
    .setListener(null)
    .setInterpolator(mInterpolator).start();
    } else {
    mLabel.setVisibility(VISIBLE);
    }

    mEditText.setHint(null);
    }

    /**
    * Hide the label using an animation
    * Hide the label
    */
    private void hideLabel() {
    mLabel.setAlpha(1f);
    mLabel.setTranslationY(0f);
    mLabel.animate()
    .alpha(0f)
    .translationY(mLabel.getHeight())
    .setDuration(ANIMATION_DURATION)
    .setListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
    mLabel.setVisibility(View.GONE);
    }
    }).start();
    private void hideLabel(boolean animate) {
    if (animate) {
    float scale = mEditText.getTextSize() / mLabel.getTextSize();
    ViewCompat.setScaleX(mLabel, 1f);
    ViewCompat.setScaleY(mLabel, 1f);
    ViewCompat.setTranslationY(mLabel, 0f);

    ViewCompat.animate(mLabel)
    .translationY(mLabel.getHeight())
    .setDuration(ANIMATION_DURATION)
    .scaleX(scale)
    .scaleY(scale)
    .setListener(new ViewPropertyAnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(View view) {
    mLabel.setVisibility(INVISIBLE);
    mEditText.setHint(mHint);
    }
    })
    .setInterpolator(mInterpolator).start();
    } else {
    mLabel.setVisibility(INVISIBLE);
    mEditText.setHint(mHint);
    }
    }

    /**
    7 changes: 6 additions & 1 deletion attrs.xml
    Original file line number Diff line number Diff line change
    @@ -19,7 +19,12 @@

    <declare-styleable name="FloatLabelLayout">
    <attr name="floatLabelTextAppearance" format="reference" />
    <attr name="floatLabelSidePadding" format="reference|dimension" />
    <attr name="floatLabelPaddingLeft" format="reference|dimension" />
    <attr name="floatLabelPaddingRight" format="reference|dimension" />
    <attr name="floatLabelPaddingTop" format="reference|dimension" />
    <attr name="floatLabelPaddingBottom" format="reference|dimension" />
    <!-- The hint to display in the floating label -->
    <attr name="floatLabelHint" format="reference|string" />
    </declare-styleable>

    </resources>
    2 changes: 1 addition & 1 deletion example_colors-float_label.xml
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@
    <!-- The color when activated/focused (usually your app's accent color) -->
    <item android:color="..." android:state_activated="true" />

    <!-- The color when not activated/focused (usually grey) -->
    <!-- The color when not activated/focused (usually android:textColorSecondary) -->
    <item android:color="..." />

    </selector>
    2 changes: 0 additions & 2 deletions example_layout.xml
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,6 @@
    <your.package.FloatLabelLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    app:floatLabelTextAppearance="@style/TextAppearance.YourApp.FloatLabel">

    <EditText
    @@ -27,7 +26,6 @@
    <your.package.FloatLabelLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    app:floatLabelTextAppearance="@style/TextAppearance.YourApp.FloatLabel">

    <EditText
    3 changes: 1 addition & 2 deletions example_styles.xml
    Original file line number Diff line number Diff line change
    @@ -4,8 +4,7 @@

    <style name="TextAppearance.YourApp.FloatLabel" parent="android:TextAppearance.Small">
    <item name="android:textColor">@color/float_label</item>
    <item name="android:textSize">11sp</item>
    <item name="android:textStyle">bold</item>
    <item name="android:textSize">12sp</item>
    </style>

    </resources>
  2. @chrisbanes chrisbanes revised this gist Oct 22, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion FloatLabelLayout.java
    Original file line number Diff line number Diff line change
    @@ -41,7 +41,7 @@ public final class FloatLabelLayout extends FrameLayout {

    private static final long ANIMATION_DURATION = 150;

    private static final float DEFAULT_PADDING_LEFT_RIGHT_DP = 12f;
    private static final float DEFAULT_PADDING_LEFT_RIGHT_DP = 4f;

    private EditText mEditText;
    private TextView mLabel;
  3. @chrisbanes chrisbanes revised this gist Apr 25, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion example_styles.xml
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@

    <resources>

    <style name="TextAppearance.Philm.FloatLabel" parent="android:TextAppearance.Small">
    <style name="TextAppearance.YourApp.FloatLabel" parent="android:TextAppearance.Small">
    <item name="android:textColor">@color/float_label</item>
    <item name="android:textSize">11sp</item>
    <item name="android:textStyle">bold</item>
  4. @chrisbanes chrisbanes revised this gist Apr 25, 2014. 2 changed files with 22 additions and 0 deletions.
    11 changes: 11 additions & 0 deletions example_colors-float_label.xml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    <?xml version="1.0" encoding="utf-8"?>

    <selector xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- The color when activated/focused (usually your app's accent color) -->
    <item android:color="..." android:state_activated="true" />

    <!-- The color when not activated/focused (usually grey) -->
    <item android:color="..." />

    </selector>
    11 changes: 11 additions & 0 deletions example_styles.xml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    <?xml version="1.0" encoding="utf-8"?>

    <resources>

    <style name="TextAppearance.Philm.FloatLabel" parent="android:TextAppearance.Small">
    <item name="android:textColor">@color/float_label</item>
    <item name="android:textSize">11sp</item>
    <item name="android:textStyle">bold</item>
    </style>

    </resources>
  5. @chrisbanes chrisbanes revised this gist Apr 24, 2014. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions FloatLabelLayout.java
    Original file line number Diff line number Diff line change
    @@ -57,23 +57,23 @@ public FloatLabelLayout(Context context, AttributeSet attrs) {
    public FloatLabelLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    final TypedArray array = context
    final TypedArray a = context
    .obtainStyledAttributes(attrs, R.styleable.FloatLabelLayout);

    final int sidePadding = array.getDimensionPixelSize(
    final int sidePadding = a.getDimensionPixelSize(
    R.styleable.FloatLabelLayout_floatLabelSidePadding,
    dipsToPix(DEFAULT_PADDING_LEFT_RIGHT_DP));
    mLabel = new TextView(context);
    mLabel.setPadding(sidePadding, 0, sidePadding, 0);
    mLabel.setVisibility(INVISIBLE);

    mLabel.setTextAppearance(context,
    array.getResourceId(R.styleable.FloatLabelLayout_floatLabelTextAppearance,
    a.getResourceId(R.styleable.FloatLabelLayout_floatLabelTextAppearance,
    android.R.style.TextAppearance_Small));

    addView(mLabel, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

    array.recycle();
    a.recycle();
    }

    @Override
  6. @chrisbanes chrisbanes revised this gist Apr 24, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion example_layout.xml
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    app:floatLabelTextAppearance="@style/TextAppearance.Philm.FloatLabel">
    app:floatLabelTextAppearance="@style/TextAppearance.YourApp.FloatLabel">

    <EditText
    android:id="@+id/edit_username"
  7. @chrisbanes chrisbanes revised this gist Apr 24, 2014. 1 changed file with 44 additions and 0 deletions.
    44 changes: 44 additions & 0 deletions example_layout.xml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,44 @@
    <?xml version="1.0" encoding="utf-8"?>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <your.package.FloatLabelLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    app:floatLabelTextAppearance="@style/TextAppearance.Philm.FloatLabel">

    <EditText
    android:id="@+id/edit_username"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="@string/account_username_hint"
    android:singleLine="true"
    android:inputType="textNoSuggestions"
    android:imeOptions="actionNext"
    android:nextFocusDown="@+id/edit_password" />

    </your.package.FloatLabelLayout>

    <your.package.FloatLabelLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    app:floatLabelTextAppearance="@style/TextAppearance.YourApp.FloatLabel">

    <EditText
    android:id="@+id/edit_password"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="@string/account_password_hint"
    android:singleLine="true"
    android:inputType="textNoSuggestions"
    android:imeOptions="actionDone" />

    </your.package.FloatLabelLayout>

    </LinearLayout>
  8. @chrisbanes chrisbanes created this gist Apr 24, 2014.
    197 changes: 197 additions & 0 deletions FloatLabelLayout.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,197 @@
    /*
    * Copyright (C) 2014 Chris Banes
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    */

    import android.animation.Animator;
    import android.animation.AnimatorListenerAdapter;
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.text.Editable;
    import android.text.TextUtils;
    import android.text.TextWatcher;
    import android.util.AttributeSet;
    import android.util.TypedValue;
    import android.view.Gravity;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.EditText;
    import android.widget.FrameLayout;
    import android.widget.TextView;

    /**
    * Layout which an {@link android.widget.EditText} to show a floating label when the hint is hidden
    * due to the user inputting text.
    *
    * @see <a href="https://dribbble.com/shots/1254439--GIF-Mobile-Form-Interaction">Matt D. Smith on Dribble</a>
    * @see <a href="http://bradfrostweb.com/blog/post/float-label-pattern/">Brad Frost's blog post</a>
    */
    public final class FloatLabelLayout extends FrameLayout {

    private static final long ANIMATION_DURATION = 150;

    private static final float DEFAULT_PADDING_LEFT_RIGHT_DP = 12f;

    private EditText mEditText;
    private TextView mLabel;

    public FloatLabelLayout(Context context) {
    this(context, null);
    }

    public FloatLabelLayout(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    }

    public FloatLabelLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    final TypedArray array = context
    .obtainStyledAttributes(attrs, R.styleable.FloatLabelLayout);

    final int sidePadding = array.getDimensionPixelSize(
    R.styleable.FloatLabelLayout_floatLabelSidePadding,
    dipsToPix(DEFAULT_PADDING_LEFT_RIGHT_DP));
    mLabel = new TextView(context);
    mLabel.setPadding(sidePadding, 0, sidePadding, 0);
    mLabel.setVisibility(INVISIBLE);

    mLabel.setTextAppearance(context,
    array.getResourceId(R.styleable.FloatLabelLayout_floatLabelTextAppearance,
    android.R.style.TextAppearance_Small));

    addView(mLabel, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

    array.recycle();
    }

    @Override
    public final void addView(View child, int index, ViewGroup.LayoutParams params) {
    if (child instanceof EditText) {
    // If we already have an EditText, throw an exception
    if (mEditText != null) {
    throw new IllegalArgumentException("We already have an EditText, can only have one");
    }

    // Update the layout params so that the EditText is at the bottom, with enough top
    // margin to show the label
    final LayoutParams lp = new LayoutParams(params);
    lp.gravity = Gravity.BOTTOM;
    lp.topMargin = (int) mLabel.getTextSize();
    params = lp;

    setEditText((EditText) child);
    }

    // Carry on adding the View...
    super.addView(child, index, params);
    }

    private void setEditText(EditText editText) {
    mEditText = editText;

    // Add a TextWatcher so that we know when the text input has changed
    mEditText.addTextChangedListener(new TextWatcher() {

    @Override
    public void afterTextChanged(Editable s) {
    if (TextUtils.isEmpty(s)) {
    // The text is empty, so hide the label if it is visible
    if (mLabel.getVisibility() == View.VISIBLE) {
    hideLabel();
    }
    } else {
    // The text is not empty, so show the label if it is not visible
    if (mLabel.getVisibility() != View.VISIBLE) {
    showLabel();
    }
    }
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
    }

    });

    // Add focus listener to the EditText so that we can notify the label that it is activated.
    // Allows the use of a ColorStateList for the text color on the label
    mEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
    @Override
    public void onFocusChange(View view, boolean focused) {
    mLabel.setActivated(focused);
    }
    });

    mLabel.setText(mEditText.getHint());
    }

    /**
    * @return the {@link android.widget.EditText} text input
    */
    public EditText getEditText() {
    return mEditText;
    }

    /**
    * @return the {@link android.widget.TextView} label
    */
    public TextView getLabel() {
    return mLabel;
    }

    /**
    * Show the label using an animation
    */
    private void showLabel() {
    mLabel.setVisibility(View.VISIBLE);
    mLabel.setAlpha(0f);
    mLabel.setTranslationY(mLabel.getHeight());
    mLabel.animate()
    .alpha(1f)
    .translationY(0f)
    .setDuration(ANIMATION_DURATION)
    .setListener(null).start();
    }

    /**
    * Hide the label using an animation
    */
    private void hideLabel() {
    mLabel.setAlpha(1f);
    mLabel.setTranslationY(0f);
    mLabel.animate()
    .alpha(0f)
    .translationY(mLabel.getHeight())
    .setDuration(ANIMATION_DURATION)
    .setListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
    mLabel.setVisibility(View.GONE);
    }
    }).start();
    }

    /**
    * Helper method to convert dips to pixels.
    */
    private int dipsToPix(float dps) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dps,
    getResources().getDisplayMetrics());
    }
    }
    25 changes: 25 additions & 0 deletions attrs.xml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    <!--
    Copyright (C) 2014 Chris Banes
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
    -->

    <?xml version="1.0" encoding="utf-8"?>
    <resources>

    <declare-styleable name="FloatLabelLayout">
    <attr name="floatLabelTextAppearance" format="reference" />
    <attr name="floatLabelSidePadding" format="reference|dimension" />
    </declare-styleable>

    </resources>