android 对话框(Dialog)使用(2)

参考:

android 对话框(Dialog)使用:http://blog.csdn.net/u012005313/article/details/58153917

之前总结了基本的关于 dialog 的使用,经过一段时间的学习,对 dialog 的使用又有了一些新的体会。下面加入一些新的操作,同时完善之前的内容。

主要内容

  1. 自定义对话框 - 登录界面的实现
  2. 设置 activity 为对话框样式
  3. material-dialogs - 开源对话框库使用
  4. DatePickerDialogTimePickerDialog 使用
  5. WheelView - 开源滚轮控件使用
  6. DialogFragment 使用
  7. ProgressDialog 使用
  8. showhidedismiss 使用
  9. 代码链接

自定义对话框 - 登录界面的实现

dialog 可实现自定义界面,比如登录对话框

首先设置登录界面 - dialog_login.xml:用户名和密码输入框,以及清除按钮和登录按钮

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="8dp">

    <TableLayout
        android:id="@+id/dl_table"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:stretchColumns="1"
        android:visibility="visible"
        tools:layout_editor_absoluteX="8dp"
        tools:layout_editor_absoluteY="8dp">

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:layout_width="60dp"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:text="用户" />

            <EditText
                android:id="@+id/et_user"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="60dp"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:text="密码" />

            <EditText
                android:id="@+id/et_password"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="textPassword" />

        </TableRow>

    </TableLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/btn_clear"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/btn_clear" />

        <Button
            android:id="@+id/btn_login"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/btn_login" />

    </LinearLayout>

</LinearLayout>

将该界面加载生成 View 对象:

View view = View.inflate(this, R.layout.dialog_login, null);

Note:View.inflate 详细内容

/**
 * Inflate a view from an XML resource.  This convenience method wraps the {@link
 * LayoutInflater} class, which provides a full range of options for view inflation.
 *
 * @param context The Context object for your activity or application.
 * @param resource The resource ID to inflate
 * @param root A view group that will be the parent.  Used to properly inflate the
 * layout_* parameters.
 * @see LayoutInflater
 */
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
    LayoutInflater factory = LayoutInflater.from(context);
    return factory.inflate(resource, root);
}

生成 dialog 对象,将自定义视图加载进去:

AlertDialog.Builder builder = new AlertDialog.Builder(this)
        .setView(view);

显示该登录对话框:

builder.show();

Note:调用 builder.show 等同于先创建 AlertDialog,再进行显示

/**
 * Creates an {@link AlertDialog} with the arguments supplied to this
 * builder and immediately displays the dialog.
 * <p>
 * Calling this method is functionally identical to:
 * <pre>
 *     AlertDialog dialog = builder.create();
 *     dialog.show();
 * </pre>
 */
public AlertDialog show() {
    final AlertDialog dialog = create();
    dialog.show();
    return dialog;
}

这样就完成了登录对话框的创建,如果想要进一步获取用户输入的信息,点击按钮可以进行操作,增加如下代码:

final EditText etUser = (EditText) view.findViewById(R.id.et_user);
final EditText etPassword = (EditText) view.findViewById(R.id.et_password);

Button btnClear = (Button) view.findViewById(R.id.btn_clear);
btnClear.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        etUser.setText(null);
        etPassword.setText(null);
    }
});

Button btnLogin = (Button) view.findViewById(R.id.btn_login);
btnLogin.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String user = etUser.getText().toString();
        String password = etPassword.getText().toString();

        Log.e(TAG, "onClick: user = " + user + " password = " + password);
        Toast.makeText(getApplicationContext(), "user = " + user + " password = " + password, Toast.LENGTH_SHORT).show();
    }
});

Note:如果想要符合 MD 规范,可以使用 AlertDialog 自带的按钮,还可以使用自带的标题栏

设置 activity 为对话框样式

继续上面的登录界面,现在已经实现了登录对话框,此时并不需要 activity 背景界面,可以设置 activity 主题为 dialog,消去空白的的登录界面。设置方式 - 修改 AndroidManifest.xml 中对应 activity,设置主题为 dialog 样式:

