通过DialogFragment玩转高仿IOS弹框~

本文介绍了如何使用DialogFragment在Android中创建高仿IOS风格的弹窗,包括加载框、提示框和选择照片的实现。DialogFragment具有良好的横竖屏切换兼容性,文章还提供了具体的代码示例和对外公开的调用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

        LZ虽说是搞Android的,但是对IOS的风格样式甚是酷爱,感觉简约大方,而今天,我们通过DialogFragment一起开启高仿之路~篇幅所限,文章有删减,可以点击左下角“阅读原文”查看全部内容。


本文目标

        今天我们的目标简单可以概括为,通过DialogFragment高仿IOS弹框效果并对外公开,让大家可以通过一个地址调用,具体分为以下三部分:

  • 玩转DialogFragment;

  • 高仿IOS弹框效果;

  • 上传jcenter,方便有需要的人直接compile。


玩转DialogFragment

        在此,不知道的大家可能会说,DialogFragment这是什么鬼?哈哈,主要LZ之前是真心没了解过这个东东,恕我孤陋寡闻~

        下面为大家简单介绍下,主要还是LZ自己回顾下~ ^_^

DialogFragment简介

        DialogFragment是在Android3.0(API level 11)中引入的,主要是为了替代AlertDialog(重点)。

DialogFragment优势

  • DialogFragment和Fragment生命周期基本一致,方便管理;

  • 完美兼容横竖屏切换,让你想怎么切,就怎么切~而我们之前使用的AlertDialog就out了~

        话说上面说到我们之前玩转的AlertDialog对于横竖屏切换有点弱,有的伙伴说了,光说不练假把式,来个demo瞅瞅~


论:DialogFragment于AlertDialog横竖屏切换对比

        首先编写一个AlertDialog例子,如下:

        // AlertDialog
        findViewById(R.id.btn_alert_dialog).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new AlertDialog.Builder(selfActivity).setTitle("测试标题").setMessage("Hello Word~!").setPositiveButton("哈喽", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialogInterface.dismiss();
                    }
                }).show();
            }
        });

        接着编写一个简单的DialogFragment例子,如下:


1. 定义LoadingDialog类

package cn.hlq.iosdialog.manager;

import android.app.DialogFragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.TextView;

import cn.hlq.iosdialog.R;

/**
 * create by heliquan at 2017年7月30日
 * Android 普通加载框
 */
public class LoadingDialog extends DialogFragment {

    /**
     * 默认点击外面无效
     */
    private boolean onTouchOutside = false;

    /**
     * 加载框提示信息 设置默认
     */
    private String hintMsg = "正在加载,请稍后...";

    /**
     * 设置是否允许点击外面取消
     *
     * @param onTouchOutside
     * @return
     */
    public LoadingDialog setOnTouchOutside(boolean onTouchOutside) {
        this.onTouchOutside = onTouchOutside;
        return this;
    }

    /**
     * 设置加载框提示信息
     *
     * @param hintMsg
     */
    public LoadingDialog setHintMsg(String hintMsg) {
        if (!hintMsg.isEmpty()) {
            this.hintMsg = hintMsg;
        }
        return this;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // 设置背景透明
        getDialog().getWindow().setBackgroundDrawableResource(android.R.color.transparent);
        // 去掉标题 死恶心死恶心的
        getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
        // set cancel on touch outside
        getDialog().setCanceledOnTouchOutside(onTouchOutside);
        View loadingView = inflater.inflate(R.layout.hlq_android_dialog_loading, container);
        TextView hintTextView = loadingView.findViewById(R.id.tv_loading_dialog_hint);
        hintTextView.setText(hintMsg);
        return loadingView;
    }

}

        

        代码编写完毕后,下面为大家附上对比图:

        

        错误日志如下:

