从一个点开始增长动画显示DialogFragment

104

当用户在ListView中点击一行时,我正在显示一个DialogFragment。我想要动画展示对话框,使其从行的中心逐渐增大。在从启动器打开文件夹时可以看到类似的效果。

我想到了一种方式,即TranslateAnimationScaleAnimation的组合。还有其他方法吗?


1
请参考此链接了解DialogFragment上的动画。 - ASH
10个回答

196

由于DialogFragmentDialog类的包装器,因此您应该为基本Dialog设置一个主题,以获得所需的动画效果:

public class CustomDialogFragment extends DialogFragment implements OnEditorActionListener
{
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) 
    {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) 
    {
        // Set a theme on the dialog builder constructor!
        AlertDialog.Builder builder = 
            new AlertDialog.Builder( getActivity(), R.style.MyCustomTheme );
    
        builder  
        .setTitle( "Your title" )
        .setMessage( "Your message" )
        .setPositiveButton( "OK" , new DialogInterface.OnClickListener() 
            {      
              @Override
              public void onClick(DialogInterface dialog, int which) {
              dismiss();                  
            }
        });
        return builder.create();
    }
}

然后您只需要定义包含所需动画的主题即可。在 styles.xml 中添加自定义主题:

<style name="MyCustomTheme" parent="@android:style/Theme.Panel">
    <item name="android:windowAnimationStyle">@style/MyAnimation.Window</item>
</style>

<style name="MyAnimation.Window" parent="@android:style/Animation.Activity"> 
    <item name="android:windowEnterAnimation">@anim/anim_in</item>
    <item name="android:windowExitAnimation">@anim/anim_out</item>
</style>    

现在将动画文件添加到 res/anim 文件夹中:

(android:pivotY 是关键)

anim_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:interpolator="@android:anim/linear_interpolator"
        android:fromXScale="0.0"
        android:toXScale="1.0"
        android:fromYScale="0.0"
        android:toYScale="1.0"
        android:fillAfter="false"
        android:startOffset="200"
        android:duration="200" 
        android:pivotX = "50%"
        android:pivotY = "-90%"
    />
    <translate
        android:fromYDelta="50%"
        android:toYDelta="0"
        android:startOffset="200"
        android:duration="200"
    />
</set>

anim_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:interpolator="@android:anim/linear_interpolator"
        android:fromXScale="1.0"
        android:toXScale="0.0"
        android:fromYScale="1.0"
        android:toYScale="0.0"
        android:fillAfter="false"
        android:duration="200" 
        android:pivotX = "50%"        
        android:pivotY = "-90%"        
    />
    <translate
        android:fromYDelta="0"
        android:toYDelta="50%"
        android:duration="200"
    />
</set>

最后,在这里棘手的问题是让你的动画从每一行的中心开始增长。我假设该行在屏幕水平方向上填充,所以一方面android:pivotX值将是静态的。另一方面,您无法通过编程方式修改android:pivotY值。

我的建议是,您定义多个动画,每个动画在android:pivotY属性上有不同的百分比值(和多个引用这些动画的主题)。然后,当用户点击行时,计算该行在屏幕上的百分比Y位置。知道了百分比位置后,为对话框分配一个具有适当的android:pivotY值的主题。

这不是完美的解决方案,但可能对您有帮助。如果您不喜欢结果,那么我建议忘记DialogFragment,并动画一个简单的View从行的正中心开始增长。


在屏幕上为不同位置设置多个动画是个好主意。虽然这是一种hack方法,但似乎是唯一的解决办法。 - Edward Dale
你知道有没有可能在这些动画完成时获取回调吗?我想做一个两部分的动画,其中一个是对话框滑入,然后淡化视图。如何将监听器附加到windowAnimations? - android_student
2
设置主题可以像在onCreate(bundle)上执行setStyle(DialogFragment.STYLE_NORMAL, R.style.MyCustomTheme)一样简单。参见:https://developer.android.com/reference/android/app/DialogFragment.html - tropicalfish
转换API 21+有解决方案吗? - RexSplode
你的动画很棒,能否找到更多这样的XML文件(一种动画库)? - Ismail Iqbal
显示剩余3条评论

140

看看这段代码,对我有效。

// 上滑动画

<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" >

    <translate
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromYDelta="100%"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toXDelta="0" />

</set>

// 下滑动画

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <translate
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromYDelta="0%p"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toYDelta="100%p" />

</set>

// 样式

<style name="DialogAnimation">
    <item name="android:windowEnterAnimation">@anim/slide_up</item>
    <item name="android:windowExitAnimation">@anim/slide_down</item>
</style>

// 对话框片段内部

@Override
public void onActivityCreated(Bundle arg0) {
    super.onActivityCreated(arg0);
    getDialog().getWindow()
    .getAttributes().windowAnimations = R.style.DialogAnimation;
}

9
抱歉,这完全没有回答我的问题。 - Edward Dale
4
这可能并不完全符合提问者的要求,但我认为这是一个很好的起点。 - mdelolmo
3
很棒的例子!这是唯一对我起作用的示例。诀窍是在onActivityCreated(...)方法中设置动画/主题。 - tomurka
3
这可能有用,但完全没有回答问题。 - Olayinka
1
小调整: 在向上滑动的动画中,android:toXDelta="0" 应该改为 android:toYDelta="0"。 - Erik Melkersson
显示剩余5条评论

31

DialogFragment有一个公共的getTheme()方法,您可以覆盖它以实现这个确切的目的。该解决方案使用的代码行数较少:

public class MyCustomDialogFragment extends DialogFragment{
    ...
    @Override
    public int getTheme() {
        return R.style.MyThemeWithCustomAnimation;
    }
}

