FloatingActionButton 展开成一个新的活动
Posted
技术标签:
【中文标题】FloatingActionButton 展开成一个新的活动【英文标题】:FloatingActionButton expand into a new activity 【发布时间】:2015-10-15 23:26:16 【问题描述】:在 android 材料设计原则页面上,其中一个示例显示 FAB 扩展到新的全屏。 (在“全屏”下)
http://www.google.com/design/spec/components/buttons-floating-action-button.html#buttons-floating-action-button-transitions
我尝试在我的应用中实现相同的效果,但收效甚微。 我设法使用以下代码作为参考创建了一个扩展为视图的 FAB:https://gist.github.com/chris95x8/882b5c5d0aa2096236ba。
它有效,但我想知道是否可以将相同的效果应用于活动转换。我试过自己查找并玩它,但找不到任何可能有用的东西。
我知道我可以将 FAB 扩展为一个片段,而不是一个全新的活动,但我不确定这是否是正在做的事情,以及这是否是最佳的。
所以我的问题是,有没有办法将 fab-expanding 显示效果实现为活动转换,还是应该只是显示一个新片段?
【问题讨论】:
您在材料设计指南中链接的内容绝对是Activity
过渡。使用Fragment
完全没有意义。
我从 Github 下载了你的代码。我没有看到任何与浮动操作按钮相关的代码。它在哪里。我认为您应该发布相关代码而不是让人们下载它们(这很麻烦)。并且很少/有一些解释。
回答您的问题,据我所知,浮动操作按钮与特定片段或活动无关。您想要上面的示例项目吗?
@TheOriginalAndroid 感谢您的评论!正如我所说,这不是我的代码,而是我用作参考的代码。我正在寻找的是使用与我发布的 github 链接中的视图相同的动画,仅作为活动转换。我找不到这样的例子,我对android的了解还不足以达到那个效果。
很抱歉,代码参考对读者没有帮助,反而会误导读者。谷歌的网页和动画就足够了。但是我不知道您现有的代码是什么样的,如果有的话。无论如何,我发布了一个答案开始。由于时间限制,希望我们能在这方面迅速取得进展。
【参考方案1】:
我正在开发一个将FloatingActionButton
扩展为新Activity
的应用程序。我不确定你是否喜欢我的实现,但请先看图片:
所以第一张图片显示MainActivity
,最后一张显示SecondActivity
,这是从FAB“扩展”而来的。
现在,我想提一下 我实际上并没有扩展一个 FAB 到一个新的 Activity
,但我可以让用户感觉到新页面是从那个 FAB 扩展的,我认为这对开发者和用户来说都足够了。
这里是实现:
准备工作:
-
当然是
FloatingActionButton
,
访问https://github.com/kyze8439690/RevealLayout 并将此库导入您的项目。它用于播放显示动画。它有一个自定义的BakedBezierInterpolator
来控制显示动画并使其具有材质样式。
步骤:
像这样创建activity_main.xml:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_>
<!--Your main content here-->
<RevealLayout
android:id="@+id/reveal_layout"
android:layout_
android:layout_
android:visibility="invisible">
<View
android:id="@+id/reveal_view"
android:layout_
android:layout_
android:visibility="invisible"/>
</RevealLayout>
</FrameLayout>
查找视图:
mRevealLayout = (RevealLayout) findViewById(R.id.reveal_layout);
mRevealView = findViewById(R.id.reveal_view);
当用户点击 FAB 时展开:
mFab.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
mFab.setClickable(false); // Avoid naughty guys clicking FAB again and again...
int[] location = new int[2];
mFab.getLocationOnScreen(location);
location[0] += mFab.getWidth() / 2;
location[1] += mFab.getHeight() / 2;
final Intent intent = new Intent(MainActivity.this, SecondActivity.class);
mRevealView.setVisibility(View.VISIBLE);
mRevealLayout.setVisibility(View.VISIBLE);
mRevealLayout.show(location[0], location[1]); // Expand from center of FAB. Actually, it just plays reveal animation.
mFab.postDelayed(new Runnable()
@Override
public void run()
startActivity(intent);
/**
* Without using R.anim.hold, the screen will flash because of transition
* of Activities.
*/
overridePendingTransition(0, R.anim.hold);
, 600); // 600 is default duration of reveal animation in RevealLayout
mFab.postDelayed(new Runnable()
@Override
public void run()
mFab.setClickable(true);
mRevealLayout.setVisibility(View.INVISIBLE);
mViewToReveal.setVisibility(View.INVISIBLE);
, 960); // Or some numbers larger than 600.
);
这里是 res/anim 中的 hold.xml:
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="960" <!-- Enough-large time is OK -->
android:fromXDelta="0%"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="0%"/>
</set>
就是这样。
改进:
RevealLayout
对于 API 17(Android 4.2) 下的设备有一个错误(播放矩形而不是圆形显示动画),你可以在它的构造函数中添加这些行:
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2)
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
如果您的SecondActivity
包含复杂的内容,那么在layout.xml 中用作reveal_view 的简单View
是不够的/完美的。您可以在RevealLayout
reveal_layout 中包含第二个布局。如果第二个布局每次都不相同,这似乎很浪费且难以控制。但对我来说,会的。因此,如果需要,您可以进行其他改进。
如果要实现与 Material Design Guide 中显示的完全相同的动画,可以将 RevealLayout
的 layout_height 设置为特定数字,而不是 match_parent。展开动画结束后(或动画播放后一段时间,应该使动画的整个过程流畅),然后你可以动画translationY。重要的一点是,通过控制动画时长在视觉上欺骗用户。
最后,这是我自己的经验/尝试,我是开发 Android 应用程序的初学者。 如果有任何错误/进一步改进,请留下 cmets/编辑我的答案。谢谢。
【讨论】:
这是一个有趣的库,您在详细的帖子上做得很好。然而,实现这一点似乎需要做很多工作,尤其是与使用 FragmentTransactions 和动画方法相比。 @The Original Android 是的,我同意。但是,当您选择使用片段展开时,很难让您将动画展开全屏。您只能在状态栏下方和导航栏上方为更改设置动画。或许你可以将style设置为windowTranslucentStatus/Navigation,这样每一个动画都可以真正的全屏显示,但是会导致“adjustResize不起作用”等其他问题,可能无法解决。另一方面,问题是“FAB 扩展为 Activity”----作者确实说过他想扩展为 Activity 而不是 Fragment,这是他的要求。 @ywwynm 感谢您的帖子,我考虑过以与您相同的方式实现效果,但正如您所说,这是一种肮脏的方式,我觉得很令人失望。我一直在寻找可能已将效果实现为活动转换但找不到任何应用程序,并希望有一种干净的 SDK-ish 方式来做到这一点。不幸的是,我想要么做你所做的,要么使用带有片段的单个活动是要走的路。感谢您抽出宝贵时间回答! 很抱歉 RevealLayout 有硬件加速错误,因为我在 Genymotion 上测试了较低的 api 版本,它似乎有效。我会尽快修复它。 @yugy 感谢您的简单而华丽的图书馆。当我回答这个问题时,我并不太关心开源世界,所以我忘了向你提交一个问题。很抱歉。【参考方案2】:我根据这个问题 Circular reveal transition for new activity 制作了一个自定义活动,它在活动完成时处理 CircularRevealAnimation 和他的反向效果:
public class RevealActivity extends AppCompatActivity
private View revealView;
public static final String REVEAL_X="REVEAL_X";
public static final String REVEAL_Y="REVEAL_Y";
public void showRevealEffect(Bundle savedInstanceState, final View rootView)
revealView=rootView;
if (savedInstanceState == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
rootView.setVisibility(View.INVISIBLE);
ViewTreeObserver viewTreeObserver = rootView.getViewTreeObserver();
if(viewTreeObserver.isAlive())
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
@Override
public void onGlobalLayout()
circularRevealActivity(rootView);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
else
rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
);
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void circularRevealActivity(View rootView)
int cx = getIntent().getIntExtra(REVEAL_X, 0);
int cy = getIntent().getIntExtra(REVEAL_Y, 0);
float finalRadius = Math.max(rootView.getWidth(), rootView.getHeight());
// create the animator for this view (the start radius is zero)
Animator circularReveal = ViewAnimationUtils.createCircularReveal(rootView, cx, cy, 0, finalRadius);
circularReveal.setDuration(400);
// make the view visible and start the animation
rootView.setVisibility(View.VISIBLE);
circularReveal.start();
@Override
public boolean onOptionsItemSelected(MenuItem item)
switch (item.getItemId())
case android.R.id.home: onBackPressed();break;
return super.onOptionsItemSelected(item);
@Override
public void onBackPressed()
destroyActivity(revealView);
private void destroyActivity(View rootView)
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP)
destroyCircularRevealActivity(rootView);
else
finish();
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void destroyCircularRevealActivity(final View rootView)
int cx = getIntent().getIntExtra(REVEAL_X, 0);
int cy = getIntent().getIntExtra(REVEAL_Y, 0);
float finalRadius = Math.max(rootView.getWidth(), rootView.getHeight());
// create the animator for this view (the start radius is zero)
Animator circularReveal = ViewAnimationUtils.createCircularReveal(rootView, cx, cy, finalRadius, 0);
circularReveal.setDuration(400);
circularReveal.addListener(new Animator.AnimatorListener()
@Override
public void onAnimationStart(Animator animator)
@Override
public void onAnimationEnd(Animator animator)
rootView.setVisibility(View.INVISIBLE);
finishAfterTransition();
@Override
public void onAnimationCancel(Animator animator)
@Override
public void onAnimationRepeat(Animator animator)
);
// make the view visible and start the animation
rootView.setVisibility(View.VISIBLE);
circularReveal.start();
您可以使用自己的活动来扩展它,并像这样调用 onCreate 方法“showRevealEffect”:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.your_activity_layout);
//your code
View root= findViewById(R.id.your_root_id);
showRevealEffect(savedInstanceState, root);
您还必须使用像这样的透明主题:
<style name="Theme.Transparent" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="colorControlNormal">@android:color/white</item>
</style>
最后,要启动此活动,您应该通过额外的坐标传递动画应该开始的位置:
int[] location = new int[2];
fab.getLocationOnScreen(location);
Intent intent = new Intent(this, YourRevealActivity.class);
intent.putExtra(SearchActivity.REVEAL_X, location[0]);
intent.putExtra(SearchActivity.REVEAL_Y, location[1]);
startActivity(intent);
【讨论】:
【参考方案3】:有人从Plaid调查了activity之间的transition实现。她的示例是通过https://github.com/hujiaweibujidao/FabDialogMorph 发布的。
简而言之,她通过以下方式过境两项活动:
-
FAB 作为共享元素。
目标 Activity 中的布局与 FAB 具有相同的
android:transitionName
。
为了平滑动画,实现并应用了MorphDrawable
(扩展自Drawable
)和MorphTransition
(扩展自ChangeBounds
)。
【讨论】:
【参考方案4】:你可以使用这个库 [https://github.com/sergiocasero/RevealFAB][1]
[1]: https://github.com/sergiocasero/RevealFAB 3rd 方,简单易用
添加到您的布局
<RelativeLayout...>
<android.support.design.widget.CoordinatorLayout...>
<!-- YOUR CONTENT -->
</android.support.design.widget.CoordinatorLayout>
<com.sergiocasero.revealfab.RevealFAB
android:id="@+id/reveal_fab"
android:layout_
android:layout_
app:fab_color="@color/colorAccent"
app:fab_icon="@drawable/ic_add_white_24dp"
app:reveal_color="@color/colorAccent" />
</RelativeLayout>
重要提示:此组件高于您的内容。如果需要,您可以使用 Coordinator、LinearLayout... 或其他相对布局:)
如您所见,您有 3 个自定义属性用于自定义颜色和图标
设置意图信息:
revealFAB = (RevealFAB) findViewById(R.id.reveal_fab);
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
revealFAB.setIntent(intent);
revealFAB.setOnClickListener(new RevealFAB.OnClickListener()
@Override
public void onClick(RevealFAB button, View v)
button.startActivityWithAnimation();
);
不要忘记调用 onResume() 方法!
@Override
protected void onResume()
super.onResume();
revealFAB.onResume();
【讨论】:
以上是关于FloatingActionButton 展开成一个新的活动的主要内容,如果未能解决你的问题,请参考以下文章