Commit 0335d67d authored by Mygod's avatar Mygod

Add animation for bottom bar during Snackbar transitions

Fix #1036 the one-year-old issue.

There are still some subtle blackness during the exit animation. Unfortunately this is something I am not able to fix as I don't and probably won't ever have enough knowledge about Android's drawing passes. Anyway this still looks much better than before.
parent 0d0c2d59
package android.support.design.widget
import android.view.animation.Interpolator
object SnackbarAnimation {
val FAST_OUT_SLOW_IN_INTERPOLATOR: Interpolator get() = AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR
const val ANIMATION_DURATION = BaseTransientBottomBar.ANIMATION_DURATION.toLong()
}
/*******************************************************************************
* *
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
package com.github.shadowsocks.widget
import android.content.Context
import android.support.design.widget.CoordinatorLayout
import android.util.AttributeSet
import android.view.View
import com.github.shadowsocks.R
class BottomMarginBehavior : CoordinatorLayout.Behavior<View> {
constructor() : super()
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean =
dependency.id == R.id.stat // sorry I'm too lazy to write an attribute
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
(child.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = (parent.height - dependency.y).toInt()
return true
}
}
/*******************************************************************************
* *
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
package com.github.shadowsocks.widget
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.os.Build
import android.support.design.widget.CoordinatorLayout
import android.support.design.widget.SnackbarAnimation
import android.support.design.widget.Snackbar
import android.support.v4.view.ViewCompat
import android.util.AttributeSet
import android.view.View
/**
* Full credits go to: https://stackoverflow.com/a/35904421/2245107
*/
class MoveUpwardBehavior : CoordinatorLayout.Behavior<View> {
constructor() : super()
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean =
dependency is Snackbar.SnackbarLayout
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
child.translationY = Math.min(0f, dependency.translationY - dependency.height)
return true
}
/**
* Based on BaseTransientBottomBar.animateViewOut (support lib 27.0.2).
*/
override fun onDependentViewRemoved(parent: CoordinatorLayout, child: View, dependency: View) {
val animator = ValueAnimator()
val start = child.translationY
animator.setFloatValues(start, 0f)
animator.interpolator = SnackbarAnimation.FAST_OUT_SLOW_IN_INTERPOLATOR
animator.duration = SnackbarAnimation.ANIMATION_DURATION
animator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
private var previousValue = start
override fun onAnimationUpdate(animator: ValueAnimator) {
val currentValue = animator.animatedValue as Float
if (Build.VERSION.SDK_INT > 19) child.translationY = currentValue
else ViewCompat.offsetTopAndBottom(child, (currentValue - previousValue).toInt())
previousValue = currentValue
}
})
animator.start()
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:layout_width="match_parent" android:layout_height="match_parent"
tools:ignore="UnknownIdInLayout" android:clipChildren="false">
<FrameLayout android:id="@+id/fragment_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/stat"/>
<LinearLayout android:id="@+id/stat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
android:paddingStart="16dip"
android:paddingEnd="16dip"
android:paddingTop="16dip"
android:paddingBottom="16dip"
android:focusable="true"
android:background="@drawable/background_stat"
android:orientation="vertical"
android:nextFocusRight="@+id/fab"
android:layout_above="@+id/snackbar">
<TextView android:id="@+id/status"
android:textSize="16sp"
android:textColor="?android:attr/textColorPrimary"
android:ellipsize="marquee"
android:paddingEnd="72dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="RtlSymmetry"/>
<com.github.shadowsocks.widget.BoundedGridLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
app:bounded_width="288dp"
app:columnCount="4">
<TextView app:layout_column="0" style="@style/TextAppearance.AppCompat.Body2"
android:ellipsize="marquee" android:text="@string/sent"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:gravity="end"/>
<TextView app:layout_column="1" android:id="@+id/tx" android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
android:layout_width="0dp" android:layout_height="wrap_content"
app:layout_columnWeight="1" android:layout_gravity="fill_horizontal"/>
<TextView app:layout_column="2" android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:ellipsize="marquee" android:text="▲"
android:layout_width="wrap_content" android:layout_height="wrap_content"/>
<TextView app:layout_column="3" android:id="@+id/txRate" android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
android:layout_width="0dp" android:layout_height="wrap_content" app:layout_columnWeight="1"
android:layout_gravity="fill_horizontal" android:gravity="end"/>
<TextView app:layout_row="1" app:layout_column="0" style="@style/TextAppearance.AppCompat.Body2"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:gravity="end"
android:ellipsize="marquee" android:text="@string/received"/>
<TextView app:layout_row="1" app:layout_column="1" android:id="@+id/rx" android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
android:layout_width="0dp" android:layout_height="wrap_content" app:layout_columnWeight="1"
android:layout_gravity="fill_horizontal" />
<TextView app:layout_row="1" app:layout_column="2" android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="▼"/>
<TextView app:layout_row="1" app:layout_column="3" android:id="@+id/rxRate" android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
android:layout_width="0dp" android:layout_height="wrap_content" app:layout_columnWeight="1"
android:layout_gravity="fill_horizontal" android:gravity="end"/>
<android.support.design.widget.CoordinatorLayout
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"
android:id="@+id/snackbar"
android:background="@color/material_primary_700"
android:clipChildren="false"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/fragment_holder"
android:background="?android:attr/windowBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.github.shadowsocks.widget.BottomMarginBehavior"/>
<LinearLayout
android:id="@+id/stat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/background_stat"
android:elevation="4dp"
android:focusable="true"
android:nextFocusRight="@+id/fab"
android:orientation="vertical"
android:paddingBottom="16dip"
android:paddingEnd="16dip"
android:paddingStart="16dip"
android:paddingTop="16dip"
android:layout_gravity="bottom"
app:layout_behavior="com.github.shadowsocks.widget.MoveUpwardBehavior">
<TextView
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:paddingEnd="72dip"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
tools:ignore="RtlSymmetry"/>
<com.github.shadowsocks.widget.BoundedGridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:bounded_width="288dp"
app:columnCount="4">
<TextView
style="@style/TextAppearance.AppCompat.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:ellipsize="marquee"
android:gravity="end"
android:text="@string/sent"
app:layout_column="0"/>
<TextView
android:id="@+id/tx"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="fill_horizontal"
android:ellipsize="marquee"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
app:layout_column="1"
app:layout_columnWeight="1"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:text="▲"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
app:layout_column="2"/>
<TextView
android:id="@+id/txRate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="fill_horizontal"
android:ellipsize="marquee"
android:gravity="end"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
app:layout_column="3"
app:layout_columnWeight="1"/>
<TextView
style="@style/TextAppearance.AppCompat.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:ellipsize="marquee"
android:gravity="end"
android:text="@string/received"
app:layout_column="0"
app:layout_row="1"/>
<TextView
android:id="@+id/rx"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="fill_horizontal"
android:ellipsize="marquee"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
app:layout_column="1"
app:layout_columnWeight="1"
app:layout_row="1"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:text="▼"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
app:layout_column="2"
app:layout_row="1"/>
<TextView
android:id="@+id/rxRate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="fill_horizontal"
android:ellipsize="marquee"
android:gravity="end"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
app:layout_column="3"
app:layout_columnWeight="1"
app:layout_row="1"/>
</com.github.shadowsocks.widget.BoundedGridLayout>
</LinearLayout>
......@@ -72,11 +142,11 @@
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignTop="@+id/stat"
android:layout_marginEnd="16dp"
android:layout_marginTop="@dimen/fab_margin_top"
android:elevation="6dp">
android:elevation="6dp"
app:layout_anchor="@id/stat"
app:layout_anchorGravity="top|end">
<com.github.shadowsocks.widget.ServiceButton
android:id="@+id/fab"
android:layout_width="wrap_content"
......@@ -86,9 +156,4 @@
app:pressedTranslationZ="6dp"
app:srcCompat="@drawable/ic_service_idle"/>
</FrameLayout>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/snackbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment