前言
文章代码示例已放到 Github 上了, 有需要的朋友可以去看下 TDialog , 欢迎 star 和 fork, 项目会一直维护, 有疑问可以提 Issues 或留言.
文章目录
TDialog 框架的由来
框架使用解析
框架原理解析
正文开始前先来一波效果图
一. TDialog 的由来
所有框架的由来都是为了更方便, 更高效的解决问题, TDialog 也一样, 是为了在项目中更高效的实现项目的弹窗效果
TDialog 是继承自 DialogFragment 进行封装的, 大部分开发者在实现弹窗效果的时候, 会首选系统提供的 AlertDialog; 但是使用系统的 Dialog 在某些情况下会出现问题, 最常见的场景是当手机屏幕旋转时 Dialog 弹窗会消失, 并抛出一个系统, 这个异常不会导致异常崩溃, 因为 Google 开发者知道这个问题, 并进行了处理. Dialog 使用起来其实更简单, 但是 Google 却是推荐尽量使用 DialogFragment.
1.DialogFragment 的优点
DialogFragment 本身是 Fragment 的子类,有着和 Fragment 基本一样的生命周期,使用 DialogFragment 来管理对话框,当旋转屏幕和按下后退键的时候可以更好的管理其生命周期
在手机配置变化导致 Activity 需要重新创建时,例如旋转屏幕,基于 DialogFragment 的对话框将会由 FragmentManager 自动重建,然而基于 Dialog 实现的对话框却没有这样的能力
使用
1. 在项目 build.gradle 文件中添加依赖
compile 'com.timmy.tdialog:tdialog:1.1.3'
2.Activity 或者 Fragment 中使用
使用方法解析
new TDialog.Builder(getSupportFragmentManager())
.setLayoutRes(R.layout.dialog_click)
.setWidth(600)
.setHeight(800)
.setScreenWidthAspect(DialogEncapActivity.this,0.5f)
.setScreenHeightAspect(DialogEncapActivity.this,0.6f)
.setTag("DialogTest")
.setDimAmount(0.6f)
.setGravity(Gravity.CENTER)
.setOnBindViewListener(new OnBindViewListener() {
@Override
public void bindView(BindViewHolder bindViewHolder) {
bindViewHolder.setText(R.id.tv_content, "abcdef");
}
})
.addOnClickListener(R.id.btn_right, R.id.tv_title)
.setOnViewClickListener(new OnViewClickListener() {
@Override
public void onViewClick(BindViewHolder viewHolder,View view, TDialog tDialog) {
}
})
.create()
.show();
TDialog 的实现原理和系统 Dialog 原理差不多, 主要使用 Builder 设计模式实现
1. 创建弹窗, 必须传入 xml 布局文件, 且自己设置背景色, 因为默认是透明背景色
2. 设置弹窗的宽高 (如果不设置宽或者高, 默认使用包裹内容的高度)
new TDialog.Builder(getSupportFragmentManager())
.setLayoutRes(R.layout.dialog_click)
.create()
.show();
3. 设置弹窗展示的位置
new TDialog.Builder(getSupportFragmentManager())
.setLayoutRes(R.layout.dialog_click)
.setWidth(600) //设置弹窗固定宽度(单位:px)
.setHeight(800)//设置弹窗固定高度
.setScreenWidthAspect(Activity.this,0.5f) //动态设置弹窗宽度为屏幕宽度百分比(取值0-1f)
.setScreenHeightAspect(Activity.this,0.6f)//设置弹窗高度为屏幕高度百分比(取值0-1f)
.create()
.show();
.setGravity(Gravity.CENTER)
其他位置有:Gravity.Bottom / Gravity.LEFT等等和设置控件位置一样
4. 设置弹窗背景色透明度 (取值 0-1f,0 为全透明)
.setDimAmount(0.6f)
5. 当弹窗需要动态改变控件子 view 内容时, 这里借鉴了 RecyclerView.Adapter 的设计思想, 内部封装好一个 BindViewHolder
6. 监听弹窗子控件的点击事件, 内部也是通过 BindViewHolder 实现 addOnClickListener(ids[]) 只需要将点击事件控件的 id 传入, 并设置回调接口 setOnViewClickListener()
.setOnBindViewListener(new OnBindViewListener() {
@Override
public void bindView(BindViewHolder bindViewHolder) {
bindViewHolder.setText(R.id.tv_content, "abcdef");
}
})
7. 设置底部列表弹窗
.addOnClickListener(R.id.btn_right, R.id.tv_title)
.setOnViewClickListener(new OnViewClickListener() {
@Override
public void onViewClick(BindViewHolder viewHolder,View view1, TDialog tDialog) {
switch (view1.getId()) {
case R.id.btn_right:
Toast.makeText(DialogEncapActivity.this, "btn_right", Toast.LENGTH_SHORT).show();
tDialog.dismiss();
break;
case R.id.tv_title:
Toast.makeText(DialogEncapActivity.this, "tv_title", Toast.LENGTH_SHORT).show();
break;
}
}
})
列表弹窗
new TDialog.Builder(getSupportFragmentManager())
.setHeight(600)
.setScreenWidthAspect(this, 0.8f)
.setGravity(Gravity.CENTER)
.setAdapter(new TBaseAdapter<String>(R.layout.item_simple_text, Arrays.asList(data)) {
@Override
protected void onBind(BindViewHolder holder, int position, String s) {
holder.setText(R.id.tv, s);
}
})
.setOnAdapterItemClickListener(new TBaseAdapter.OnAdapterItemClickListener<String>() {
@Override
public void onItemClick(BindViewHolder holder, int position, String s, TDialog tDialog) {
Toast.makeText(DiffentDialogActivity.this, "click:" + s, Toast.LENGTH_SHORT).show();
tDialog.dismiss();
}
})
.create()
.show();
为了方便使用:
不用传入 layoutRes 布局文件, TDialog 内部设置了一个默认的 RecyclerView 布局, 且 RecyclerView 的控件 id 为 recycler_view, 背景为 #ffffff
setAdapter(Adapter), 设置 recyclerview 的 adapter, 为了封装 Adapter 的 item 点击事件, 传入的 adapter 需要为 TBaseAdapter 的实现类
setOnAdapterItemClickListener(), 设置 adapter 的点击事件
TBaseAdapter 实现: 需要使用者传入 item 的 xml 布局, 和 List 数据
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
android:orientation="vertical" />
如果使用者需要使用自己的列表布局时, 可以使用 setListLayoutRes(layotuRes,LayoutManager) 方法设置 xml 布局和布局管理器 LayoutManager, 切记 xml 布局中的 RecyclerView 的 id 必须设置为 recycler_view(如效果图中的分享弹窗)
public abstract class TBaseAdapter<T> extends RecyclerView.Adapter<BindViewHolder> {
private final int layoutRes;
private List<T> datas;
private OnAdapterItemClickListener adapterItemClickListener;
private TDialog dialog;
protected abstract void onBind(BindViewHolder holder, int position, T t);
public TBaseAdapter(@LayoutRes int layoutRes, List<T> datas) {
this.layoutRes = layoutRes;
this.datas = datas;
}
@Override
public BindViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new BindViewHolder(LayoutInflater.from(parent.getContext()).inflate(layoutRes, parent, false));
}
@Override
public void onBindViewHolder(final BindViewHolder holder, final int position) {
onBind(holder, position, datas.get(position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
adapterItemClickListener.onItemClick(holder, position, datas.get(position), dialog);
}
});
}
@Override
public int getItemCount() {
return datas.size();
}
public void setTDialog(TDialog tDialog) {
this.dialog = tDialog;
}
public interface OnAdapterItemClickListener<T> {
void onItemClick(BindViewHolder holder, int position, T t, TDialog tDialog);
}
public void setOnAdapterItemClickListener(OnAdapterItemClickListener listener) {
this.adapterItemClickListener = listener;
}
}
自定义列表布局
//底部分享
public void shareDialog(View view) {
new TDialog.Builder(getSupportFragmentManager())
.setListLayoutRes(R.layout.dialog_share_recycler, new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false))
.setScreenWidthAspect(this, 1.0f)
.setGravity(Gravity.BOTTOM)
.setAdapter(new TBaseAdapter<String>(R.layout.item_share, Arrays.asList(sharePlatform)) {
@Override
protected void onBind(BindViewHolder holder, int position, String s) {
holder.setText(R.id.tv, s);
}
})
.setOnAdapterItemClickListener(new TBaseAdapter.OnAdapterItemClickListener<String>() {
@Override
public void onItemClick(BindViewHolder holder, int position, String item, TDialog tDialog) {
Toast.makeText(DiffentDialogActivity.this, item, Toast.LENGTH_SHORT).show();
tDialog.dismiss();
}
})
.create()
.show();
}
框架原理解析
<?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="match_parent"
android:layout_height="match_parent"
android:background="#242424">
<TextView
android:id="@+id/textView3"
android:layout_width="0dp"
android:layout_height="50dp"
android:gravity="center"
android:text="分享到"
android:textColor="@color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView3" />
</android.support.constraint.ConstraintLayout>
TDialog 的实现原理主要分为三步
实例化 TDialog.Builer 对象 builder, 然后调用各种 setXXX() 方法设置数据, 设置的数据都保存在 TController.TParams 实例中
create() 方法调用后才会实例化 TDialog 对象, 并将 TController.TParams 中设置的数据传递到 TDialog 的属性 TController 对象中
show() 方法调用显示弹窗
项目 github 地址: https://github.com/Timmy-zzh/TDialog
来源: https://juejin.im/post/5a4f3d266fb9a01c9e45c3ad