<activity
    android:name=".custom.CustomActivity"
    android:theme="@style/Theme.AppCompat.Light.Dialog.Alert"></activity>

但是还得解决登录框取消后,仍会出现背景的问题

可以使用 DialogInterface.OnKeyListener 接口:

/**
 * Interface definition for a callback to be invoked when a key event is
 * dispatched to this dialog. The callback will be invoked before the key
 * event is given to the dialog.
 */
interface OnKeyListener {
    /**
     * Called when a key is dispatched to a dialog. This allows listeners to
     * get a chance to respond before the dialog.
     * 
     * @param dialog The dialog the key has been dispatched to.
     * @param keyCode The code for the physical key that was pressed
     * @param event The KeyEvent object containing full information about
     *            the event.
     * @return True if the listener has consumed the event, false otherwise.
     */
    public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event);
}

具体思路:dialog 注册此接口,出现点击操作时,判断是否是返回键。若是,则关闭整个 activity

AlertDialog alertDialog = builder.create();

DialogInterface.OnKeyListener onKeyListener = new DialogInterface.OnKeyListener() {
    @Override
    public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            finish();

            return true;
        }
        return false;
    }
};

alertDialog.setOnKeyListener(onKeyListener);

同时还要设置点击屏幕对话框外的区域不结束 dialog

alertDialog.setCancelable(false);

完整代码如下:

View view = View.inflate(this, R.layout.dialog_login, null);

AlertDialog.Builder builder = new AlertDialog.Builder(this)
        .setView(view);

final EditText etUser = (EditText) view.findViewById(R.id.et_user);
final EditText etPassword = (EditText) view.findViewById(R.id.et_password);

Button btnClear = (Button) view.findViewById(R.id.btn_clear);
btnClear.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        etUser.setText(null);
        etPassword.setText(null);
    }
});

Button btnLogin = (Button) view.findViewById(R.id.btn_login);
btnLogin.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String user = etUser.getText().toString();
        String password = etPassword.getText().toString();

        Log.e(TAG, "onClick: user = " + user + " password = " + password);
        Toast.makeText(getApplicationContext(), "user = " + user + " password = " + password, Toast.LENGTH_SHORT).show();
    }
});

AlertDialog alertDialog = builder.create();

DialogInterface.OnKeyListener onKeyListener = new DialogInterface.OnKeyListener() {
    @Override
    public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            finish();

            return true;
        }
        return false;
    }
};

alertDialog.setOnKeyListener(onKeyListener);

alertDialog.setCancelable(false);

alertDialog.show();

参考:

AlertDialog点击按钮后对话框不消失的处理方法

此时,不管是否有输入,点击按钮后对话框自动消失,可通过设置按钮点击事件实现手动决定对话框是否消失。

实现方法:在对话框初始化阶段,设置按钮点击事件为 null;显示对话框后,设置按钮点击事件,即可手动决定是否 dismiss 对话框

修改后代码如下:

View view = View.inflate(this, R.layout.dialog_login, null);

final EditText etUser = (EditText) view.findViewById(R.id.et_user);
final EditText etPassword = (EditText) view.findViewById(R.id.et_password);

AlertDialog.Builder builder = new AlertDialog.Builder(this)
        .setTitle(getResources().getString(R.string.app_name))
        .setView(view)
        .setPositiveButton(getResources().getString(R.string.btn_login), null)
        .setNegativeButton(getResources().getString(R.string.btn_clear), null);

AlertDialog alertDialog = builder.create();

DialogInterface.OnKeyListener onKeyListener = new DialogInterface.OnKeyListener() {
    @Override
    public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            finish();

            return true;
        }
        return false;
    }
};

alertDialog.setOnKeyListener(onKeyListener);

alertDialog.setCancelable(false);

alertDialog.show();

alertDialog.getButton(Dialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String user = etUser.getText().toString();
        String password = etPassword.getText().toString();

        Log.e(TAG, "onClick: user = " + user + " password = " + password);
        Toast.makeText(getApplicationContext(), "user = " + user + " password = " + password, Toast.LENGTH_SHORT).show();
    }
});

