Добавляем Floating Action Button в свое Android приложение

в 10:01, , рубрики: floating action button, Разработка под android

В этом году на презентации Google I/O был представлен новая версия Android — L. Вместе с этим было представлено много новых плюшек для пользователей и разработчиков. Но одним из главных новшеств, несомненно, было новое решение Google для унификации дизайна — Material Design.

Одним из паттернов Material Design является Floating Action Button.

Что такое Floating Action Button ?

Google говорит, что это «специальный метод для способствования действию». Сама же кнопка имеет форму круга, плавающего над интерфейсом.

Добавляем Floating Action Button в свое Android приложение

Стоит отметить, что Floating Action Button должна отражать только главное действие в приложении.

Быстрая и грязная реализация

Я хотел создать быстрый способ добавления простейшей FAB для своих Android приложений с minSdkVersion = 14 (Ice Cream Sandwich). Я также реализовал анимацию появления/исчезновения и небольшие возможности для кастомизации кнопки.

Весь код доступен в Github Gist (добавьте этот класс в свой проект).

Для надежности продублирую код здесь

package  your_package;
 
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
 
public class FloatingActionButton extends View {
  Context context;
  Paint mButtonPaint;
  Paint mDrawablePaint;
  Bitmap mBitmap;
  boolean mHidden = false;
 
  public FloatingActionButton(Context context) {
    super(context);
    this.context = context;
    init(Color.WHITE);
  }
 
  public void setFloatingActionButtonColor(int FloatingActionButtonColor) {
    init(FloatingActionButtonColor);
  }
 
  public void setFloatingActionButtonDrawable(Drawable FloatingActionButtonDrawable) {
    mBitmap = ((BitmapDrawable) FloatingActionButtonDrawable).getBitmap();
    invalidate();
  }
 
  public void init(int FloatingActionButtonColor) {
    setWillNotDraw(false);
    setLayerType(View.LAYER_TYPE_SOFTWARE, null);
 
    mButtonPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mButtonPaint.setColor(FloatingActionButtonColor);
    mButtonPaint.setStyle(Paint.Style.FILL);
    mButtonPaint.setShadowLayer(10.0f, 0.0f, 3.5f, Color.argb(100, 0, 0, 0));
    mDrawablePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
    invalidate();
  }
 
  @Override
  protected void onDraw(Canvas canvas) {
    setClickable(true);
    canvas.drawCircle(getWidth() / 2, getHeight() / 2, (float) (getWidth() / 2.6), mButtonPaint);
    canvas.drawBitmap(mBitmap, (getWidth() - mBitmap.getWidth()) / 2,
        (getHeight() - mBitmap.getHeight()) / 2, mDrawablePaint);
  }
 
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_UP) {
      setAlpha(1.0f);
    } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
      setAlpha(0.6f);
    }
    return super.onTouchEvent(event);
  }
 
  public void hideFloatingActionButton() {
    if (!mHidden) {
      ObjectAnimator scaleX = ObjectAnimator.ofFloat(this, "scaleX", 1, 0);
      ObjectAnimator scaleY = ObjectAnimator.ofFloat(this, "scaleY", 1, 0);
      AnimatorSet animSetXY = new AnimatorSet();
      animSetXY.playTogether(scaleX, scaleY);
      animSetXY.setInterpolator(new AccelerateInterpolator());
      animSetXY.setDuration(100);
      animSetXY.start();
      mHidden = true;
    }
  }
 
  public void showFloatingActionButton() {
    if (mHidden) {
      ObjectAnimator scaleX = ObjectAnimator.ofFloat(this, "scaleX", 0, 1);
      ObjectAnimator scaleY = ObjectAnimator.ofFloat(this, "scaleY", 0, 1);
      AnimatorSet animSetXY = new AnimatorSet();
      animSetXY.playTogether(scaleX, scaleY);
      animSetXY.setInterpolator(new OvershootInterpolator());
      animSetXY.setDuration(200);
      animSetXY.start();
      mHidden = false;
    }
  }
 
  public boolean isHidden() {
    return mHidden;
  }
 
  static public class Builder {
    private FrameLayout.LayoutParams params;
    private final Activity activity;
    int gravity = Gravity.BOTTOM | Gravity.RIGHT; // default bottom right
    Drawable drawable;
    int color = Color.WHITE;
    int size = 0;
    float scale = 0;
 
    public Builder(Activity context) {
      scale = context.getResources().getDisplayMetrics().density;
      // The calculation (value * scale + 0.5f) is a widely used to convert to dps to pixel units
      // based on density scale
      // see developer.android.com (Supporting Multiple Screen Sizes)
      size = (int) (72 * scale + 0.5f); // default size is 72dp by 72dp
      params = new FrameLayout.LayoutParams(size, size);
      params.gravity = gravity;
 
      this.activity = context;
    }
 
    /**
     * Sets the gravity for the FAB
     */
    public Builder withGravity(int gravity) {
      this.gravity = gravity;
      return this;
    }
 
    /**
     * Sets the margins for the FAB in dp
     */
    public Builder withMargins(int left, int top, int right, int bottom) {
      params.setMargins((int) (left * scale + 0.5f), (int) (top * scale + 0.5f),
          (int) (right * scale + 0.5f), (int) (bottom * scale + 0.5f));
      return this;
    }
 
    /**
     * Sets the FAB drawable
     */
    public Builder withDrawable(final Drawable drawable) {
      this.drawable = drawable;
      return this;
    }
 
    /**
     * Sets the FAB color
     */
    public Builder withButtonColor(final int color) {
      this.color = color;
      return this;
    }
 
    /**
     * Sets the FAB size in dp
     */
    public Builder withButtonSize(int size) {
      size = (int) (size * scale + 0.5f);
      params = new FrameLayout.LayoutParams(size, size);
      return this;
    }
 
    public FloatingActionButton create() {
      final FloatingActionButton button = new FloatingActionButton(activity);
      button.setFloatingActionButtonColor(this.color);
      button.setFloatingActionButtonDrawable(this.drawable);
      params.gravity = this.gravity;
      ViewGroup root = (ViewGroup) activity.findViewById(android.R.id.content);
      root.addView(button, params);
      return button;
    }
  }
}

При создании кнопки в XML, я обнаружил некоторые трудности позиционирования View у нашей кнопки над остальными View (в частности, над Navigation Drawer). Я решил реализовать кнопку программно и работать посредством Builder-паттерна, что позволит размещать FAB выше других View в Activity при вызове .create().

Отлично! Но как мне добавить это в свое приложение ?

Добавить Floating Action Button очень даже просто:

FloatingActionButton fabButton = new FloatingActionButton.Builder(this) 
    .withDrawable(yourDrawable) 
    .withButtonColor(Color.WHITE) 
    .withGravity(Gravity.BOTTOM | Gravity.RIGHT) 
    .withMargins(0, 0, 16, 16) 
    .create();

Размер кнопки легко изменить посредством вызова .withButtonSize(int size). По умолчанию стоит 72dp.

Заключение

Похоже, что Google будет использовать этот паттерн во многих своих приложениях. И еще до сих пор нет никаких новостей о том, будет ли Google добавлять floating action button в support library, поэтому пока что не стесняйтесь использовать это решение.

Код на Github Gist

Автор: questman

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js