在2048游戏中,询问用户是否退出游戏,游戏失败或者成功后弹出对话框让用户做下一步选择,总之Dialog是必须要用的,但是系统默认的Dialog和游戏的UI风格不搭,因此要求我们必须自定义Dialog.ProgressBar主要用在游戏启动的时候显示游戏的加载进度,也是游戏必不可有的部分,也需要自定义。
Dialog的自定义
Dialog的自定义是非常简单的事情,为了更好的理解Dialog的工作机理,学习一下WindowManger类的作用是有必要的。
WindowManger
WindowManager在Android的窗口管理机制中占有核心的地位,为什么这么说呢,这个类有些方法,可以直接添加一个View,也可以删除一个View,典型的说就是悬浮窗,悬浮窗的实现就是使用WindowManager类的,悬浮窗具体的使用可以参考我的这篇文章:Android悬浮窗使用总结
简单的说,我们可以把一个Button添加到界面上来,也可以把更加复杂的东西添加到界面上来。那么Dialog是不也是这么实现的呢?我的理解是是的(如果不对请拍砖),他们本质上都是使用WindowManager的addView方法来向界面添加View的,理解了这个,那我们很容易理解怎么自定义Dialog了,其实就是自己构建好一个View,在通过Dialog中封装好的一些方法,最终还是使用WindowManager的addView方法来向系统添加View.
实现步骤
自定义Dialog的步骤简要的可以总结为两步:
1.创建View或者布局文件
2.添加
在我的2048游戏中,askDialog的布局文件非常简单,如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="@drawable/bg_round_rectangle_white_stroke"
android:layout_width="@dimen/ask_dialog_width"
android:layout_height="@dimen/ask_dialog_height">
<ImageView
android:background="@drawable/bg_default_button_edge_top"
android:src="@mipmap/game_name"
android:layout_margin="5dp"
android:id="@+id/header_image"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/header_line"
android:layout_below="@+id/header_image"
android:background="@android:color/white"
android:layout_width="match_parent"
android:layout_height="2dp" />
<TextView
android:id="@+id/ask_text"
android:gravity="center"
android:textSize="@dimen/text_mode_size"
android:textColor="@android:color/holo_blue_light"
android:layout_below="@id/header_line"
android:layout_centerHorizontal="true"
android:text="@string/allow_go_back_ask"
android:layout_width="wrap_content"
android:layout_height="100dp" />
<Button
style="@style/dialog_button"
android:text="@string/allow_go_back"
android:id="@+id/dialog_button_yes"
android:layout_below="@+id/ask_text"
android:layout_marginLeft="20dp"
android:layout_width="90dp"
android:layout_height="40dp" />
<Button
style="@style/dialog_button"
android:text="@string/forbid_go_back"
android:id="@+id/dialog_button_no"
android:layout_below="@+id/ask_text"
android:layout_marginRight="20dp"
android:layout_alignParentRight="true"
android:layout_width="90dp"
android:layout_height="40dp" />
</RelativeLayout>
很少有人会有耐心看这些code,之间看下它的效果吧:
现在我们把布局文件构造好了,现在我们需要添加到界面上来:
private Dialog buildAskDialog(){
Activity activity = getActivity();
final Dialog askDialog = new Dialog(activity,R.style.CustomDialog);
askDialog.setContentView(R.layout.ask_dialog_layout);
Button button;
button = (Button) askDialog.findViewById(R.id.dialog_button_yes);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Game2048StaticControl.isGoBackEnabled = true;
goGameMainActivity();
if(askDialog != null){
askDialog.dismiss();
}
}
});
button = (Button) askDialog.findViewById(R.id.dialog_button_no);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Game2048StaticControl.isGoBackEnabled = false;
goGameMainActivity();
if(askDialog != null){
askDialog.dismiss();
}
}
});
return askDialog;
}
我们想系统添加了我们的View,当然是用的是布局文件。核心代码是:
final Dialog askDialog = new Dialog(activity,R.style.CustomDialog);
askDialog.setContentView(R.layout.ask_dialog_layout);
创建一个对话框,使用Dialog的setContentView方法添加。添加好了一号我们还需要给其中的一个view设置时间监听器,当然我们需要先获得它的实例,使用的是Dialog的findViewByID方法:
askDialog.findViewById(R.id.dialog_button_yes);
设置完这些以后,调用Dialog的show方法就可以显示出来Dialog了,是不是很简单?
自定义Dialog样式
Dialog是显示出来了,可以,这个时候Dialog的背景是白色的,并不是我们想要的透明的,怎么办呢?我们可以设置透明的主题:
比如在这款游戏中创建Dialog的代码如下:
Dialog(activity,R.style.CustomDialog);
可见我们给它传入了一个R.style.CustomDialog的自定义样式:
<style name="CustomDialog" parent="android:style/Theme.Dialog">
<!--背景颜色及透明程度-->
<item name="android:windowBackground">@android:color/transparent</item>
<!--是否有标题 -->
<item name="android:windowNoTitle">true</item>
<!--是否浮现在activity之上-->
<item name="android:windowIsFloating">true</item>
<!--是否模糊-->
<item name="android:backgroundDimEnabled">false</item>
</style>
具体的配置项注释中已经说的很清楚了。
Drawable
此外,我还想补充一点另外的知识,就是Dialog的背景,他是一个ShapeDrawable,Drawable在Android UI开发中真的用的特别的多,如果对它不熟悉,建议下点功夫把它彻底搞定。2048游戏中的Dialog的ShapeDrawable第一如下:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="12dp" />
<solid android:color="@color/half_transparency_wihte"/>
<stroke android:color="@android:color/white"
android:width="5dp" />
</shape>
具体就不展开了大家可以自行去学习,此外Dialog中的Button的background也是使用了Drawable,叫做StateListDrawable,其定义如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true"
android:drawable="@drawable/bg_button_dialog_focus" /> <!-- focused -->
<item android:state_pressed="true"
android:drawable="@drawable/bg_button_dialog_focus" /> <!-- hovered -->
<item android:state_focused="false"
android:drawable="@drawable/bg_button_dialog_default" /> <!-- default -->
</selector>
里面涉及到了forcus,pressed和unforcus三种状态,pressed状态和forcus状态使用的drawable一样的,确定以如下:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="5dp" />
<solid android:color="@android:color/holo_blue_light"/>
</shape>
就是一个最简单的圆角矩形。
unforcus状态对应的drawable如下:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="5dp" />
<solid android:color="@android:color/darker_gray"/>
</shape>
只是颜色不同而已。
Dialog的自定义就说到这里。
ProgressBar的自定义
ProgressBar的自定义本人研究也不深,就不班门弄斧了,简单的介绍下在这款游戏中对ProgressBar的自定义吧。
先从ProgressBar的使用说起,我们在xml中如此使用:
<ProgressBar
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_below="@+id/frame_layout_container"
android:indeterminateDrawable="@drawable/progress_self_define"
android:layout_marginTop="20dp"
android:indeterminate="true"
android:indeterminateBehavior="repeat"
android:layout_centerHorizontal="true"
android:layout_width="500dp"
android:layout_height="10dp" />
android:indeterminate=”true”是的这个ProgressBar永久循环,永久循环的ProgressBar的自定义样式使用的是android:indeterminateDrawable=”@drawable/progress_self_define”。因此,可以想象,如果你使用的不是永久循环的ProgressBar,自定义的drawable应该设置到android:progressDrawable=”“。其他的条目就不啰嗦了,下面看一下这款游戏中使用的样式:android:indeterminateDrawable
“`
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<corners android:radius="5dip" />
<solid android:color="@android:color/darker_gray"/>
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<corners android:radius="5dip" />
<solid android:color="@android:color/holo_green_light"/>
</shape>
</clip>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="5dip" />
<solid android:color="@android:color/holo_orange_light"/>
</shape>
</clip>
</item>
“
background配置背景,progress配置进度条,secondaryProgress配置第二进度条。最终的结果如下:
此外,循环的ProgressBar还可以使用帧动画:animation-list来实现自定义,这种方式更好一点,以后可能会改为这种方式。