/* * Copyright 2014 Oguz Bilgener */ package com.oguzdev.circularfloatingactionmenu.library; import android.app.Activity; import android.content.Context; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; import android.os.Build; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; /** * An alternative Floating Action Button implementation that can be independently placed in * one of 8 different places on the screen. */ public class FloatingActionButton extends FrameLayout { public static final int THEME_LIGHT = 0; public static final int THEME_DARK = 1; public static final int POSITION_TOP_CENTER = 1; public static final int POSITION_TOP_RIGHT = 2; public static final int POSITION_RIGHT_CENTER = 3; public static final int POSITION_BOTTOM_RIGHT = 4; public static final int POSITION_BOTTOM_CENTER = 5; public static final int POSITION_BOTTOM_LEFT = 6; public static final int POSITION_LEFT_CENTER = 7; public static final int POSITION_TOP_LEFT = 8; private View contentView; private boolean systemOverlay; /** * Constructor that takes parameters collected using {@link FloatingActionMenu.Builder} * @param context a reference to the current context * @param layoutParams * @param theme * @param backgroundDrawable * @param position * @param contentView * @param contentParams */ public FloatingActionButton(Context context, ViewGroup.LayoutParams layoutParams, int theme, Drawable backgroundDrawable, int position, View contentView, FrameLayout.LayoutParams contentParams, boolean systemOverlay) { super(context); this.systemOverlay = systemOverlay; if(!systemOverlay && !(context instanceof Activity)) { throw new RuntimeException("Given context must be an instance of Activity, " +"since this FAB is not a systemOverlay."); } setPosition(position, layoutParams); // If no custom backgroundDrawable is specified, use the background drawable of the theme. if(backgroundDrawable == null) { if(theme == THEME_LIGHT) backgroundDrawable = context.getResources().getDrawable(R.drawable.button_action_selector); else backgroundDrawable = context.getResources().getDrawable(R.drawable.button_action_dark_selector); } setBackgroundResource(backgroundDrawable); if(contentView != null) { setContentView(contentView, contentParams); } setClickable(true); attach(layoutParams); } /** * Sets the position of the button by calculating its Gravity from the position parameter * @param position one of 8 specified positions. * @param layoutParams should be either FrameLayout.LayoutParams or WindowManager.LayoutParams */ public void setPosition(int position, ViewGroup.LayoutParams layoutParams) { boolean setDefaultMargin = false; int gravity; switch (position) { case POSITION_TOP_CENTER: gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; break; case POSITION_TOP_RIGHT: gravity = Gravity.TOP | Gravity.RIGHT; break; case POSITION_RIGHT_CENTER: gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL; break; case POSITION_BOTTOM_CENTER: gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; break; case POSITION_BOTTOM_LEFT: gravity = Gravity.BOTTOM | Gravity.LEFT; break; case POSITION_LEFT_CENTER: gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL; break; case POSITION_TOP_LEFT: gravity = Gravity.TOP | Gravity.LEFT; break; case POSITION_BOTTOM_RIGHT: default: setDefaultMargin = true; gravity = Gravity.BOTTOM | Gravity.RIGHT; break; } if(!systemOverlay) { try { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) layoutParams; lp.gravity = gravity; setLayoutParams(lp); } catch (ClassCastException e) { throw new ClassCastException("layoutParams must be an instance of " + "FrameLayout.LayoutParams, since this FAB is not a systemOverlay"); } } else { try { WindowManager.LayoutParams lp = (WindowManager.LayoutParams) layoutParams; lp.gravity = gravity; if(setDefaultMargin) { int margin = getContext().getResources().getDimensionPixelSize(R.dimen.action_button_margin); lp.x = margin; lp.y = margin; } setLayoutParams(lp); } catch(ClassCastException e) { throw new ClassCastException("layoutParams must be an instance of " + "WindowManager.LayoutParams, since this FAB is a systemOverlay"); } } } /** * Sets a content view that will be displayed inside this FloatingActionButton. * @param contentView */ public void setContentView(View contentView, FrameLayout.LayoutParams contentParams) { this.contentView = contentView; FrameLayout.LayoutParams params; if(contentParams == null ){ params = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER); final int margin = getResources().getDimensionPixelSize(R.dimen.action_button_content_margin); params.setMargins(margin, margin, margin, margin); } else { params = contentParams; } params.gravity = Gravity.CENTER; contentView.setClickable(false); this.addView(contentView, params); } /** * Attaches it to the content view with specified LayoutParams. * @param layoutParams */ public void attach(ViewGroup.LayoutParams layoutParams) { if(systemOverlay) { try { getWindowManager().addView(this, layoutParams); } catch(SecurityException e) { throw new SecurityException("Your application must have SYSTEM_ALERT_WINDOW " + "permission to create a system window."); } } else { ((ViewGroup) getActivityContentView()).addView(this, layoutParams); } } /** * Detaches it from the container view. */ public void detach() { if(systemOverlay) { getWindowManager().removeView(this); } else { ((ViewGroup) getActivityContentView()).removeView(this); } } /** * Finds and returns the main content view from the Activity context. * @return the main content view */ public View getActivityContentView() { try { return ((Activity) getContext()).getWindow().getDecorView().findViewById(android.R.id.content); } catch(ClassCastException e) { throw new ClassCastException("Please provide an Activity context for this FloatingActionButton."); } } public WindowManager getWindowManager() { return (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); } private void setBackgroundResource(Drawable drawable) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { setBackground(drawable); } else { setBackgroundDrawable(drawable); } } /** * A builder for {@link FloatingActionButton} in conventional Java Builder format */ public static class Builder { private Context context; private ViewGroup.LayoutParams layoutParams; private int theme; private Drawable backgroundDrawable; private int position; private View contentView; private LayoutParams contentParams; private boolean systemOverlay; public Builder(Context context) { this.context = context; // Default FloatingActionButton settings int size = context.getResources().getDimensionPixelSize(R.dimen.action_button_size); int margin = context.getResources().getDimensionPixelSize(R.dimen.action_button_margin); FrameLayout.LayoutParams layoutParams = new LayoutParams(size, size, Gravity.BOTTOM | Gravity.RIGHT); layoutParams.setMargins(margin, margin, margin, margin); setLayoutParams(layoutParams); setTheme(FloatingActionButton.THEME_LIGHT); setPosition(FloatingActionButton.POSITION_BOTTOM_RIGHT); setSystemOverlay(false); } public Builder setLayoutParams(ViewGroup.LayoutParams params) { this.layoutParams = params; return this; } public Builder setTheme(int theme) { this.theme = theme; return this; } public Builder setBackgroundDrawable(Drawable backgroundDrawable) { this.backgroundDrawable = backgroundDrawable; return this; } public Builder setBackgroundDrawable(int drawableId) { return setBackgroundDrawable(context.getResources().getDrawable(drawableId)); } public Builder setPosition(int position) { this.position = position; return this; } public Builder setContentView(View contentView) { return setContentView(contentView, null); } public Builder setContentView(View contentView, LayoutParams contentParams) { this.contentView = contentView; this.contentParams = contentParams; return this; } public Builder setSystemOverlay(boolean systemOverlay) { this.systemOverlay = systemOverlay; return this; } public FloatingActionButton build() { return new FloatingActionButton(context, layoutParams, theme, backgroundDrawable, position, contentView, contentParams, systemOverlay); } public static WindowManager.LayoutParams getDefaultSystemWindowParams(Context context) { int size = context.getResources().getDimensionPixelSize(R.dimen.action_button_size); WindowManager.LayoutParams params = new WindowManager.LayoutParams( size, size, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, // z-ordering WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); params.format = PixelFormat.RGBA_8888; params.gravity = Gravity.TOP | Gravity.LEFT; return params; } } }