本书第12章是讲解Dialog。12.4挑战练习是在CriminalIntent项目中,再增加一个TimePickerFragment的对话框fragment。通过在CrimeFragment用户界面上添加的时间按钮,
弹出TimePickerFragment界面,允许用户使用TimePicker组件选择crime发生的具体时间。

我的修改思路是:

  1. 按照DatePickerFragment实现的步骤、方法实现实现TimePickerFragment;
  2. crime日期与时间是一个整体:
    1. DatePickerFragment仅可以调整:年月日,时分不变动;
    2. TimePickerFragment仅可以调整:时分,时分不变动;
    3. 故Activity切换时,交换数据(附加到Intent上的extra数据单元共用一个Date。

具体实现如下。请各位高手拍砖。

1、使用AppCompat兼容库

依据DatePickerFragment实现方式,仍使用AppCompat兼容库。它在实现DatePickerFragment时,已经添加到CriminalIntent项目中。

2、增加、更新资源文件

2.1)增加标题:“Time of crime:”

在项目中,res\values\strings.xml增加 <string name="time_picker_title">Time of crime:</string>

即:

1 <resources>
2     ... ...
3
4     <string name="time_picker_title">Time of crime:</string>
5 </resources>

2.2)在CrimFragment界面上添加Time Button

在CrimFragment界面上显示Time Button,需要在项目中res\layout\fragment_crime.xml文件,增加下列代码:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout
 3     ... ...
 4
 5     <Button
 6         ... ...
 7         />
 8
 9     <Button
10         android:id="@+id/crime_time"
11         android:layout_width="match_parent"
12         android:layout_height="wrap_content"
13         android:layout_marginLeft="16dp"
14         android:layout_marginRight="16dp"
15         />
16
17     <CheckBox
18         ... ...
19         />
20
21 </LinearLayout>

2.3)为了保证设备旋转后仍然能正常显示

还需在项目的res\layout-land\fragment_crime.xml文件增加类似上面代码:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout
 3     ... ...
 4
 5         <Button
 6             ... ...
 7             />
 8
 9         <Button
10             android:id="@+id/crime_time"
11             android:layout_width="wrap_content"
12             android:layout_height="wrap_content"
13             android:layout_weight="1"/>
14
15         <CheckBox
16             ... ...
17             />
18
19     ... ...
20
21 </LinearLayout>

3、创建新的TimePickerFragment

TimePickerFragment是DialogFragment的子类。然后,在TimePickerFragment中,创建并配置显示TimePicker组件的AlertDialog实例。TimePickerFragment同样由CrimePagerActivity托管。

3.1)使用android.support.v4.app.DialogFragment库

创建TimePickerFragment类,并设置其超类DialogFragment,由android.support.v4.app.DialogFragment库支持。

在TimePickerFragment.java中,重载DialogFragment类的onCreateDialog(Bundle savedInstanceState)方法。由托管activity的FragmentManager会调用它,在屏幕上显示DialogFragment。

onCreateDialog(Bundle savedInstanceState)方法的实现代码,创建一个带标题栏和OK按钮的AlertDialog,代码如下。注意:导入AlertDialog时,还是选择AppCompat库中的版本:android.support.v7.app.AlertDialog。

 1 public class TimePickerFragment extends DialogFragment {
 2
 3     @Override
 4     public Dialog onCreateDialog(Bundle savedInstanceState) {
 5
 6         return new AlertDialog.Builder(getActivity())
 7                 .setTitle(R.string.time_picker_title)
 8                 .setPositiveButton(android.R.string.ok, null)
 9                 .create();
10     }
11 }

这里类似DatePickerFragment,使用AlerDialog.Builder类,以Fluent Interface的方式创建AlertDialog实例。

3.2)显示TimeDialogFragment

同DatePickerFragment一样,TimeDialogFragment实例也是由托管activity的FragmentManager管理。使用fragment实例的public void show(FragmentManager manager, String tag)方法,将TimePickerFragment添加给FragmentManager管理并放置到屏幕上。

在CrimeFragment(CrimeFragment.java)中,也为TimePickerFragment增加一个tag常量:

1 private static final String DIALOG_TIME = "DialogTime";

