前言
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. 来个动画:
<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"/>
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