alertDialog.getButton(Dialog.BUTTON_NEGATIVE).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        etUser.setText(null);
        etPassword.setText(null);
    }
});

material-dialogs - 开源对话框库使用

一个开源的,符合 MD 设计规范的对话框

github:material-dialogs

使用方式和 dialog 类似

DatePickerDialogTimePickerDialog 使用

android 内置了日期选择对话框和时间选择对话框组件

DatePickerDialog 使用

获取当前年,月,日:

Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);

创建 DatePickerDialog 并显示:

DatePickerDialog datePickerDialog = new DatePickerDialog(this, this, year, month, day);

datePickerDialog.show();

另外,需要设置回调接口 DatePickerDialog.OnDateSetListener

完整代码如下:

public class DatePickerActivity extends AppCompatActivity implements DatePickerDialog.OnDateSetListener {
    private static final String TAG = DatePickerActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_date_picker);

        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        int day = calendar.get(Calendar.DAY_OF_MONTH);

        DatePickerDialog datePickerDialog = new DatePickerDialog(this, this, year, month, day);

        datePickerDialog.show();
    }

    @Override
    public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
        Log.e(TAG, "onDateSet: year = " + year + " month = " + month + " day = " + dayOfMonth);
    }
}   

TimePickerDialog 使用

获取当前时间:

Calendar calendar = Calendar.getInstance();

int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);

创建 TimePickerDialog 并显示:

TimePickerDialog timePickerDialog = new TimePickerDialog(this, this, hour, minute, true);

timePickerDialog.show();

另外,需要设置回调接口 TimePickerDialog.OnDateSetListener

完整代码如下:

public class TimePickerActivity extends AppCompatActivity
        implements TimePickerDialog.OnTimeSetListener {
    private static final String TAG = TimePickerDialog.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_time_picker);

        Calendar calendar = Calendar.getInstance();

        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);

        TimePickerDialog timePickerDialog = new TimePickerDialog(this, this, hour, minute, true);

        timePickerDialog.show();
    }

    @Override
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        Log.e(TAG, "onTimeSet: h = " + hourOfDay + " m = " + minute);
    }
}

WheelView - 开源滚轮控件使用

android 没有滚轮控件,不过网上已经有了很多的开源滚轮控件,参考了其中两个

参考

  • android-wheel
  • venshine/WheelView

有时间详细学习一下

DialogFragment 使用

android 推荐使用 DialogFragment 用作对话框的容器,这样可保证能够正确处理生命周期事件,同时有利于复用

实现最简单的对话框使用

首先,扩展 DialogFragment 中的 onCreateDilog 方法,创建 AlertDialog 对象并返回:

public class MyDialogFragment extends DialogFragment {

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
//        return super.onCreateDialog(savedInstanceState);
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
                .setTitle("MyDialogFragment")
                .setMessage("DialogFragment")
                .setPositiveButton("OK", null)
                .setNegativeButton("Cancel", null);

        return builder.create();
    }
}

其次,在 Activity 中创建 MyDialogFragment 实例,并调用 show 方法即可显示对话框:

MyDialogFragment myDialogFragment = new MyDialogFragment();
myDialogFragment.show(getSupportFragmentManager(), "myDialogFragment");

Note:show 方法第二个参数是一个标识符

将事件传递回对话框的宿主