然后,在onCreateView(...)方法中,添加点击时间按钮展现TimePickerFragment界面,实现mTimeButton按钮的OnClickListener监听器接口,代码:

 1 public class CrimeFragment extends Fragment {
 2
 3         ... ...
 4         private static final String DIALOG_TIME = "DialogTime";
 5
 6         ... ...
 7
 8         mTimeButton = (Button) v.findViewById(R.id.crime_time);
 9         mTimeButton.setText(DateFormat.format("h:mm a", mCrime.getDate()));
10         mTimeButton.setOnClickListener(new View.OnClickListener() {
11             @Override
12             public void onClick(View v) {
13                 FragmentManager manager = getFragmentManager();
14                 TimePickerFragment timeDialog = new TimePickerFragment();
15                 timeDialog.show(manager, DIALOG_TIME);
16             }
17         });
18
19         ... ...
20 }

这就可以显示:带标题(Time of crime:)和OK(确定)按钮的AlertDialog。

3.3)设置对话框的显示内容

同DatePickerFragment.

这时需要在TimePirckerFragment(TimePirckerFragment.java)中,使用AlertDialog.Builder的setView(...)方法, 添加TimePicker组件给AlertDialog对话框:

1 public AlertDialog.Builder setView(View view)

该方法配置对话框,实现在标题栏与按钮之间显示传入的View对象 —— TimePicker。要展示TimePicker,需要在项目工具窗口中,以TimePicker为根元素,创建名为dialog_time.xml的布局文件:

1 <?xml version="1.0" encoding="utf-8"?>
2 <TimePicker
3     xmlns:android="http://schemas.android.com/apk/res/android"
4     android:id="@+id/dialog_time_time_picker"
5     android:layout_width="match_parent"
6     android:layout_height="match_parent"
7     android:calendarViewShown="false">
8 </TimePicker>

同时在TimePickerFragment.onCreateDialog(...)方法中,实例化DatePicker视图并添加给对话框:

 1     @Override
 2     public Dialog onCreateDialog(Bundle savedInstanceState) {
 3
 4         View v = LayoutInflater.from(getActivity())
 5                 .inflate(R.layout.dialog_time, null);
 6
 7         return new AlertDialog.Builder(getActivity())
 8                 .setView(v)
 9                 .setTitle(R.string.time_picker_title)
10                 .setPositiveButton(android.R.string.ok, null)
11                 .create();
12     }

此时运行CriminalIntent,点击时间按钮,TimePicker就显示在对话框上。

4、数据传递

在书中例子中,DatePicker将修改的日期传递给CrimeFragment后,时间就被“清零”(时间回到0点),而时间按钮上的文字还是之前的时间。

我对DatePickerFragment进行修改,将原来

 1     @Override
 2     public Dialog onCreateDialog(Bundle savedInstanceState) {
 3         ... ...
 4         Calendar calendar = Calendar.getInstance();
 5         ... ...
 6
 7         return new AlertDialog.Builder(getActivity())
 8                 ... ...
 9                 .setPositiveButton(android.R.string.ok,
10                         new DialogInterface.OnClickListener() {
11                             @Override
12                             public void onClick(DialogInterface dialog, int which) {
13                                 ... ...
14
15                                 Date date = new GregorianCalendar(year, month, day).getTime();
16
17                                 ... ...
18                             }
19                         }
20                 )
21                 .create();
22     }

改为:

 1     @Override
 2     public Dialog onCreateDialog(Bundle savedInstanceState) {
 3         ... ...
 4         final Calendar calendar = Calendar.getInstance();
 5         ... ...
 6
 7         return new AlertDialog.Builder(getActivity())
 8                 ... ...
 9                 .setPositiveButton(android.R.string.ok,
10                         new DialogInterface.OnClickListener() {
11                             @Override
12                             public void onClick(DialogInterface dialog, int which) {
13                                 ... ...
14
15                                 Date date = new GregorianCalendar(year, month, day,
16                                         calendar.get(Calendar.HOUR_OF_DAY),
17                                         calendar.get(Calendar.MINUTE),
18                                         calendar.get(Calendar.SECOND)).getTime();
19
20                                 ... ...
21                             }
22                         }
23                 )
24                 .create();
25     }

