AlertDiaglog对话框的使用
官方文档
弹出框,用于提示用户和进行简单交互,可以提供最多三个按钮,标题(title
)和提示信息(message
)可以通过方法直接设置,如果需要呈现复杂布局,也可以自定义布局并设置。
虽然现在有几个人用的app都会自定义这种弹出提示的UI,一般不会用android原生的控件,但我还是用的很多的(UI?不存在的,只能用MD凑合凑合这样子)。
这篇笔记主要集中在一般用法和可能会踩到的坑的解决办法,不会讨论代码原理啥的。
基本使用
通常使用builder来构建一个AlertDialog:
AlertDialog. Builder ( this )
. setTitle ( "Notice" )
. setMessage ( "Some Message" )
. setPositiveButton ( "OK" ) { _, _ - >
. create ( ) . show ( )
最简单的dialog,显示一些信息,点击ok隐藏。
注意,在.setPositiveButton("OK") { _, _ -> // do sth }
监听方法中,dialog.dismiss()
是自动调用的,所以不用写,NegativeButton
和NeutralButton
也一样。
自定义View
有时需要在dialog中显示输入框,提示让用户输入一些信息,在点击ok时处理输入信息。 先自定义一个布局layout_dialog_report.xml
:
< ? xml version= "1.0" encoding= "utf-8" ? >
< LinearLayout xmlns: android= "http://schemas.android.com/apk/res/android"
android: orientation= "vertical" android: layout_width= "match_parent"
android: layout_height= "match_parent" >
< EditText
android: id= "@+id/et_input"
android: layout_width= "match_parent"
android: layout_height= "wrap_content"
android: layout_margin= "16dp"
android: inputType= "text" / >
< / LinearLayout>
在dialog中使用:
val inputView = LayoutInflater. from ( this ) . inflate ( R. layout. layout_dialog_report, null)
val nameInputDialog = AlertDialog. Builder ( this )
. setTitle ( "请输入" )
. setView ( inputView)
. setPositiveButton ( "OK" ) { _, _ - > }
. setNegativeButton ( "Cancel" ) { _, _ - > }
. create ( )
nameInputDialog. show ( )
nameInputDialog. getButton ( DialogInterface. BUTTON_POSITIVE) . setOnClickListener {
PreferenceUtil. saveNickname ( inputView. et_report. text. toString ( ) )
nameInputDialog. dismiss ( )
checkJob ( )
}
注意:这里的坑就是必须使用nameInputDialog.getButton()
方法获取到button之后单独设置点击事件,使用.setPositiveButton("OK") { _, _ -> }
方法不起作用。
除了用setTitle()
和setIcon()
设置标题和icon之外,还可以用setCustomTitle (View customTitleView)
方法自定义标题样式。
选择一项的dialog
列出一组可供选择的选项,主要使用setItems()
方法,该方法可以接受资源文件中的一组array
,或一个CharSequence
数组。
val jobList = arrayOf ( "Python" , "Java" , "JavaScript" , "Kotlin" , "C++" )
AlertDialog. Builder ( this )
. setTitle ( "Notice" )
. setItems ( jobList) { dialog, which - >
val selesctItem = jobList[ which]
}
. create ( ) . show ( )
和一般按钮一样,这些选项也是点击之后会自动调用dismiss()
方法隐藏。
多选项的dialog
可供多选的列表,需要提供一组数据,同时提供一个boolean值数组,设置的监听器将监听到多选的操作变化,手动将多选的结果将保存在这个数组中。
val dbList = arrayOf ( "Python" , "Java" , "JavaScript" , "Kotlin" , "C++" )
val chooseList = arrayOf ( false , false , false , false ) . toBooleanArray ( )
AlertDialog. Builder ( activity)
. setTitle ( "Notice" )
. setMultiChoiceItems ( dbList, chooseList) { _, which, isChecked - >
chooseList[ which] = isChecked
}
. setPositiveButton ( "OK" ) { _, _ - >
for ( i in chooseList. indices) {
if ( chooseList[ i] ) {
}
}
}
. create ( ) . show ( )
文档中还提供了以cursor为参数的设置方法:
public AlertDialog. Builder setMultiChoiceItems ( Cursor cursor,
String isCheckedColumn,
String labelColumn,
DialogInterface. OnMultiChoiceClickListener listener)
单选项的dialog
这个单选与上面的单选区别在于上面那个是点击之后即时反应的,而这一种是类似多选项,选中之后还可以更改,点击按钮确定之后才会处理。具体可以看效果图。 类似于多选项的设置方式,提供array资源或者一组字符串,也可以使用cursor,并且多了一个使用ListAdapter
的方法,第二个参数是默认选中的item index,设置为-1则表示没有默认选中的。
val items = arrayOf ( "同时转发到QQ" , "同时转发到微信" , "同时转发到微博" ) ;
AlertDialog. Builder ( this )
. setSingleChoiceItems ( items, 1 ) { _, which - >
val item = items[ which]
}
. setPositiveButton ( "OK" ) { _, _ - >
}
. create ( ) . show ( )
在dialog上显示dialog
有时点击确定之后,需要在这一层上再弹出一层dialog作为二次提示或者警告,但一般确认按钮都会自动调用dismiss()
使dialog消失。这里有两个解决办法,一是通过反射把底层dialog的状态设置为不显示,这样dismiss()
方法就不起作用,在上层选择完之后,再手动选择是否隐藏底层dialog。原理是dismiss()
方法会先判断"mShowing"
这个属性然后再隐藏dialog。 一个两层dialog的例子:
AlertDialog. Builder ( this )
. setTitle ( "Notice" )
. setItems ( jobList) { out, which - >
val field = out? . javaClass? . superclass? . getDeclaredField (
"mShowing" )
field? . isAccessible = true
field? . set ( out, false )
AlertDialog. Builder ( this )
. setTitle ( jobList[ which] )
. setMessage ( PreferenceUtil. getDescForJob ( jobList[ which] ) )
. setPositiveButton ( "OK" ) { inner, _ - >
PreferenceUtil. setJob ( jobList[ which] )
EventUtil. onEvent ( this , chooseJob, jobList[ which] )
inner. dismiss ( )
field? . set ( out, true )
out. dismiss ( )
}
. setNegativeButton ( "Cancel" ) { dialog, _ - > dialog. dismiss ( ) }
. create ( ) . show ( )
}
. create ( ) . show ( )
第二种方法就是前面说的自定义View作为dialog的内容,则点击按钮不会自动调用dismiss()
,也就不会自动消失了。
[上述部分原文链接:https://www.jianshu.com/p/f96196944918] (https://www.jianshu.com/p/f96196944918)
自定义AlertDialog的一般实现举例
Android默认的AlertDialog太单调,我们可以通过继承原生的Dialog来实现自定义的Dialog。
本文的自定义Dialog和原生的AlertDialog的创建方式类似,通过一个静态Builder类来设置Dialog的图标、标题、内容和按钮。
如果想要在Dialog中使用输入框或者其他控件,方法也是类似的,只要写好布局再加载就可以了。
Github:https://github.com/imcloudfloating/DesignApp
效果:
布局文件代码:
(注意这里的根布局的宽高如果用match_parent或者设置为具体的数值都和wrap_conten效果一样,可以通过设置子控件的大小来撑开)
< ? xml version= "1.0" encoding= "utf-8" ? >
< android. support. constraint. ConstraintLayout
xmlns: android= "http://schemas.android.com/apk/res/android"
xmlns: app= "http://schemas.android.com/apk/res-auto"
xmlns: tools= "http://schemas.android.com/tools"
android: layout_width= "wrap_content"
android: layout_height= "wrap_content"
android: background= "#ffffff" >
< LinearLayout
android: id= "@+id/dialog_header"
android: orientation= "vertical"
android: layout_width= "220dp"
android: layout_height= "wrap_content"
android: padding= "16dp"
android: gravity= "center"
android: background= "@color/colorGreen"
app: layout_constraintTop_toTopOf= "parent"
app: layout_constraintStart_toStartOf= "parent"
app: layout_constraintEnd_toEndOf= "parent" >
< ! -- Icon -- >
< ImageView
android: contentDescription= "@id/dialog_title"
android: id= "@+id/dialog_icon"
android: layout_width= "100dp"
android: layout_height= "100dp"
android: src= "@drawable/ic_check_circle" / >
< ! -- Title(default is gone) -- >
< TextView
android: id= "@+id/dialog_title"
android: layout_width= "wrap_content"
android: layout_height= "wrap_content"
android: padding= "8dp"
android: textSize= "18sp"
android: textStyle= "bold"
android: textColor= "#ffffff"
android: visibility= "gone" / >
< / LinearLayout>
< LinearLayout
android: orientation= "vertical"
android: layout_width= "wrap_content"
android: layout_height= "wrap_content"
android: padding= "16dp"
android: gravity= "center"
app: layout_constraintTop_toBottomOf= "@+id/dialog_header"
app: layout_constraintStart_toStartOf= "parent"
app: layout_constraintEnd_toEndOf= "parent"
app: layout_constraintBottom_toBottomOf= "parent" >
< ! -- Dialog Message -- >
< TextView
android: id= "@+id/dialog_message"
android: layout_width= "wrap_content"
android: layout_height= "wrap_content"
android: padding= "8dp"
tools: text= "Dialog Message" / >
< Button
android: id= "@+id/dialog_button"
android: layout_width= "100dp"
android: layout_height= "42dp"
android: layout_marginTop= "16dp"
android: layout_marginBottom= "8dp"
android: background= "@drawable/bg_dialog_button"
android: textColor= "#ffffff"
android: text= "@string/dialog_button" >
< / Button>
< / LinearLayout>
< / android. support. constraint. ConstraintLayout>
InfoDialog类:
package com. cloud. design. dialog;
import android. app. Dialog;
import android. content. Context;
import android. graphics. Bitmap;
import android. support. annotation. NonNull;
import android. view. LayoutInflater;
import android. view. View;
import android. view. ViewGroup;
import android. widget. Button;
import android. widget. ImageView;
import android. widget. TextView;
import com. cloud. design. R;
public class InfoDialog extends Dialog {
private InfoDialog ( Context context, int themeResId) {
super ( context, themeResId) ;
}
public static class Builder {
private View mLayout;
private ImageView mIcon;
private TextView mTitle;
private TextView mMessage;
private Button mButton;
private View. OnClickListener mButtonClickListener;
private InfoDialog mDialog;
public Builder ( Context context) {
mDialog = new InfoDialog ( context, R. style. Theme_AppCompat_Dialog) ;
LayoutInflater inflater =
( LayoutInflater) context. getSystemService ( Context. LAYOUT_INFLATER_SERVICE) ;
mLayout = inflater. inflate ( R. layout. dialog, null, false ) ;
mDialog. addContentView ( mLayout, new ViewGroup. LayoutParams ( ViewGroup. LayoutParams. MATCH_PARENT,
ViewGroup. LayoutParams. WRAP_CONTENT) ) ;
mIcon = mLayout. findViewById ( R. id. dialog_icon) ;
mTitle = mLayout. findViewById ( R. id. dialog_title) ;
mMessage = mLayout. findViewById ( R. id. dialog_message) ;
mButton = mLayout. findViewById ( R. id. dialog_button) ;
}
public Builder setIcon ( int resId) {
mIcon. setImageResource ( resId) ;
return this ;
}
public Builder setIcon ( Bitmap bitmap) {
mIcon. setImageBitmap ( bitmap) ;
return this ;
}
public Builder setTitle ( @NonNull String title) {
mTitle. setText ( title) ;
mTitle. setVisibility ( View. VISIBLE) ;
return this ;
}
public Builder setMessage ( @NonNull String message) {
mMessage. setText ( message) ;
return this ;
}
public Builder setButton ( @NonNull String text, View. OnClickListener listener) {
mButton. setText ( text) ;
mButtonClickListener = listener;
return this ;
}
public InfoDialog create ( ) {
mButton. setOnClickListener ( view - > {
mDialog. dismiss ( ) ;
mButtonClickListener. onClick ( view) ;
} ) ;
mDialog. setContentView ( mLayout) ;
mDialog. setCancelable ( true ) ;
mDialog. setCanceledOnTouchOutside ( false ) ;
return mDialog;
}
}
}
弹出:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate ( Bundle savedInstanceState) {
super . onCreate ( savedInstanceState) ;
setContentView ( R. layout. activity_main) ;
findViewById ( R. id. button_show_dialog) . setOnClickListener ( v - > {
InfoDialog infoDialog = new InfoDialog. Builder ( this )
. setTitle ( "Done" )
. setMessage ( "Something done" )
. setButton ( "OK" , view - >
Toast. makeText ( this , "OK Clicked." , Toast. LENGTH_SHORT) . show ( )
) . create ( ) ;
infoDialog. show ( ) ;
} ) ;
}
}
上述部分原文链接:https://www.cnblogs.com/cloudfloating/p/9811380.html