Android-DialogFragment详细学习

2017/6/30 posted in  Android

DialogFragment 设置全屏方法

方法一

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setStyle(STYLE_NO_FRAME, android.R.style.Theme_Holo_Light);
}

方法二

style

<style name="style_dialog" parent="android:style/Theme.Dialog">
    <item name="android:windowBackground">@color/white</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowIsFloating">true</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:scrollHorizontally">true</item>
    <!-- Dialog进入及退出动画 -->
    <item name="android:windowAnimationStyle">@style/BottomToTopAnim</item>
</style>

<style name="BottomToTopAnim" parent="android:Animation">
    <item name="@android:windowEnterAnimation">@anim/bottom_in</item>
    <item name="@android:windowExitAnimation">@anim/bottom_out</item>
</style>

bottom_in bottom_out 进入进出动画

<?xml version="1.0" encoding="utf-8"?>

<translate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fromYDelta="100%p"
    android:toYDelta="0"/>

<?xml version="1.0" encoding="utf-8"?>
<translate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fromYDelta="0"
    android:toYDelta="100%p"/>

代码

@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {

    LayoutInflater inflater = getActivity().getLayoutInflater();
    View view = inflater.inflate(R.layout.view, null);

    final Dialog dialog = new Dialog(getActivity(), R.style.style_dialog);
    dialog.setContentView(view);
    dialog.show();

    Window window = dialog.getWindow();
    window.setGravity(Gravity.BOTTOM); //可设置dialog的位置
    window.getDecorView().setPadding(0, 0, 0, 0); //消除边距

    WindowManager.LayoutParams lp = window.getAttributes();
    lp.width = WindowManager.LayoutParams.MATCH_PARENT;   //设置宽度充满屏幕
    lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
    window.setAttributes(lp);
    return dialog;

}

生命周期

  • onAttach
  • onCreate
  • onCreateView
  • onStart
  • onStop
  • third activity on destroy
  • onDestroyView
  • onDetach
  • onAttach
  • onCreate
  • onCreateView
  • onStart

设置背景为透明

View decorView = getDialog().getWindow().getDecorView();
decorView.setBackground(new ColorDrawable(Color.TRANSPARENT));

(PS:Window -> DecorView -> FrameLayout -> FrameLayout -> 我们的自定义View) 这个逻辑大家应该都知道的,所以我们只需要改变底部的DecorView的背景色即可。

设置弹框位置

因为View是在window下面的,我们只需要让window的Grivaty属性是Bottom,这样,里面的元素都是居于底部即可。

Window window = getDialog().getWindow();
WindowManager.LayoutParams layoutParams = window.getAttributes();
layoutParams.gravity = Gravity.BOTTOM;
window.setAttributes(layoutParams);

解决DialogFragment两边的间隙

因为View都是被包含在window里面,虽然我们的自己的View的宽度已经设置成了match_parent,但是我们并没有对window设置宽度为最大。所以我们先来改变window的宽度。

改变window的宽度:

Window window = getDialog().getWindow();
WindowManager.LayoutParams layoutParams = window.getAttributes();
layoutParams.gravity = Gravity.BOTTOM;
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
window.setAttributes(layoutParams);

我们在前面修改弹框位置的代码处,多添加一句:

layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;

设置之后虽然二边的间隙变小了很多。但是还是有间隙,既然我们都已经把window的宽度变为match_parent,还是没填充,说明应该是有padding值。那我们马上就想到了,难道是DecorView里面有padding值。毕竟我们的View也是被包含在DecorView里面。废话不多说,我们马上实验:

decorView.setPadding(0,0,0,0);

PS:这里还有另外一种方法,不写这句decorView.setPadding(0,0,0,0);而是直接设置window的背景颜色,window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));源码中其实也给DecorView设置了padding值。所以效果相同。

设置弹框从下而上显示

我们看过ios的弹框效果,是从底部从下而上升起,然后消失的时候也是从上而下消失。所以消失的时候我们不能单纯的让DialogFragment执行dismiss(),而是先让弹框执行下移的动画效果,然后再dismiss()

既然谈到了上下的移动,大家肯定马上想到了用TranslateAnimation动画来做,我们就一步步来看如何用这个来实现:

  • 弹框出现动画:
Animation slide = new TranslateAnimation(
      Animation.RELATIVE_TO_SELF, 0.0f,
      Animation.RELATIVE_TO_SELF, 0.0f, 
      Animation.RELATIVE_TO_SELF, 1.0f, 
      Animation.RELATIVE_TO_SELF, 0.0f
);
slide.setDuration(400);
slide.setFillAfter(true);
slide.setFillEnabled(true);
view.startAnimation(slide);

我们来看TranslateAnimation,这里我们传了八个参数,一般大家用到的是只传四个参数:

TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)

也就是从坐标(fromXDelta,fromYDelta)(toXDelta,toYDelta)
我们可以点进去这个构造函数查看:

public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) {
      mFromXValue = fromXDelta;
      mToXValue = toXDelta;
      mFromYValue = fromYDelta;
      mToYValue = toYDelta;

      mFromXType = ABSOLUTE;
      mToXType = ABSOLUTE;
      mFromYType = ABSOLUTE;
      mToYType = ABSOLUTE;
}

之所以我们以前用的只传了四个参数,是因为他给我们把另外四个参数以及赋了默认值,也就是ABSOLUTE。我们继续看有哪几种可以选择:

  /**
   * The specified dimension is an absolute number of pixels.
   */
  public static final int ABSOLUTE = 0;

  /**
   * The specified dimension holds a float and should be multiplied by the
   * height or width of the object being animated.
   */
  public static final int RELATIVE_TO_SELF = 1;

  /**
   * The specified dimension holds a float and should be multiplied by the
   * height or width of the parent of the object being animated.
   */
  public static final int RELATIVE_TO_PARENT = 2;

通过字面意思我们也能理解:

ABSOLUTE是绝对坐标,RELATIVE_TO_SELF是相对于自身,RELATIVE_TO_PARENT是相对于父View。
而我们只需要我们的弹框显示的位置,让的起始位置如下图所示:

刚开始超过屏幕,并且高度为弹框自身的高度,然后再回到原始位置,所以我们就用:

Animation slide = new TranslateAnimation(
      Animation.RELATIVE_TO_SELF, 0.0f,
      Animation.RELATIVE_TO_SELF, 0.0f, 
      Animation.RELATIVE_TO_SELF, 1.0f, 
      Animation.RELATIVE_TO_SELF, 0.0f
);

从原来的位置,增加了自身高度的距离为起始点,开始移动,然后再回到原来的位置。

  • 消失动画:

只要跟上面反过来就可以了。同时这里我们要额外增加监听动画结束事件,因为我们让弹框往下移动结束后,要让这个弹框dismiss掉:

Animation slide = new TranslateAnimation(
      Animation.RELATIVE_TO_SELF, 0.0f,
      Animation.RELATIVE_TO_SELF, 0.0f, 
      Animation.RELATIVE_TO_SELF, 0.0f, 
      Animation.RELATIVE_TO_SELF, 1.0f
);
slide.setAnimationListener(new Animation.AnimationListener() {
  @Override
  public void onAnimationStart(Animation animation) {}

  @Override
  public void onAnimationEnd(Animation animation) {
      IOSDialogFragment.this.dismiss();
  }

  @Override
  public void onAnimationRepeat(Animation animation) {}
});

所以我们的动画的代码总结下就是:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
    getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
    rootView = inflater.inflate(R.layout.fragment_ios_dialog, container, false);
    slideToUp(rootView);
    return rootView;
}


public void slideToUp(View view){
    Animation slide = new TranslateAnimation(
        Animation.RELATIVE_TO_SELF, 0.0f,
        Animation.RELATIVE_TO_SELF, 0.0f, 
        Animation.RELATIVE_TO_SELF,1.0f, Animation.RELATIVE_TO_SELF, 0.0f);

    slide.setDuration(400);
    slide.setFillEnabled(true);
    slide.setFillAfter(true);
    view.startAnimation(slide);
}

public void slideToDown(View view){
    Animation slide = new TranslateAnimation(
        Animation.RELATIVE_TO_SELF, 0.0f,
        Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF,0.0f, Animation.RELATIVE_TO_SELF, 1.0f);

    slide.setDuration(400);
    slide.setFillEnabled(true);
    slide.setFillAfter(true);
    view.startAnimation(slide);

    slide.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {

        }

        @Override
        public void onAnimationEnd(Animation animation) {
            IOSDialogFragment.this.dismiss();//弹框消失
        }

        @Override
        public void onAnimationRepeat(Animation animation) {

        }
    });
}