有时候需要将对话框获取到的数据,比如登录对话框中的用户名,密码,传递回 activity 中进行下一步的操作。 DialogFragment 并没有提供类似的功能,需要自定义相应的接口,进行下一步的操作

  • 首先,在 MyDialogFragment.java 中定义接口 MyDialogListener,包含接口函数 onDialogClick,用于按钮的回调:

    public interface MyDialogListener {
        void onDialogClick(String content, int type);
    }
    
  • MyDialogFragment.java 中定义该接口,同时在 onAttach 函数中实例化该接口:

    private MyDialogListener myDialogListener;
    
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            // Instantiate the NoticeDialogListener so we can send events to the host
            myDialogListener = (MyDialogListener) context;
        } catch (ClassCastException e) {
            // The activity doesn't implement the interface, throw exception
            throw new ClassCastException(context.toString()
                    + " must implement MyDialogListener");
        }
    }
    
  • onCreateDialog 函数中,创建 AlertDialog 过程中,设置按钮的点击事件,调用接口函数,点击 user 按钮,则返回用户名;点击 password 按钮,则返回密码:

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
    
        View view = View.inflate(getContext(), R.layout.dialog_login, null);
    
        final EditText etUser = (EditText) view.findViewById(R.id.et_user);
        final EditText etPassword = (EditText) view.findViewById(R.id.et_password);
    
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
                .setTitle("MyDialogFragment")
                .setView(view)
                .setPositiveButton("user", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        myDialogListener.onDialogClick(etUser.getText().toString(), 1);
                    }
                })
                .setNegativeButton("password", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        myDialogListener.onDialogClick(etPassword.getText().toString(), 2);
                    }
                });
    
        return builder.create();
    }
    
  • 在宿主 activity 中绑定该接口,实现接口函数功能:

    public class DialogFragmentActivity extends AppCompatActivity implements MyDialogFragment.MyDialogListener {
    
        private TextView textView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_dialog_fragment);
    
            textView = (TextView) findViewById(R.id.text);
    
            MyDialogFragment myDialogFragment = new MyDialogFragment();
            myDialogFragment.show(getSupportFragmentManager(), "myDialogFragment");
        }
    
        @Override
        public void onDialogClick(String content, int type) {
            if (type == 1) {
                textView.setText("user: " + content);
            } else {
                textView.setText("password: " + content);
            }
        }
    }
    

setCanceledOnTouchOutsidesetCancelable 使用

DialogFragment 没有内置函数 setCanceledOnTouchOutside,可以在创建 AlertDialog 时设置该函数:

AlertDialog alertDialog = builder.create();

alertDialog.setCanceledOnTouchOutside(false);

不应该直接调用 AlertDialog.setCancelable 方法,而是使用 DialogFragment 内置函数 setCancelable

myDialogFragment.setCancelable(false); 

Note:可通过调用 isCancelable 获取 DialogFragment 状态

取消 DialogFragment

调用 DialogFragment 内置函数 dismiss 即可:

@Override
protected void onDestroy() {
    super.onDestroy();
    myDialogFragment.dismiss();
}

ProgressDialog 使用

android 系统自带了一个进度条对话框 - ProgressDialog
在进行信息查询,数据上传时,可使用该对话框提示用户等待。

参考

ProgressDialog

创建 ProgressDialog 并显示

ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.setMessage("Hello World");
progressDialog.show();

Note:在程序中可能需要多次进行数据操作,可以创建一个 ProgressDialog 实例并保留下来

showhidedismiss 使用

  • 创建对话框实例后,需要调用 show 方法显示该对话框

  • 如果需要多次使用 ProgressDialog,只需要创建一个 ProgressDialog 实例,在不需要使用时调用 hide 函数进行隐藏:

    /**
     * Hide the dialog, but do not dismiss it.
     */
    public void hide() {
        if (mDecor != null) {
            mDecor.setVisibility(View.GONE);
        }
    }
    
  • 最后在结束时,可以调用 dismiss 函数将对话框清除:

    /**
     * Dismiss this dialog, removing it from the screen. This method can be
     * invoked safely from any thread.  Note that you should not override this
     * method to do cleanup when the dialog is dismissed, instead implement
     * that in {@link #onStop}.
     */
    @Override
    public void dismiss() {
        if (Looper.myLooper() == mHandler.getLooper()) {
            dismissDialog();
        } else {
            mHandler.post(mDismissAction);
        }
    }
    

代码链接

OSChina:https://git.oschina.net/zjZSTU/dialogdemo.git

你可能感兴趣的:(android,对话框)