最近在做视频播放和直播项目,等项目写完了会将视频方面的东西整理出来。今天整理总结一下安卓设计模式中的builder设计模式。
首先我们看看安卓中最典型的builder设计模式是如何使用的:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setCustomTitle(titleView);
builder.setNegativeButton("取消", onClickListener);
builder.setPositiveButton("确定", onClickListener);
//...设置一些其他属性值
builder.create().show();
这个dialog就是安卓系统用builder设计模式给我封装的最典型的一个应用。那么为什么要用builder设计模式呢?网上的解释基本上就是:将一个复杂的对象的构建与他的表示分离,使得同样的构建构成可以创建不同的表示。理解了半天,云里雾里!我的理解就是我们去创建一个有很多属性值得对象的时候,不去直接通过它的构造器创建,而是先去创建一个builder(其实名字叫什么无所谓),然后给builder设置你想要创建对象的属性值,这样间接地来创建对象。这样的好处就是清晰明了,你需要什么、关心什么属性值就给他赋值,不关心的可以完全不用理睬。
我们如何使用builder模式呢?下面通过一个简单的小例子来写一个我们自己的builder模式:
public class Person {
private String mName;
private int mAge;
/**
* 一个构造器
*
* @param name
* @param age
*/
public Person(String name, int age) {
this.mName = name;
this.mAge = age;
}
/**
* get set方法,需要就写,不需要就不写
*
* @return
*/
public String getName() {
return mName;
}
public void setName(String name) {
this.mName = name;
}
/**
* 静态内部类builder
*/
static class Builder {
private String mName;
private int mAge;
public Builder setName(String name) {
this.mName = name;
return this;
}
public Builder setAge(int age) {
this.mAge = age;
return this;
}
public Person create() {
return new Person(mName, mAge);
}
}
}
这里只是给了两个属性,一个最简单的builder模式例子写完了,下面来看如何使用:
Person person = new Person.Builder()
.setAge(12)
.setName("王")
.create();
至此,一个最简单的builder设计、使用就结束了,自己动手敲一遍发现,原来如此简单,然后我们再来总结一下几个步骤:
1.创建一个静态内部类builder
2.将外部类的所有属性都复制一份到内部类中
3.静态内部类中写设置属性值的set方法,并且每次都将自己返回
4.静态内部类中写一个方法create创建外部类的实例并返回
应用:学习了就要使用,安卓原生alertdailg封装的固然非常完美,但是如果我们使用上面的方法去创建的话,样式显示在不同的api上会有很大的差别的,特别是在低版本api中样式特别丑,所以项目中基本上很少使用默认样式,而是会选择自定义样式,另外就是在fragment出来不久后谷歌就给出了dialogfragment,然后建议开发人员使用dialogfragment替代alertdialog,具体有什么好处,为什么 我也没有深究过。但是在使用dialogfragment过程中,会有很多需要注意的细节,每次都需要特别注意,而且每次都为了一个效果调试半天,所以正好借助builder模式,我们自己来封装一下:
先看效果:
当然前两张不是使用的封装代码实现的,而是使用dialogfragment内部dialog实现的。第三张、第四张其实是有出现和退出动画效果的,只是我不会制作gif图。第三张其实我们我们要实现的效果就是可以改变位置、大小、样式等,第四张我想展示的效果就是内部可以嵌套viewpager等等实现各种效果,最后一张是测试的,也贴出来了。看一眼链式调用使用方法吧:
CustomDialogFragment.Builder.getInstance()
.setResId(R.layout.layout_one)
.setAnimationId(R.style.dialog_animation)
.setOnRootViewReadyListener(new CustomDialogFragment.Builder.OnRootViewReadyListener() {
@Override
void rootView(View view, Bundle savedInstanceState, final DialogFragment dialog) {
view.findViewById(R.id.one).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
view.findViewById(R.id.two).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
}
})
.show(getFragmentManager());
是不是很简单,当然可以设置很多属性,这里仅仅展示了几个。最后将源码贴出来,不过多解释,各个属性值得参数含义都有注解,注意的地方就是各个参数在fragment中生命周期中设置的位置,否则会出现没有效果的问题。
/**
* Created by zhq on 2017/12/1.
*/
public class CustomDialogFragment extends DialogFragment {
private static final int NO_SET_DATA = -1;
private static final String LAYOUT_RES_ID = "res_id";
private static final String CUSTOME_STYLE = "style";
private static final String DIALOG_WIDTH = "width";
private static final String DIALOG_HEIGHT = "height";
private static final String DIALOG_GRAVITY = "gravity";
private static final String DIALOG_DIMAMOUNT = "amount";
private static final String DIALOG_NO_TITLE = "no_title";
private static final String DIALOG_GET_ROOTVIEW = "no_title";
private static final String DIALOG_STATE_BAR_TRANSLATE = "bar_translate";
private static final String DIALOG_CANCLEABLE = "cancle";
private static final String DIALOG_CANCLEABLE_OUTSIDE = "cancle_outside";
private static final String DIALOG_ANIMATION = "animation";
private static CustomDialogFragment getInstance(int resId, int styleId, int width, int height, int gravity,
float dimamount, boolean notitle, boolean stateBarTranslate, boolean cancleAble,
boolean canceledOnTouchOutside, int animationId, Builder.OnRootViewReadyListener listener) {
CustomDialogFragment fragment = new CustomDialogFragment();
Bundle bundle = new Bundle();
bundle.putInt(LAYOUT_RES_ID, resId);
bundle.putInt(CUSTOME_STYLE, styleId);
bundle.putInt(DIALOG_WIDTH, width);
bundle.putInt(DIALOG_HEIGHT, height);
bundle.putInt(DIALOG_GRAVITY, gravity);
bundle.putFloat(DIALOG_DIMAMOUNT, dimamount);
bundle.putBoolean(DIALOG_NO_TITLE, notitle);
bundle.putParcelable(DIALOG_GET_ROOTVIEW, listener);
bundle.putBoolean(DIALOG_STATE_BAR_TRANSLATE, stateBarTranslate);
bundle.putBoolean(DIALOG_CANCLEABLE, cancleAble);
bundle.putBoolean(DIALOG_CANCLEABLE_OUTSIDE, canceledOnTouchOutside);
bundle.putInt(DIALOG_ANIMATION, animationId);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int styleId = getArguments().getInt(CUSTOME_STYLE, -1);
if (NO_SET_DATA != styleId) {
setStyle(DialogFragment.STYLE_NORMAL, styleId);
}
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.RED));
Bundle bundle = getArguments();
if (bundle.getBoolean(DIALOG_NO_TITLE)) {
getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
}
float dimamount = bundle.getFloat(DIALOG_DIMAMOUNT);
if (-1.0f != dimamount) {
getDialog().getWindow().setDimAmount(dimamount);//设置背景颜色
}
if (bundle.getBoolean(DIALOG_STATE_BAR_TRANSLATE, false)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getDialog().getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
if (NO_SET_DATA != bundle.getInt(DIALOG_ANIMATION)) {
getDialog().getWindow().setWindowAnimations(bundle.getInt(DIALOG_ANIMATION));
}
View inflate = inflater.inflate(getArguments().getInt(LAYOUT_RES_ID), container, false);
AutoUtils.autoSize(inflate);
return inflate;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Builder.OnRootViewReadyListener listener = getArguments().getParcelable(DIALOG_GET_ROOTVIEW);
if (null != listener) {
listener.rootView(view, savedInstanceState, this);
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Bundle arguments = getArguments();
int width = arguments.getInt(DIALOG_WIDTH);
int height = arguments.getInt(DIALOG_HEIGHT);
int gravity = arguments.getInt(DIALOG_GRAVITY);
if (-3 != (width + height + gravity)) {
Window dialogWindow = getDialog().getWindow();
WindowManager.LayoutParams lp = dialogWindow.getAttributes();
if (NO_SET_DATA != width) {
lp.width = width;
}
if (NO_SET_DATA != height) {
lp.height = height;
}
if (NO_SET_DATA != gravity) {
lp.gravity = gravity;
}
dialogWindow.setAttributes(lp);
}
getDialog().setCancelable(arguments.getBoolean(DIALOG_CANCLEABLE));
getDialog().setCanceledOnTouchOutside(arguments.getBoolean(DIALOG_CANCLEABLE_OUTSIDE));
}
@Override
public void show(FragmentManager manager, String tag) {
if (!this.isAdded()) {
manager.beginTransaction().add(this, tag).commitAllowingStateLoss();
}
}
@Override
public void dismiss() {
super.dismissAllowingStateLoss();
}
public static class Builder {
/**
* 设置页面布局文件id
*/
private int resId;
/**
* 设置显示样式,可以在样式中设置动画等等
*/
private int styleId = -1;
/**
* 设置高度
*/
private int height = -1;
/**
* 设置宽度
*/
private int width = -1;
/**
* 设置显示的位置
*/
private int gravity = -1;
/**
* 设置背景透明度
*/
private float dimAmount = 0.6f;
/**
* 是否为没有标题
*/
private boolean notitile = true;
/**
* 处理4.4以上手机全屏时状态栏变黑
*/
private boolean stateBarTranslate = true;
/**
* 点击返回键是否消失
*/
private boolean cancleAble = true;
/**
* 点击返回键是否消失
*/
private int animationId = -1;
/**
* 触摸弹框外部区域是否消失
*/
private boolean canceledOnTouchOutside = true;
/**
* 设置消失事件监听
*/
private OnDilogDisssmissListener dissListener;
/**
* 拿到页面根view
*/
private OnRootViewReadyListener rootViewReadyListener;
public static Builder getInstance() {
return new Builder();
}
public void show(FragmentManager fragmentManager) {
CustomDialogFragment dialogFragment = CustomDialogFragment.getInstance(resId, styleId, width, height, gravity, dimAmount,
notitile, stateBarTranslate, cancleAble, canceledOnTouchOutside, animationId, rootViewReadyListener);
if (null != dissListener) {
dialogFragment.getDialog().setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
dissListener.onDissmisss();
}
});
}
dialogFragment.show(fragmentManager, "");
}
public Builder setResId(int resId) {
this.resId = resId;
return this;
}
public Builder setAnimationId(int animationId) {
this.animationId = animationId;
return this;
}
public Builder setStyleId(int styleId) {
this.styleId = styleId;
return this;
}
public Builder setHeight(int height) {
this.height = height;
return this;
}
public Builder setWidth(int width) {
this.width = width;
return this;
}
public Builder setGravity(int gravity) {
this.gravity = gravity;
return this;
}
public Builder setDimAmount(float dimAmount) {
this.dimAmount = dimAmount;
return this;
}
public Builder setNoTitle(boolean notitle) {
this.notitile = notitle;
return this;
}
public Builder setOnDilogDisssmissListener(OnDilogDisssmissListener listener) {
this.dissListener = listener;
return this;
}
public Builder setOnRootViewReadyListener(OnRootViewReadyListener listener) {
this.rootViewReadyListener = listener;
return this;
}
public Builder setStateBarTranslate(boolean stateBarTranslate) {
this.stateBarTranslate = stateBarTranslate;
return this;
}
public Builder setCancelable(boolean cancleAble) {
this.cancleAble = cancleAble;
return this;
}
public Builder setCanceledOnTouchOutside(boolean canceledOnTouchOutside) {
this.canceledOnTouchOutside = canceledOnTouchOutside;
return this;
}
interface OnDilogDisssmissListener {
void onDissmisss();
}
public static abstract class OnRootViewReadyListener implements Parcelable {
public abstract void rootView(View view, Bundle savedInstanceState, DialogFragment dialog);
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
}
}
}
}
最后,如果项目中有很多例如退出啊等等需要用户确认的弹框,其实另一种封装个人觉得更简单,代码就不贴了自行下载吧,看看效果和使用:
使用方法:
NormalDialogFragment instance = NormalDialogFragment.getInstance("确定退出吗?", "确定", "取消");
instance.setOnPositiveClickListener(new NormalDialogFragment.OnPositiveClickListener() {
@Override
public void onPositiveClickListen() {
//退出
}
});
instance.show(getSupportFragmentManager(), "");
NormalDialogFragment instance = NormalDialogFragment.getInstance("确定退出吗?", "退出后将丢失重要文件");
instance.setOnPositiveClickListener(new NormalDialogFragment.OnPositiveClickListener() {
@Override
public void onPositiveClickListen() {
//退出
}
});
instance.show(getSupportFragmentManager(), "");
有需要的自行下载源码吧:
github地址: https://github.com/zhq217217/builder-dialogfragment