08-06 13:23:35.774 3403-3403/cn.hlq.iosdialog E/WindowManager: android.view.WindowLeaked: Activity cn.hlq.iosdialog.MainActivity has leaked window com.android.internal.policy.PhoneWindow$DecorView{eca8948 V.E...... R....... 0,0-1368,632} that was originally added here
                                                                   at android.view.ViewRootImpl.(ViewRootImpl.java:368)
                                                                   at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:299)
                                                                   at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
                                                                   at android.app.Dialog.show(Dialog.java:319)
                                                                   at android.app.AlertDialog$Builder.show(AlertDialog.java:1112)
                                                                   at cn.hlq.iosdialog.MainActivity$1.onClick(MainActivity.java:40)
                                                                   at android.view.View.performClick(View.java:5198)
                                                                   at android.view.View$PerformClick.run(View.java:21147)
                                                                   at android.os.Handler.handleCallback(Handler.java:739)
                                                                   at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                   at android.os.Looper.loop(Looper.java:148)
                                                                   at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                   at java.lang.reflect.Method.invoke(Native Method)
                                                                   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

        消失原因为,切换屏幕时,dialog没有被重新创建,从而造成窗口泄露。如果依然要使用AlertDialog而且不想横竖屏切换消失,LZ提供最简单方法去解决: 切换屏幕时候重新创建AlertDilog。(哈哈,不要打我~)


二、高仿IOS弹框效果

        首先还是老规矩,看看效果,不然说不过去哈~

        录制的效果不是很好,稍微有点模糊,Sorry~我们将依次为大家介绍,瞧好吧您那~


仿IOS加载框 -> 传说中菊花转~ <-

        首先要明确一个实现过程,具体如下:

        1.继承DialogFragment,重写onCreateView();

        2.编写布局文件,动画;

        简单概括如上,下面依次附上源码,比较简单~


1. 定义IOSLoadingDialog菊花转类:

package cn.hlq.iosdialog.manager;

import android.app.DialogFragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.TextView;

import cn.hlq.iosdialog.R;

/**
 * Created by HLQ on 2017/7/31
 */

public class IOSLoadingDialog extends DialogFragment {

    /**
     * 默认点击外面无效
     */
    private boolean onTouchOutside = false;

    /**
     * 加载框提示信息 设置默认
     */
    private String hintMsg = "正在加载";

    /**
     * 设置是否允许点击外面取消
     *
     * @param onTouchOutside
     * @return
     */
    public IOSLoadingDialog setOnTouchOutside(boolean onTouchOutside) {
        this.onTouchOutside = onTouchOutside;
        return this;
    }

    /**
     * 设置加载框提示信息
     *
     * @param hintMsg
     */
    public IOSLoadingDialog setHintMsg(String hintMsg) {
        if (!hintMsg.isEmpty()) {
            this.hintMsg = hintMsg;
        }
        return this;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // 设置背景透明
        getDialog().getWindow().setBackgroundDrawableResource(android.R.color.transparent);
        // 去掉标题 死恶心死恶心的
        getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
        // set cancel on touch outside
        getDialog().setCanceledOnTouchOutside(onTouchOutside);
        View loadingView = inflater.inflate(R.layout.hlq_ios_dialog_loading, container);
        TextView hintTextView = loadingView.findViewById(R.id.tv_ios_loading_dialog_hint);
        hintTextView.setText(hintMsg);
        return loadingView;
    }

}


2. 定义布局文件:

        略


3. 来个动画:

&lt;animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
                 android:drawable="@drawable/ic_ios_loading"
                 android:fromDegrees="0.0"
                 android:pivotX="50.0%"
                 android:pivotY="50.0%"
                 android:toDegrees="-360.0"/&gt;


4. 调用方式:

        // 高仿IOS加载框
        findViewById(R.id.btn_ios_loading_dialog).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                IOSLoadingDialog iosLoadingDialog = new IOSLoadingDialog().setOnTouchOutside(true);
                iosLoadingDialog.show(selfActivity.getFragmentManager(), "iosLoadingDialog");
            }
        });

        回过头,我们来瞅瞅对外提供的方法:

        设置是否允许点击外面取消;

        > 默认为false,可以通过setOnTouchOutside()设置是否允许点击外面取消。

        设置加载框提示信息;

        > 默认为“正在加载”,可以通过setHintMsg()传入需要设置加载框提示信息。