由于在inline函数DialogInterface.OnClickListener()的onClick(...)使用到calendar,这样就要将calender定义改为final,即:

1 final Calendar calendar = Calendar.getInstance();

这样在DatePicker传递修改后的日期回CrimeFragment后,时间持不变。我认为时间的改变正是由TimePicker来完成。就是说 crime的日期(date)和时间(time)是相互关联的。基于这一思路,参考DatePickerFragment,进一步添加时间值的传递。

4.1)把crime记录的时间传递给TimePickerFragment

这就需要新建newInstance(Date)方法,然后将Date作为argument附加给fragment。

要新时间返回给CrimeFragment,且更新相应视图和模型层,这需将时间值打包为extra并附加到Intent上,然后调用CrimeFragment.onActivityResult(...)方法,并传入准备好的Intent参数。如前所属“crime的日期(date)和时间(time)是相互关联的”,Date类包含时间,这样时间extra就应该与DatePicker共用一个单元。

4.2)传递数据给TimePickerFragment

如前所述,应该用含有时间的crime Date值保存到TimePickerFragment的argument bundle中,TimePickerFragment使可直接获取到它。这样就使用DatePrickerFragment的ARG_DATE标记(tag)。为此要import ARG_DATE,即:

1 import static com.example.bigzhg.criminalintent.DatePickerFragment.ARG_DATE;

在TimePickerFragment.java中,添加newInstance(Date)方法,完成创建和设置fragment argument。

 1 public class TimePickerFragment extends DialogFragment {
 2
 3     ... ...
 4
 5     public static TimePickerFragment newInstance(Date date) {
 6         Bundle args = new Bundle();
 7         args.putSerializable(ARG_DATE, date);
 8
 9         TimePickerFragment fragment = new TimePickerFragment();
10         fragment.setArguments(args);
11         return fragment;
12     }
13
14     ... ...
15 }

再在CrimeFragment中,用TimePickerFragment.newInstance(Date)方法替换掉TimePickerFragment的构造方法:

 1     @Override
 2     public View onCreateView(
 3             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
 4
 5         ... ...
 6
 7         mTimeButton.setOnClickListener(new View.OnClickListener() {
 8             @Override
 9             public void onClick(View v) {
10                 FragmentManager manager = getFragmentManager();
11
12                 // TimePickerFragment timeDialog = new TimePickerFragment();
13                 TimePickerFragment timeDialog = TimePickerFragment
14                         .newInstance(mCrime.getDate());
15                 ... ...
16             }
17         });
18
19     ... ...
20
21     }

同DatePickerFragment一样,使用Date中的信息来初始化TimePicker对象。

在onCreateDialog(...)方法内,从argument中获取crime日期(如前所述)的对象Date对象,再创建一个Calendar对象,然后用Date对象配置它,再从Calendar对象中取回所需信息(时、分),来为TimePicker进行初始化:

 1 public class TimePickerFragment extends DialogFragment {
 2
 3     private TimePicker mTimePicker;
 4
 5     ... ...
 6
 7     @Override
 8     public Dialog onCreateDialog(Bundle savedInstanceState) {
 9         Date date = (Date) getArguments().getSerializable(ARG_DATE);
10
11         Calendar calendar = Calendar.getInstance();
12         calendar.setTime(date);
13
14         int hour = calendar.get(Calendar.HOUR_OF_DAY);
15         int minute = calendar.get(Calendar.MINUTE);
16
17         View v = LayoutInflater.from(getActivity())
18                 .inflate(R.layout.dialog_time, null);
19
20         mTimePicker = (TimePicker) v.findViewById(R.id.dialog_time_time_picker);
21         mTimePicker.setIs24HourView(false);
22         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
23             mTimePicker.setHour(hour);
24             mTimePicker.setMinute(minute);
25         } else {
26             mTimePicker.setCurrentHour(hour);
27             mTimePicker.setCurrentMinute(minute);
28         }
29
30         return new AlertDialog.Builder(getActivity())
31                 .setView(v)
32                 .setTitle(R.string.time_picker_title)
33                 .setPositiveButton(android.R.string.ok, null)
34                 .create();
35     }
36
37     ... ...
38
39 }

在onCreateDialog(...)方法内,设置TimePicker是上下午(非24小时)格式:

