Add initial implementation of a FloatingActionMenu for future use

This commit is contained in:
Jakob Nixdorf 2017-07-05 12:42:11 +02:00
parent 8a1b379b30
commit 975233829a
No known key found for this signature in database
GPG key ID: BE99BF86574A7DBC
13 changed files with 323 additions and 26 deletions

View file

@ -34,6 +34,7 @@ dependencies {
compile 'com.android.support:design:25.3.1'
compile 'com.android.support:recyclerview-v7:25.3.1'
compile 'com.android.support:cardview-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.journeyapps:zxing-android-embedded:3.0.3@aar'
compile 'com.google.zxing:core:3.2.1'
compile 'commons-codec:commons-codec:1.5'
@ -47,5 +48,4 @@ dependencies {
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
}

View file

@ -0,0 +1,130 @@
package org.shadowice.flocke.andotp;
import android.content.Context;
import android.support.constraint.ConstraintLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.view.ViewCompat;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
public class FloatingActionMenu {
private boolean isFabMenuOpen = false;
private ConstraintLayout mainLayout;
private Animation fabOpenAnimation;
private Animation fabCloseAnimation;
private FloatingActionButton baseFloatingActionButton;
private FloatingActionButton qrFAB;
private FloatingActionButton manualFAB;
private LinearLayout qrLayout;
private LinearLayout manualLayout;
private FABHandler fabHandler;
public FloatingActionMenu(Context context, ConstraintLayout mainLayout) {
this.mainLayout = mainLayout;
fabOpenAnimation = AnimationUtils.loadAnimation(context, R.anim.fab_open);
fabCloseAnimation = AnimationUtils.loadAnimation(context, R.anim.fab_close);
baseFloatingActionButton = (FloatingActionButton) mainLayout.findViewById(R.id.baseFloatingActionButton);
qrFAB = (FloatingActionButton) mainLayout.findViewById(R.id.qrFAB);
manualFAB = (FloatingActionButton) mainLayout.findViewById(R.id.manualFAB);
qrLayout = (LinearLayout) mainLayout.findViewById(R.id.qrLayout);
manualLayout = (LinearLayout) mainLayout.findViewById(R.id.manualLayout);
baseFloatingActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (isFabMenuOpen)
collapse();
else
expand();
}
});
qrFAB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (fabHandler != null)
fabHandler.onQRFabClick();
collapse();
}
});
manualFAB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (fabHandler != null)
fabHandler.onManualFabClick();
collapse();
}
});
}
public void setFABHandler(FABHandler fabHandler) {
this.fabHandler = fabHandler;
}
public void show() {
mainLayout.setVisibility(View.VISIBLE);
}
public void hide() {
mainLayout.setVisibility(View.GONE);
}
public void expand() {
ViewCompat.animate(baseFloatingActionButton)
.rotation(45F)
.withLayer()
.setDuration(300)
.setInterpolator(new LinearInterpolator())
.start();
qrLayout.setVisibility(View.VISIBLE);
manualLayout.setVisibility(View.VISIBLE);
qrLayout.startAnimation(fabOpenAnimation);
manualLayout.startAnimation(fabOpenAnimation);
qrFAB.setClickable(true);
manualFAB.setClickable(true);
isFabMenuOpen = true;
}
public void collapse() {
ViewCompat.animate(baseFloatingActionButton)
.rotation(0F)
.withLayer()
.setDuration(300)
.setInterpolator(new LinearInterpolator())
.start();
qrLayout.startAnimation(fabCloseAnimation);
manualLayout.startAnimation(fabCloseAnimation);
qrLayout.setVisibility(View.GONE);
manualLayout.setVisibility(View.GONE);
qrFAB.setClickable(false);
manualFAB.setClickable(false);
isFabMenuOpen = false;
}
public interface FABHandler {
void onQRFabClick();
void onManualFabClick();
}
}

View file