仿IOS提示框

        关于提示框,常用的一般有俩种类型,如下:

        1.提供用户俩个按钮,可确定或取消;

        2.只提供一个按钮,主要用于相关提示,提醒。

        而实现流程,相对来说比较简单,今天主要是通过ViewStub去控制不同的显示隐藏,简单概括如下:

        1.继承DialogFragment,重写onCreateView;

        2.编写俩个按钮以及单个按钮布局;

        3.新增按下效果,对外开放相关方法内容。


1. 编写HintDialog类

package cn.hlq.iosdialog.manager;

import android.app.DialogFragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.Window;
import android.widget.TextView;

import cn.hlq.iosdialog.R;

/**
 * 提示框 Created by HLQ on 2017/6/15
 */
public class HintDialog extends DialogFragment {

    private TextView tvTitle; // 标题
    private TextView tvContent; // 内容
    private TextView tvCancelTextView; // 取消
    private TextView tvConfirmTextView; // 确定
    private TextView tvSingleTextView; // 单个按钮

    /**
     * 确认回调
     */
    private HintConfirmCallback confirmCallback;
    /**
     * 取消回调
     */
    private HintCancelCallback cancelCallback;
    /**
     * 单选回调
     */
    private HintSingleCallback singleCallback;

    private boolean isSingleButton = false; // 是否启用单个按钮

    /**
     * 默认点击外面无效
     */
    private boolean onTouchOutside = false;

    /**
     * 标题
     */
    private String title = "提示";

    /**
     * 内容
     */
    private String content;

    private String confirm, cancel; // 确定 取消 可单独定制

    /**
     * 设置确定按钮内容
     *
     * @param confirmMsg
     * @return
     */
    public HintDialog setOnConfirmBtnText(String confirmMsg) {
        this.confirm = confirmMsg;
        return this;
    }

    /**
     * 设置取消按钮内容
     *
     * @param cancelMsg
     * @return
     */
    public HintDialog setOnCancelBtnText(String cancelMsg) {
        this.cancel = cancelMsg;
        return this;
    }

    /**
     * 设置是否启用单个按钮
     *
     * @param isSingle
     * @return
     */
    public HintDialog setIsSingleButton(boolean isSingle) {
        this.isSingleButton = isSingle;
        return this;
    }

    /**
     * 设置是否允许点击外面
     *
     * @param onTouchOutside
     * @return
     */
    public HintDialog setOnTouchOutside(boolean onTouchOutside) {
        this.onTouchOutside = onTouchOutside;
        return this;
    }

    /**
     * 设置标题
     *
     * @param title
     * @return
     */
    public HintDialog setTitle(String title) {
        if (!title.isEmpty()) {
            this.title = title;
        }
        return this;
    }

    /**
     * 设置内容
     *
     * @param content
     * @return
     */
    public HintDialog setContent(String content) {
        this.content = content;
        return this;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // 设置背景透明
        getDialog().getWindow().setBackgroundDrawableResource(android.R.color.transparent);
        // 去掉标题 死恶心死恶心的
        getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
        // set cancel on touch outside
        getDialog().setCanceledOnTouchOutside(onTouchOutside);
        View hintView = inflater.inflate(R.layout.hlq_dialog_hint, null);
        initView(hintView);
        return hintView;
    }