1 mTimePicker.setIs24HourView(false);

由于我用于调试的手机时Galaxy Note II,系统为Android 4.4.2。因setCurrentHour()和setCurrentMinute()已在新系统中不再使用,故增加SDK的版本判断:

1         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
2             mTimePicker.setHour(hour);
3             mTimePicker.setMinute(minute);
4         } else {
5             mTimePicker.setCurrentHour(hour);
6             mTimePicker.setCurrentMinute(minute);
7         }

4.3)返回时间数据给CrimeFragment

CrimeFragment接收TimePickerFragment返回的时间数据,ActivityManager 负责跟踪管理父 activity与子activity间的关系。回传数据后,子activity被销毁,而ActivityManager 知道接收数据的是哪个activity。

4.3.1)设置目标fragment

这就要将CrimeFragment设置成TimePickerFragment的目标fragment。即使是在CrimeFragment和TimePickerFragment被销毁和重建后,操作系统也会重新关联它们。调用以下Fragment方法可建立这种关联:

public void setTargetFragment(Fragment fragment, int requestCode)

在CrimeFragment.java中,增加时间请求代码常量:

1 private static final int REQUEST_TIME = 1;

然后将CrimeFragment设为TimePickerFragment实例的目标fragment:

1 timeDialog.setTargetFragment(CrimeFragment.this, REQUEST_TIME);

即:

 1 public class CrimeFragment extends Fragment {
 2
 3     ... ...
 4     private static final int REQUEST_TIME = 1;
 5
 6     ... ...
 7
 8     @Override
 9     public View onCreateView(
10             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
11
12         ... ...
13
14         mTimeButton.setOnClickListener(new View.OnClickListener() {
15             @Override
16             public void onClick(View v) {
17                 ... ...
18
19                 timeDialog.setTargetFragment(CrimeFragment.this, REQUEST_TIME);
20                 ... ...
21
22             }
23         });
24
25         ... ...
26
27         return v;
28     }

4.3.2)传递时间数据给目标fragment

建立CrimeFragment与TimePickerFragment间的联系后,需将数据回传给CrimeFragment。回传时间将作为extra附加给Intent。

使用TimePickerFragment类调用CrimeFragment.onActivityResult(int 请求代码, int 结果代码, Intent)方法,实现时间数据的回传。

  • 请求代码:与传入setTargetFragment(...)方法相匹配,告诉目标fragment返回结果来自哪里。
  • 结果代码:决定下一步该采取什么行动。
  • Intent:包含extra数据。

类似DatePickerFragment类,在TimePickerFragment类中,新建sendResult(...)私有方法,创建intent并将时间数据与crime Date构成新的Date数据,作为extra附加到intent上。最后调用CrimeFragment.onActivityResult(...)方法。

再就是使用sendResult(...)私有方法。用户点按对话框中的positive(确定)按钮时,需要从TimePicker中获取时间值并回传给CrimeFragment。在onCreateDialog(...)方法中,修改setPositiveButton(...),将null参数改DialogInterface.OnClickListener,并实现DialogInterface.OnClickListener监听器接口。在监听器接口的onClick(...)方法中,获取时间并调用sendResult(...)方法。

这里crime日期和时间一个“整体”,故公用DatePickerFragment的EXTRA_DATE:

1 import static com.example.bigzhg.criminalintent.DatePickerFragment.EXTRA_DATE;