虽然与原问题无关,但我在MainActivity中展示了一个画廊,用户点击将激活DialogFragment中的幻灯片放映。然而,每当从幻灯片放映返回MainActivity时,都会出现抖动。这是因为MainActivity使用了AppTheme.NoActionBar,而DialogFragment使用默认的AppTheme。上述方法通过在片段和活动中具有一致的主题来解决了我的问题。 - tingyik90
这个解决方案对我很有效。但是有一件事让我感到困惑:在主题中直接设置windowEnterAnimation或windowExitAnimation不会影响我的DialogFragments动画。唯一有效的方法是将windowAnimationStyle设置为一个单独的XML文件,该文件定义了样式并在那里设置进入和退出动画。 - szaske
请问您能否展示一下 MyThemeWithCustomAnimation 的 XML 代码? - Oleg Yablokov

14
为了获得带有动画的全屏对话框,请编写以下代码...

样式:

```html ```
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="actionModeBackground">?attr/colorPrimary</item>
    <item name="windowActionModeOverlay">true</item>
</style>

<style name="AppTheme.NoActionBar">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
</style>

<style name="AppTheme.NoActionBar.FullScreenDialog">
    <item name="android:windowAnimationStyle">@style/Animation.WindowSlideUpDown</item>
</style>

<style name="Animation.WindowSlideUpDown" parent="@android:style/Animation.Activity">
    <item name="android:windowEnterAnimation">@anim/slide_up</item>
    <item name="android:windowExitAnimation">@anim/slide_down</item>
</style>

res/anim/slide_up.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="@android:interpolator/accelerate_quad">

    <translate
        android:duration="@android:integer/config_shortAnimTime"
        android:fromYDelta="100%"
        android:toYDelta="0%"/>
</set>

res/anim/slide_down.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="@android:interpolator/accelerate_quad">

    <translate
        android:duration="@android:integer/config_shortAnimTime"
        android:fromYDelta="0%"
        android:toYDelta="100%"/>
</set>

Java代码:

public class MyDialog extends DialogFragment {

    @Override
    public int getTheme() {
        return R.style.AppTheme_NoActionBar_FullScreenDialog;
    }
}

private void showDialog() {
    FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
    Fragment previous = getSupportFragmentManager().findFragmentByTag(MyDialog.class.getName());
    if (previous != null) {
        fragmentTransaction.remove(previous);
    }
    fragmentTransaction.addToBackStack(null);

    MyDialog dialog = new MyDialog();
    dialog.show(fragmentTransaction, MyDialog.class.getName());
}

6
在DialogFragment中,自定义动画被称为onCreateDialog。'DialogAnimation'是前面答案中的自定义动画风格。
public Dialog onCreateDialog(Bundle savedInstanceState) 
{
    final Dialog dialog = super.onCreateDialog(savedInstanceState);
    dialog.getWindow().getAttributes().windowAnimations = R.style.DialogAnimation;
    return dialog;
}

4
在您的对话框片段中,在onStart内使用decor视图。
@Override
public void onStart() {
    super.onStart();


    final View decorView = getDialog()
            .getWindow()
            .getDecorView();

    decorView.animate().translationY(-100)
            .setStartDelay(300)
            .setDuration(300)
            .start();

}

2
底层视图似乎之后无法点击。 - HaydenKai

3

如果你想在API上工作,你需要在DialogFragment的onStart方法中进行,而不是在onCreateDialog方法中。

@Override
    public void onStart() 
    {
        if (getDialog() == null) 
        {
            return;
        }

        getDialog().getWindow().setWindowAnimations(
                  R.style.DlgAnimation);

        super.onStart();
    }

在我看来,这是最好的解决方案。它也适用于AlertDialogs,只需在onShowListener中使用即可。谢谢! - Gabor Novak

3
注意:这仅是其他答案的补充。
无论您选择哪个解决方案,您可能会像我一样遇到相同的问题。
在安装新版本以使动画更改生效之前,我需要从我的开发设备上卸载该游戏。
我不确定为什么,但我猜测与Android Studio上优化的部署未能识别更改有关。

非常感谢您。我因为它无法正常工作而快要疯了。需要卸载应用程序并重新安装才能使其正常工作。 - DIRTY DAVE
我讨厌更新 Android Studio 的另一个原因是,总会出现版本有问题。在更新之前,我从来没有遇到过这个问题。 - DIRTY DAVE

1

你看过放大视图的Android开发者培训吗?这可能是一个很好的起点。

你可能想要创建一个自定义类来扩展DialogFragment以使其工作。

此外,查看Jake Wharton的NineOldAndroids,以获得从API Level 1到Honeycomb动画API兼容性的支持。


-1
将此代码添加到值动画中。
 <scale
    android:duration="@android:integer/config_longAnimTime"
    android:fromXScale="0.2"
    android:fromYScale="0.2"
    android:toXScale="1.0"
    android:toYScale="1.0"
    android:pivotX="50%"
    android:pivotY="50%"/>
<alpha
    android:fromAlpha="0.1"
    android:toAlpha="1.0"
    android:duration="@android:integer/config_longAnimTime"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"/>

调用styles.xml

<style name="DialogScale">
    <item name="android:windowEnterAnimation">@anim/scale_in</item>
    <item name="android:windowExitAnimation">@anim/scale_out</item>
</style>

在Java代码中:设置Onclick。
public void onClick(View v) {
        fab_onclick(R.style.DialogScale, "Scale" ,(Activity) context,getWindow().getDecorView().getRootView());
      //  Dialogs.fab_onclick(R.style.DialogScale, "Scale");

    }

方法设置:

alertDialog.getWindow().getAttributes().windowAnimations = type;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接