    /**
     * 初始化View
     *
     * @param hintView
     */
    private void initView(View hintView) {
        tvTitle = hintView.findViewById(R.id.tv_hint_dialog_title);
        tvContent = hintView.findViewById(R.id.tv_hint_dialog_content);
        tvTitle.setText(title);
        tvContent.setText(content);
        if (isSingleButton) {
            ViewStub vsSingleButton = hintView.findViewById(R.id.vs_single_button);
            vsSingleButton.inflate();
            tvSingleTextView = hintView.findViewById(R.id.tv_single);
            tvSingleTextView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    singleCallback.onClick();
                }
            });
        } else {
            ViewStub vsDoubleButton = hintView.findViewById(R.id.vs_double_button);
            vsDoubleButton.inflate();
            tvCancelTextView = hintView.findViewById(R.id.btn_hint_dialog_cancle);
            tvConfirmTextView = hintView.findViewById(R.id.btn_hint_dialog_confirm);
            if (!"".equals(confirm) && confirm != null) {
                tvConfirmTextView.setText(confirm);
            }
            if (!"".equals(cancel) && cancel != null) {
                tvCancelTextView.setText(cancel);
            }
            tvConfirmTextView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    confirmCallback.onClick();
                }
            });
            tvCancelTextView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    cancelCallback.onClick();
                }
            });
        }
    }

    /**
     * 确定点击事件
     *
     * @param confirmCallback
     * @return
     */
    public HintDialog setOnConfirmClickListener(HintConfirmCallback confirmCallback) {
        this.confirmCallback = confirmCallback;
        return this;
    }

    /**
     * 取消点击事件
     *
     * @param cancelCallback
     * @return
     */
    public HintDialog setOnCancelClickListener(HintCancelCallback cancelCallback) {
        this.cancelCallback = cancelCallback;
        return this;
    }

    /**
     * 单个按钮点击事件
     *
     * @param singleCallback
     * @return
     */
    public HintDialog setOnSingleClickListener(HintSingleCallback singleCallback) {
        this.singleCallback = singleCallback;
        return this;
    }

    /**
     * 确认回调
     */
    public interface HintConfirmCallback {

        void onClick();

    }

    /**
     * 取消回调
     */
    public interface HintCancelCallback {

        void onClick();

    }

    /**
     * 单个按钮回调
     */
    public interface HintSingleCallback {

        void onClick();

    }

}


2. 编写布局文件
主布局;

单个按钮布局;

对应的按下样式文件;

俩个按钮布局;

对应按下样式;
左侧;

右侧。


3. 调用方式

       // 提示框
        findViewById(R.id.btn_ios_hint_dialog).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                hintDialog.setContent("确定要离开吗?");
                hintDialog.setOnConfirmClickListener(new HintDialog.HintConfirmCallback() {
                    @Override
                    public void onClick() {
                        hintDialog.dismiss();
                        Toast.makeText(selfActivity, "点击确定", Toast.LENGTH_SHORT).show();
                    }
                });
                hintDialog.setOnCancelClickListener(new HintDialog.HintCancelCallback() {
                    @Override
                    public void onClick() {
                        hintDialog.dismiss();
                        Toast.makeText(selfActivity, "点击取消", Toast.LENGTH_SHORT).show();
                    }
                });
                hintDialog.show(selfActivity.getFragmentManager(), "hintDialog");
            }
        });
        // 单个提示框
        findViewById(R.id.btn_ios_single_hint_dialog).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                singleHintDialog.setContent("请认真填写相关信息,谢谢合作~").setIsSingleButton(true);
                singleHintDialog.setOnSingleClickListener(new HintDialog.HintSingleCallback() {
                    @Override
                    public void onClick() {
                        singleHintDialog.dismiss();
                        Toast.makeText(selfActivity, "点击确认~", Toast.LENGTH_SHORT).show();
                    }
                });
                singleHintDialog.show(selfActivity.getFragmentManager(), "singleHintDialog");
            }
        });


对外提供方法如下:

  • setTitle() 设置标题 ,默认为“提示”;

  • setContent() 设置内容;

  • setIsSingleButton() 是否启用单个按钮模式;

  • setOnTouchOutside() 是否允许点击外面消失 默认不允许

仿IOS选择照片 选取相册

首先明确实现过程,如下:

  • 继承DialogFragment,重写onCreateView,并设置底部显示;

  • 设置布局文件以及按下效果;

  • 对外提供监听回调~


        仿IOS选择照片选取相册与上传jcenter,方便有需要的人直接compile方法可点击左下角“阅读原文”查看。


项目Github地址:

 https://github.com/HLQ-Struggle/IOSDialog


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值