Skip to content

Instantly share code, notes, and snippets.

@frogermcs frogermcs/FABAnimation
Last active Aug 29, 2015

Embed
What would you like to do?
InstaMaterial source files
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary">
<ImageView
android:id="@+id/ivLogo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="center"
android:src="@drawable/img_toolbar_logo" />
</android.support.v7.widget.Toolbar>
<android.support.v7.widget.RecyclerView
android:id="@+id/rvFeed"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/toolbar"
android:scrollbars="none" />
<ImageButton
android:id="@+id/btnCreate"
android:layout_width="@dimen/btn_fab_size"
android:layout_height="@dimen/btn_fab_size"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="@dimen/btn_fab_margins"
android:layout_marginRight="@dimen/btn_fab_margins"
android:background="@drawable/btn_fab_default"
android:elevation="@dimen/default_elevation"
android:src="@drawable/ic_instagram_white"
android:textSize="28sp" />
</RelativeLayout>
// app/build.gradle
apply plugin: 'com.android.application'
apply plugin: 'hugo'
android {
compileSdkVersion 21
buildToolsVersion "21.1"
defaultConfig {
applicationId "io.github.froger.instamaterial"
minSdkVersion 15
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile "com.android.support:appcompat-v7:21.0.0"
compile 'com.android.support:support-v13:21.+'
compile 'com.android.support:support-v4:21.+'
compile 'com.android.support:palette-v7:+'
compile 'com.android.support:recyclerview-v7:+'
compile 'com.android.support:cardview-v7:21.0.+'
compile 'com.jakewharton:butterknife:5.1.2'
compile 'com.jakewharton.timber:timber:2.5.0'
compile 'com.facebook.rebound:rebound:0.3.6'
}
<?xml version="1.0" encoding="utf-8"?>
<!--drawable/btn_default_light.xml-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/btn_default_light_normal" android:state_focused="false" android:state_pressed="false" />
<item android:drawable="@color/btn_default_light_pressed" android:state_pressed="true" />
<item android:drawable="@color/btn_default_light_pressed" android:state_focused="true" />
</selector>
<?xml version="1.0" encoding="utf-8"?>
<!--drawable-v21/btn_default_light.xml-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/btn_default_light_pressed" />
<?xml version="1.0" encoding="utf-8"?>
<!--drawable/btn_fab_default.xml-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false">
<layer-list>
<item android:bottom="0dp" android:left="2dp" android:right="2dp" android:top="2dp">
<shape android:shape="oval">
<solid android:color="@color/fab_color_shadow" />
</shape>
</item>
<item android:bottom="2dp" android:left="2dp" android:right="2dp" android:top="2dp">
<shape android:shape="oval">
<solid android:color="@color/style_color_accent" />
</shape>
</item>
</layer-list>
</item>
<item android:state_pressed="true">
<shape android:bottom="2dp" android:left="2dp" android:right="2dp" android:shape="oval" android:top="2dp">
<solid android:color="@color/fab_color_pressed" />
</shape>
</item>
</selector>
<?xml version="1.0" encoding="utf-8"?>
<!--drawable-v21/btn_fab_default.xml-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/fab_color_shadow">
<item>
<shape android:shape="oval">
<solid android:color="@color/style_color_accent" />
</shape>
</item>
</ripple>
// build.gradle
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.14.0'
classpath 'com.jakewharton.hugo:hugo-plugin:1.1.+'
}
}
allprojects {
repositories {
jcenter()
}
}
<?xml version="1.0" encoding="utf-8"?>
<!--colors.xml-->
<resources>
<color name="style_color_primary">#2d5d82</color>
<color name="style_color_primary_dark">#21425d</color>
<color name="style_color_accent">#01bcd5</color>
<color name="fab_color_pressed">#007787</color>
<color name="fab_color_shadow">#44000000</color>
<color name="btn_default_light_normal">#00000000</color>
<color name="btn_default_light_pressed">#40ffffff</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<!--colors.xml-->
<resources>
<color name="style_color_primary">#2d5d82</color>
<color name="style_color_primary_dark">#21425d</color>
<color name="style_color_accent">#01bcd5</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<!--dimens.xml-->
<resources>
<dimen name="btn_fab_size">56dp</dimen>
<dimen name="btn_fab_margins">16dp</dimen>
<dimen name="default_elevation">8dp</dimen>
</resources>
//...
//FAB animation
private static final int ANIM_DURATION_FAB = 400;
private void startContentAnimation() {
btnCreate.animate()
.translationY(0)
.setInterpolator(new OvershootInterpolator(1.f))
.setStartDelay(300)
.setDuration(ANIM_DURATION_FAB)
.start();
feedAdapter.updateItems();
}
//...
public class FeedAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int ANIMATED_ITEMS_COUNT = 2;
private Context context;
private int lastAnimatedPosition = -1;
private int itemsCount = 0;
public FeedAdapter(Context context) {
this.context = context;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(context).inflate(R.layout.item_feed, parent, false);
return new CellFeedViewHolder(view);
}
private void runEnterAnimation(View view, int position) {
if (position >= ANIMATED_ITEMS_COUNT - 1) {
return;
}
if (position > lastAnimatedPosition) {
lastAnimatedPosition = position;
view.setTranslationY(Utils.getScreenHeight(context));
view.animate()
.translationY(0)
.setInterpolator(new DecelerateInterpolator(3.f))
.setDuration(700)
.start();
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
runEnterAnimation(viewHolder.itemView, position);
CellFeedViewHolder holder = (CellFeedViewHolder) viewHolder;
if (position % 2 == 0) {
holder.ivFeedCenter.setImageResource(R.drawable.img_feed_center_1);
holder.ivFeedBottom.setImageResource(R.drawable.img_feed_bottom_1);
} else {
holder.ivFeedCenter.setImageResource(R.drawable.img_feed_center_2);
holder.ivFeedBottom.setImageResource(R.drawable.img_feed_bottom_2);
}
}
@Override
public int getItemCount() {
return itemsCount;
}
public static class CellFeedViewHolder extends RecyclerView.ViewHolder {
@InjectView(R.id.ivFeedCenter)
SquaredImageView ivFeedCenter;
@InjectView(R.id.ivFeedBottom)
ImageView ivFeedBottom;
public CellFeedViewHolder(View view) {
super(view);
ButterKnife.inject(this, view);
}
}
public void updateItems() {
itemsCount = 10;
notifyDataSetChanged();
}
}
//FeedAdapter.java
public class FeedAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private int itemsCount = 10;
public FeedAdapter(Context context) {
this.context = context;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(context).inflate(R.layout.item_feed, parent, false);
return new CellFeedViewHolder(view);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
CellFeedViewHolder holder = (CellFeedViewHolder) viewHolder;
if (position % 2 == 0) {
holder.ivFeedCenter.setImageResource(R.drawable.img_feed_center_1);
holder.ivFeedBottom.setImageResource(R.drawable.img_feed_bottom_1);
} else {
holder.ivFeedCenter.setImageResource(R.drawable.img_feed_center_2);
holder.ivFeedBottom.setImageResource(R.drawable.img_feed_bottom_2);
}
}
@Override
public int getItemCount() {
return itemsCount;
}
public static class CellFeedViewHolder extends RecyclerView.ViewHolder {
@InjectView(R.id.ivFeedCenter)
SquaredImageView ivFeedCenter;
@InjectView(R.id.ivFeedBottom)
ImageView ivFeedBottom;
public CellFeedViewHolder(View view) {
super(view);
ButterKnife.inject(this, view);
}
}
}
<?xml version="1.0" encoding="utf-8"?><!-- item_feed.xml -->
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
card_view:cardCornerRadius="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/ic_feed_top" />
<io.github.froger.instamaterial.SquaredImageView
android:id="@+id/ivFeedCenter"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/ivFeedBottom"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v7.widget.CardView>
//MainActivity.java
public class MainActivity extends ActionBarActivity {
@InjectView(R.id.toolbar)
Toolbar toolbar;
@InjectView(R.id.rvFeed)
RecyclerView rvFeed;
private MenuItem inboxMenuItem;
private FeedAdapter feedAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
setupToolbar();
setupFeed();
}
private void setupToolbar() {
setSupportActionBar(toolbar);
toolbar.setNavigationIcon(R.drawable.ic_menu_white);
}
private void setupFeed() {
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
rvFeed.setLayoutManager(linearLayoutManager);
feedAdapter = new FeedAdapter(this);
rvFeed.setAdapter(feedAdapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
inboxMenuItem = menu.findItem(R.id.action_inbox);
inboxMenuItem.setActionView(R.layout.menu_item_view);
return true;
}
}
<?xml version="1.0" encoding="utf-8"?>
<!--menu_item_view.xml-->
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="?attr/actionBarSize"
android:layout_height="?attr/actionBarSize"
android:background="@drawable/btn_default_light"
android:src="@drawable/ic_inbox_white" />
<!--menu_main.xml-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<item
android:id="@+id/action_inbox"
android:icon="@drawable/ic_inbox_white"
android:title="Inbox"
app:showAsAction="always" />
</menu>
<?xml version="1.0" encoding="utf-8"?>
<!-- styles.xml-->
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/style_color_primary</item>
<item name="colorPrimaryDark">@color/style_color_primary_dark</item>
<item name="colorAccent">@color/style_color_accent</item>
</style>
</resources>
//...
private static final int ANIM_DURATION_TOOLBAR = 300;
private void startIntroAnimation() {
btnCreate.setTranslationY(2 * getResources().getDimensionPixelOffset(R.dimen.btn_fab_size));
int actionbarSize = Utils.dpToPx(56);
toolbar.setTranslationY(-actionbarSize);
ivLogo.setTranslationY(-actionbarSize);
inboxMenuItem.getActionView().setTranslationY(-actionbarSize);
toolbar.animate()
.translationY(0)
.setDuration(ANIM_DURATION_TOOLBAR)
.setStartDelay(300);
ivLogo.animate()
.translationY(0)
.setDuration(ANIM_DURATION_TOOLBAR)
.setStartDelay(400);
inboxMenuItem.getActionView().animate()
.translationY(0)
.setDuration(ANIM_DURATION_TOOLBAR)
.setStartDelay(500)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
startContentAnimation();
}
})
.start();
}
//...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.