@ -36,7 +36,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.constraint.ConstraintLayout;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.MenuItemCompat;
@ -61,11 +61,9 @@ import com.google.zxing.integration.android.IntentIntegrator;
import org.shadowice.flocke.andotp.ItemTouchHelper.SimpleItemTouchHelperCallback;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private EntriesCardAdapter adapter;
private FloatingActionButton fab;
private FloatingActionMenu floatingActionMenu;
private SimpleItemTouchHelperCallback touchHelperCallback;
private Handler handler;
@ -242,12 +240,16 @@ public class MainActivity extends AppCompatActivity {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
fab = (FloatingActionButton) findViewById(R.id.action_scan);
fab.setOnClickListener(new View.OnClickListener() {
floatingActionMenu = new FloatingActionMenu(this, (ConstraintLayout) findViewById(R.id.fab_main_layout));
floatingActionMenu.setFABHandler(new FloatingActionMenu.FABHandler() {
@Override
public void onClick(View view) {
public void onQRFabClick() {
scanQRCode();
}
@Override
public void onManualFabClick() {
}
});
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
@ -382,14 +384,14 @@ public class MainActivity extends AppCompatActivity {
MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
fab.setVisibility(View.GONE);
floatingActionMenu.hide();
touchHelperCallback.setDragEnabled(false);
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
fab.setVisibility(View.VISIBLE);
floatingActionMenu.show();
touchHelperCallback.setDragEnabled(true);
return true;
}

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
android:duration="300"
android:fromXScale="1"
android:fromYScale="1"
android:interpolator="@android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0.0"
android:toYScale="0.0" />
<alpha
android:duration="300"
android:fromAlpha="1.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toAlpha="0.0" />
</set>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
android:duration="300"
android:fromXScale="0"
android:fromYScale="0"
android:interpolator="@android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1"
android:toYScale="1" />
<alpha
android:duration="300"
android:fromAlpha="0.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toAlpha="1.0" />
</set>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M9.4,10.5l4.77,-8.26C13.47,2.09 12.75,2 12,2c-2.4,0 -4.6,0.85 -6.32,2.25l3.66,6.35 0.06,-0.1zM21.54,9c-0.92,-2.92 -3.15,-5.26 -6,-6.34L11.88,9h9.66zM21.8,10h-7.49l0.29,0.5 4.76,8.25C21,16.97 22,14.61 22,12c0,-0.69 -0.07,-1.35 -0.2,-2zM8.54,12l-3.9,-6.75C3.01,7.03 2,9.39 2,12c0,0.69 0.07,1.35 0.2,2h7.49l-1.15,-2zM2.46,15c0.92,2.92 3.15,5.26 6,6.34L12.12,15L2.46,15zM13.73,15l-3.9,6.76c0.7,0.15 1.42,0.24 2.17,0.24 2.4,0 4.6,-0.85 6.32,-2.25l-3.66,-6.35 -0.93,1.6z"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/fab_small_label_corners" />
<solid android:color="@color/fab_small_label_background"/>
</shape>

View file

@ -44,12 +44,6 @@
<include layout="@layout/content_main" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/action_scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_add_white" />
<include layout="@layout/fab_menu" />
</android.support.design.widget.CoordinatorLayout>

View file

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/fab_main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
android:id="@+id/baseFloatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/fab_base_vertical_offset"
android:layout_marginEnd="@dimen/fab_base_horizontal_offset"
android:clickable="true"
android:tint="@android:color/white"
android:src="@drawable/ic_add_white"
app:fabSize="normal"
app:layout_constraintBottom_toBottomOf="@+id/fab_main_layout"
app:layout_constraintRight_toRightOf="@+id/fab_main_layout" />
<LinearLayout
android:id="@+id/qrLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/fab_small_vertical_offset"
android:layout_marginEnd="@dimen/fab_small_horizontal_offset"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/manualLayout"
app:layout_constraintRight_toRightOf="@+id/fab_main_layout">
<TextView
android:id="@+id/qrLabelTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/fab_small_label_offset"
android:background="@drawable/shape_fab_label"
android:padding="@dimen/fab_small_label_padding"
android:text="@string/title_scan_qr"
android:textColor="@android:color/white"
android:typeface="normal" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/qrFAB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_small_margin"
android:clickable="true"
android:tint="@android:color/white"
android:src="@drawable/ic_camera_white"
app:elevation="@dimen/fab_small_elevation"
app:fabSize="mini" />
</LinearLayout>
<LinearLayout
android:id="@+id/manualLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/fab_small_vertical_offset_base"
android:layout_marginEnd="@dimen/fab_small_horizontal_offset"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/baseFloatingActionButton"
app:layout_constraintRight_toRightOf="@+id/fab_main_layout" >
<TextView
android:id="@+id/manualLabelTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/fab_small_label_offset"
android:background="@drawable/shape_fab_label"
android:padding="@dimen/fab_small_label_padding"
android:text="@string/title_enter_details"
android:textColor="@android:color/white"
android:typeface="normal" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/manualFAB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_small_margin"
android:clickable="true"
android:tint="@android:color/white"
android:src="@drawable/ic_edit_white"
app:elevation="@dimen/fab_small_elevation"
app:fabSize="mini" />
</LinearLayout>
</android.support.constraint.ConstraintLayout>

View file

@ -1,11 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#388E3C</color>
<color name="colorPrimaryDark">#00600F</color>
<color name="primary_light">#CFD8DC</color>
<color name="colorAccent">#FFCC00</color>
<color name="primary_text">#212121</color>
<color name="secondary_text">#727272</color>
<color name="icons">#FFFFFF</color>
<color name="divider">#B6B6B6</color>
<color name="colorPrimary">#388E3C</color>
<color name="colorPrimaryDark">#00600F</color>
<color name="primary_light">#CFD8DC</color>
<color name="colorAccent">#FFCC00</color>
<color name="primary_text">#212121</color>
<color name="secondary_text">#727272</color>
<color name="icons">#FFFFFF</color>
<color name="divider">#B6B6B6</color>
<color name="fab_small_label_background">#212121</color>
</resources>

View file

@ -5,7 +5,18 @@
<dimen name="activity_margin_large">32dp</dimen>
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="card_corner_radius">0dp</dimen>
<!-- FAB Menu -->
<dimen name="fab_small_elevation">2dp</dimen>
<dimen name="fab_small_margin">8dp</dimen>
<dimen name="fab_small_horizontal_offset">16dp</dimen>
<dimen name="fab_small_vertical_offset">8dp</dimen>
<dimen name="fab_small_vertical_offset_base">16dp</dimen>
<dimen name="fab_small_label_corners">4dp</dimen>
<dimen name="fab_small_label_padding">4dp</dimen>
<dimen name="fab_small_label_offset">8dp</dimen>
<dimen name="fab_base_horizontal_offset">16dp</dimen>
<dimen name="fab_base_vertical_offset">16dp</dimen>
</resources>

View file

@ -32,6 +32,8 @@
<string name="msg_storage_not_accessible">External storage currently not accessible</string>
<string name="msg_storage_permissions">Storage permissions not granted</string>
<string name="title_enter_details">Enter details</string>
<string name="title_scan_qr">Scan QR-Code</string>
<string name="title_search">Search</string>
<!-- About dialog -->