其相关代码:

 1 ... ...
 2 import static com.example.bigzhg.criminalintent.DatePickerFragment.EXTRA_DATE;
 3
 4
 5 public class TimePickerFragment extends DialogFragment {
 6
 7     ... ...
 8
 9
10     @Override
11     public Dialog onCreateDialog(Bundle savedInstanceState) {
12         ... ...
13
14         final int year = calendar.get(Calendar.YEAR);
15         final int month = calendar.get(Calendar.MONTH);
16         final int day = calendar.get(Calendar.DAY_OF_MONTH);
17         int hour = calendar.get(Calendar.HOUR_OF_DAY);
18         int minute = calendar.get(Calendar.MINUTE);
19
20         ... ...
21
22         return new AlertDialog.Builder(getActivity())
23                 .setView(v)
24                 .setTitle(R.string.time_picker_title)
25                 // .setPositiveButton(android.R.string.ok, null)
26                 .setPositiveButton(android.R.string.ok,
27                         new DialogInterface.OnClickListener() {
28                             @Override
29                             public void onClick(DialogInterface dialog, int which) {
30                                 int hour, minute;
31
32                                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
33                                     hour = mTimePicker.getHour();
34                                     minute = mTimePicker.getMinute();
35                                 } else {
36                                     hour = mTimePicker.getCurrentHour();
37                                     minute = mTimePicker.getCurrentMinute();
38                                 }
39                                 Date date = new GregorianCalendar(
40                                         year, month, day, hour, minute).getTime();
41                                 sendResult(Activity.RESULT_OK, date);
42                             }
43                         })
44                 .create();
45     }
46
47     private void sendResult(int relustCode, Date date) {
48         if (getTargetFragment() == null) {
49             return;
50         }
51
52         Intent intent = new Intent();
53         intent.putExtra(EXTRA_DATE, date);
54
55         getTargetFragment().onActivityResult(getTargetRequestCode(), relustCode, intent);
56     }
57 }

这里,calendar在面谈过crime日期和时间是一个“整体”,由crime日期创建的,在TimePickerFragment保持日期值不变,仅仅运许用户调整时间。

再切换到CrimeFragment中,覆盖onActivityResult(...)方法,从extra中获取日期数据,增加“请求代码”的判断,依据“请求代码”:

  • 对DatePicker值,设置对应Crime的记录日期,然后刷新日期按钮的显示;
  • 对TimePIcker值,设置对应Crime的记录时间,然后刷新时间按钮的显示。

另外,同日期显示一样,为避免代码冗余,可以将时间按钮文字显示代码,封装到updateTiime()公共方法中,然后分别调用。

相关代码:

 1 public class CrimeFragment extends Fragment {
 2
 3     ... ...
 4
 5     @Override
 6     public View onCreateView(
 7             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
 8
 9         ... ...
10
11         mTimeButton = (Button) v.findViewById(R.id.crime_time);
12         updateTime();
13         ... ...
14
15     }
16
17     @Override
18     public void onActivityResult(int requestCode, int resultCode, Intent data) {
19         if (resultCode != Activity.RESULT_OK) {
20             return;
21         }
22
23         Date date = (Date) data
24                 .getSerializableExtra(DatePickerFragment.EXTRA_DATE);
25         mCrime.setDate(date);
26
27         switch (requestCode) {
28             case REQUEST_DATE:
29                 updateDate();
30                 break;
31             case REQUEST_TIME:
32                 updateTime();
33                 break;
34         }
35     }
36
37     private void updateDate() {
38         mDateButton.setText(DateFormat.format("EEEE, MMMM d, yyyyy", mCrime.getDate()));
39     }
40
41     private void updateTime() {
42         mTimeButton.setText(DateFormat.format("h:mm a", mCrime.getDate()));
43     }
44 }

到此,12.4的跳转练习就完成了。完整的代码在GitHub上可以找到。

请高手指点这样添加是否存在什么隐患?谢谢!

转载于:https://www.cnblogs.com/figozhg/p/6498471.html

