Note: Full working code is available at https://github.com/mwajeeh/AnimationsPlayground
More explanation in this post: https://medium.com/p/implementing-complex-animations-in-android-full-working-code-41979cc2369e
The card expand animation can be done using Shared element transition. I am going to give you enough material to start implementation of opening of categories and collapsing of pager indicators.
I am assuming that category headings are in RecyclerView
. When a category is tapped, open the detail activity with shared element transitions like this:
int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
List<Pair<View, String>> pairs = new ArrayList<Pair<View, String>>();
for (int i = firstVisibleItemPosition; i <= lastVisibleItemPosition; i++) {
ViewHolder holderForAdapterPosition = (ViewHolder) list.findViewHolderForAdapterPosition(i);
View itemView = holderForAdapterPosition.image;
pairs.add(Pair.create(itemView, "tab_" + i));
}
Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, pairs.toArray(new Pair[]{})).toBundle();
//launch detail activity on tap
Intent intent = new Intent(context, DetailActivity.class);
context.startActivity(intent, bundle);
In the next activity I have modified version of https://github.com/JakeWharton/ViewPagerIndicator/blob/master/library/src/com/viewpagerindicator/IconPageIndicator.java
Only these two methods are modified :
public void notifyDataSetChanged() {
mIconsLayout.removeAllViews();
IconPagerAdapter iconAdapter = (IconPagerAdapter) mViewPager.getAdapter();
int count = iconAdapter.getCount();
LayoutInflater inflater = LayoutInflater.from(getContext());
for (int i = 0; i < count; i++) {
View parent = inflater.inflate(R.layout.indicator, mIconsLayout, false);
ImageView view = (ImageView) parent.findViewById(R.id.icon);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// must be same as off previous activity
view.setTransitionName("tab_" + i);
}
view.setImageResource(iconAdapter.getIconResId(i));
mIconsLayout.addView(parent);
}
if (mSelectedIndex > count) {
mSelectedIndex = count - 1;
}
setCurrentItem(mSelectedIndex);
requestLayout();
}
@Override
public void setCurrentItem(int item) {
if (mViewPager == null) {
throw new IllegalStateException("ViewPager has not been bound.");
}
mSelectedIndex = item;
mViewPager.setCurrentItem(item);
int tabCount = mIconsLayout.getChildCount();
for (int i = 0; i < tabCount; i++) {
View child = mIconsLayout.getChildAt(i);
boolean isSelected = (i == item);
child.setSelected(isSelected);
View foreground = child.findViewById(R.id.foreground);
if (isSelected) {
animateToIcon(item);
foreground.setBackgroundDrawable(ContextCompat.getDrawable(getContext(), R.drawable.fg_white));
} else {
foreground.setBackgroundDrawable(ContextCompat.getDrawable(getContext(), R.drawable.fg_gray));
}
}
}
并添加了此方法IconPageIndicator
以支持指标崩溃:
public void collapse(float top, float total) {
//do not scale to 0
float newTop = top / 1.2F;
float scale = (total - newTop) / (float) total;
ViewCompat.setScaleX(this, scale);
ViewCompat.setScaleY(this, scale);
int tabCount = mIconsLayout.getChildCount();
float alpha = (total - top) / (float) total;
for (int i = 0; i < tabCount; i++) {
View parent = mIconsLayout.getChildAt(i);
View child = parent.findViewById(R.id.foreground);
ViewCompat.setAlpha(child, 1 - alpha);
}
}
在onCreate()
中DetailActivity
,推迟过渡直到指示器布置完毕。
supportPostponeEnterTransition();
indicator.post(new Runnable() {
@Override
public void run() {
supportStartPostponedEnterTransition();
}
});
其他相关重要文件:
activity_detail.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/black"
app:elevation="0dp">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="0dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<!--dummy view-->
<View
android:layout_width="match_parent"
android:layout_height="200dp"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@android:color/transparent"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:elevation="0dp"
app:layout_collapseMode="pin"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<com.example.mwajeeh.animations.IconPageIndicator
android:id="@+id/indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:centered="true"
app:layout_behavior="com.example.mwajeeh.animations.CollapsingIndicatorBehaviour"/>
<!--add fragments in this view pager with RecyclerView as root and app:layout_behavior="@string/appbar_scrolling_view_behavior"-->
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
CollapsingIndicatorBehaviour.java
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.WindowInsetsCompat;
import android.util.AttributeSet;
import android.view.View;
public class CollapsingIndicatorBehaviour extends ViewOffsetBehavior<IconPageIndicator> {
private static final String TAG = "CollapsingIndicatorBehaviour";
private WindowInsetsCompat lastInsets;
public CollapsingIndicatorBehaviour() {
}
public CollapsingIndicatorBehaviour(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, IconPageIndicator child, View dependency) {
return dependency instanceof AppBarLayout;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, IconPageIndicator child, View dependency) {
//keep child centered inside dependency respecting android:fitsSystemWindows="true"
int systemWindowInsetTop = 0;
if (lastInsets != null) {
systemWindowInsetTop = lastInsets.getSystemWindowInsetTop();
}
int bottom = dependency.getBottom();
float center = (bottom - systemWindowInsetTop) / 2F;
float halfChild = child.getHeight() / 2F;
setTopAndBottomOffset((int) (center + systemWindowInsetTop - halfChild));
if (dependency instanceof AppBarLayout) {
float totalScrollRange = ((AppBarLayout) dependency).getTotalScrollRange();
child.collapse(-dependency.getTop(), totalScrollRange);
}
return true;
}
@NonNull
@Override
public WindowInsetsCompat onApplyWindowInsets(CoordinatorLayout coordinatorLayout, IconPageIndicator child, WindowInsetsCompat insets) {
lastInsets = insets;
return super.onApplyWindowInsets(coordinatorLayout, child, insets);
}
}
将android.support.design.widget.ViewOffsetBehavior.java和android.support.design.widget.ViewOffsetHelper复制到设计库中的代码中。这些文件不是公开的,但是我们需要它们才能使行为正常工作。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句