《Android权威编程指南(The Big Nerd Ranch Guide)(第二版)》12.4挑战练习相关推荐

  1. Android权威编程指南读书笔记(1-2章)

    第一章 Android应用初体验 1.4用户界面设计 <?xml version="1.0" encoding="utf-8"?> ADT21开发版 ...

  2. android 权威编程指南 笔记 2018-09-01

    1.android Button上面的英文字符串自动大写的问题解决 <Buttonandroid:id="@+id/true_button"android:layout_wi ...

  3. 一、 Python 基础知识笔记 —— 《Python编程:从入门到实践(第二版)》学习笔记

    前言 先安利这本书<Python编程:从入门到实践(第二版)>,作者埃里克-马瑟斯,很适合新手入门,我的python入门学习就是以这本书为核心: 再安利一个网站:菜鸟教程-Python3教 ...

  4. 安卓权威编程指南 挑战练习 20.9 创建多版本主题

    20.9 挑战练习:创建多版本主题 创建BeatBoxButton样式时,我们继承了 android:style/Widget.Holo.Button 中的一些属 性.虽然可行,但没有用上最新的系统主 ...

  5. Android Programming The Big Nerd Ranch Guide,Second Edition第十六章使用相机Intent部分的改进

    最近自己在看这本书,看到这部分并亲手上机测试时,发现并不是照书上那样编码之后就能顺利打开相机拍照.经过思考和对比发现: photoFile文件的转化uri部分编写的不合理.原因:如果运行设备的系统版本 ...

  6. OpenGL编程指南 (红宝书 第八版) 样例代码配置问题汇总

    代码下载 环境配置问题glut 和 glew1 基本头文件和库文件配置2 以下是正文内容 第一个渲染程序Triangles3 第三章 03ch03_drawcommands4 本文是笔者自己在配置中遇 ...

  7. python定义构造函数、包括颜色价格品牌_《Python编程与算法基础教程》(第二版),蒋洪宇,青松,第9章:课后练习,程序设计,版江红余,第九章,习题,答案...

    例9.1~例9.53 补充: 类名为有效的标识符,一般为多个单词组成的名称,每个单词除第一个字母大写外,其余的字母均小写 一:类对象和实例对象 例9.1(创建类对象和实例对象) >>> ...

  8. Python编程:从入门到实践(第二版)随书敲代码 第六章 字典

    alien.py # 第六章 字典 # 6.1 一个简单的字典 alien_0 = {'color': 'green', 'points': '5'} print(alien_0['color']) ...

  9. 机器学习实践指南:案例应用解析(第二版)

    试读及购买链接 机器学习实践指南2版代码及资源(原书中的360网盘链接因为360关闭网盘的原因已经失效) 1.https://pan.baidu.com/s/1nw37A5N   2.http://w ...

  10. Python 自动化指南(繁琐工作自动化)第二版:十六、使用 CSV 文件和 JSON 数据

    原文:https://automatetheboringstuff.com/2e/chapter16/ 在第 15 章,你学习了如何从 PDF 和 Word 文档中提取文本.这些文件是二进制格式的,需 ...

最新文章

  1. 操作系统原理第六章:进程同步
  2. 如何远程链接linux服务器,如何远程登陆Linux/Windows服务器
  3. 实操将TensorFlow模型部署成Docker服务化
  4. TCP socket编程记录(C语言)
  5. python_Day5_web开发(下)
  6. ajax后台怎么取mapp,后台管理实现
  7. java jdk myeclipse_java初体验(JDK+myeclipse)
  8. 【java】SpringBoot新特性 节省百分之95﹪内存占用
  9. 高等数学一使用python实现极限后有代码
  10. [软件]Beyond Compare
  11. 入手 M1 Mac 之前,你可以通过这个工具了解常用软件兼容性
  12. 生成Apk签名证书keystore,openssl与证书,keystore,jks,pem/pk8
  13. linux proftpd 用户,[原]把boblog博客的用户导入proftpd用户认证数据库
  14. centos7脚本部署云盘(seafile)
  15. 基于skynet设计游戏服务端框架
  16. pytorch学习(二)梯度:什么叫梯度?什么叫梯度下降
  17. 谈谈百度/GOOGLE联盟和一般联盟的区别
  18. 鼠标使用板载内存和使用计算机上,鼠标怎么选?教你选择适合自己的游戏鼠标。...
  19. 树莓派Python教程:如何安装树莓派系统
  20. ONVIF Device Test Tool测试工具使用方法(详细)

热门文章

  1. mysql 8.0安装_MySQL5.7升级到8.0过程详解
  2. 学习记录 php,php 学习记录
  3. STM32——整形变量转换为字符变量并通过串口发送出去
  4. 多个客户端共用同一条拉起链接 ,如何配置 Universal Link iOS App
  5. 关于Tricomi方程的类型分析与标准型求解
  6. 295.数据流的中位数
  7. 2020 网易 面经
  8. matlab算薄板模态,基于MATLAB计算FGM薄板刚柔耦合动力学响应的仿真方法与流程
  9. )类 新建javafx程序时_JavaFX 类 RadioButton用法及代码示例
  10. 后台数据量太大传输慢_哪些因素会导致慢查询?