1. 前言

相信大家都使用过支付宝或者微信等app的半浮层支付功能,本文就针对半浮层的实现进行了探讨,并尽量抽象出具有共用性的一个半浮层框架。如果你来不及看文章,就想直接撸代码,请移步https://github.com/hanxiao2018/half-float

2. 目标

2.1 效果

这里先直接了当的来描述我们要实现的效果:

(1)首先,必须是个半浮层,可以填充各种自定义视图

(2)半浮层每个视图之间可以相互切换,跳转

(3)具备一定的解耦性,每个视图只负责处理自身事件,不care其他视图事件。

3. 实现

目标确认,ok,那就来一步一步朝目标迈进即可。本着能动手就不要yy的精神,现在就必须要开始coding...

3.1 方案

半浮层故名思议可以理解为是一个占据手机非整个屏幕的视图,可以有很多实现方案,比如自定义特定高度的view、采用fragment、dialog等。从直观感受来看,dialog显然是具有天然实现半浮层的基因,但使用过dialog的人一定知道,dialog带来的问题还是很让人不舒服的,况且google已经推荐用另一个控件了,故本方案将不再采用dialog。至于自定义view可能会严重依赖宿主,不具备独立性,因此也放弃使用。那么还有什么可以使用呢,答案就是DialogFragment,没用过的可以去google一下,本半浮层框架就用DialogFragment作为半浮层容器。容器确定了,接下来就要确定半浮层的切换问题了。

显然android中的viewpager可以用不同视图来进行切换,但是这个使用过程中相对繁琐,还是放弃好了,这里将采用android提供的一个自带控件 ViewFlipper,ViewFlipper底层实际上是基于FrameLayout实现的,非常方便用于不同视图的切换。没有用过的也可自行google。

最后一个问题,不同视图之间的事件可以有自身进行处理,很好办。但是对于视图之间的交互该如何处理?举个例子,假如现在有两个浮层页面,一个是支付主页,显示了当前支付方式;另一个是支付方式页,用于选择不同的支付方式。现在我点击支付主页要选择支付方式,这个时候就要跳大支付方式页,选择之后再回到支付主页,那么支付主页的支付方式显然是需要改变的,这就涉及到了不同页面之间的事件交互。因此需要考虑到这个情况。这个实现方式有很多,比如注册回调方法,注册监听等等,然后这有不可避免的产生一定耦合,因此为避免这种情况可以采用基于事件驱动的框架,本文采用otto,没用过的可自行google。

故,本悬浮曾框架将会基于DialogFragment + ViewFlipper + otto 进行实现。

3.2 交互

实现的基础设施都已备好,那么剩下的就该谈下交互了。

3.2.1 事件类型

本框架既然采用了事件机制,那么首先区分有哪几种事件

(1) 视图切换事件

这个事件旨在触发视图切换,因为浮层容器中的不同页面会根据不同情况发生切换,比如从A页面切换到B页面,这个时间显然是业务无关的,故可以抽象出来,作为一个单独的事件。本文定义为:FlipperRequestEvent事件。

(2)具体业务事件

这类事件就和具体的页面有关了,用于携带页面交互数据、页面视图更新等。本文定义为UpdateViewEvent。前文中我们提到采用otto来处理不同页面之间的数据交互,但是想一想,一个半浮层有一两个页面还好,如果有5个以上两两都需要交互的场景,即便是用这个框架,那页面交互来回注册接收事件也是相当的繁琐,因此必须对这一块的交互进行更为合理的优化。

3.2.2 视图控制中心

如上面所述,为了避免页面之间来回注册接收事件的杂乱局面,本文抽象出一个消息控制中心,该中心的作用是用于事件的派发,包括视图切换事件以及具体业务事件,本位定义为:IViewController。这控制中心用于事件的派发,进而避免交互消息的杂乱无章;

3.2.3 事件的数据结构

前文提到了两种事件:FlipperRequestEvent和UpdateViewEvent,那么其数据结构该怎么设计呢?

(1)FlipperRequestEvent

这个事件数据结构相对简单,因为要跳转,就必须要告知控制中心跳转到那一页,因此其数据结构如下:

public class FlipperRequestEvent {

public final boolean showNext;//方向标识,true则展示下一页,否则展示前一页

public final int whichChild;//子view在半浮层中的位置,从0开始,这个下文会介绍

public FlipperRequestEvent(int whichChild,boolean showNext) {

this.whichChild = whichChild;

this.showNext = showNext;

}    }

(2)UpdateViewEvent

UpdateViewEvent可要比FlipperRequestEvent复杂多了,因为浮层中的页面五花八门,各种数据完全不一样,怎么设计能达到一种通用性呢?这里就必须用到泛型了,因此其数据结构设计如下:

public class UpdateViewEvent {

public int whichChild;//更新那个子页面

public String arg1;//提供预置的简单参数便于视图更新

public String arg2;

public boolean flag1;//提供预置的boolean标识判断

public boolean flag2;

public T obj;//真正的页面数据

public UpdateViewEvent(int whichChild) {

this.whichChild = whichChild;

}}

事实上,这里认为交互双方“知此知彼”的默契才这样设计的,也就是说实际上发送方永远会知道接收方需要什么数据。

3.2.4 注册中心

ok,前面事件封装中都有whichChild这个属性,那么这个是干什么的?其实这个就是用于描述页面在半浮层中位置的。本文设计了一个注册中心,定义为ChildIndex,所有的半浮层页面都必须在此进行注册,以便容器能感知其位置,进而根据事件(数据结构中的whichChild)来定位目标页面。那这么说,发送方必须要知道接收方的index索引了?当然不需要,如果真是靠0、1、2这种索引来进行页面的定位,那真是太不人性化了,事实上注册中心保留的是有意义的符号,比如我当前半浮层有三个页面,那么在注册中心注册如下:

public final class ChildIndex {

public static final int DEMO_FLIPPER_PAGE0 =0;

public static final int DEMO_FLIPPER_PAGE1 =1;

public static final int DEMO_FLIPPER_PAGE2 =2;

}

是的,就这么简单。

3.3 实现

3.3.1 抽象半浮层容器

首先我们抽象出一个容器基类,用于提供容器的窗口这是以及切换配置。

//BaseDialogFragment 是个抽象容器,基于此可以实现不同的浮层容器

public abstract class BaseDialogFragment extends DialogFragment {

private FrameLayout mView;//半浮层视图

protected ViewFlipper mViewFlipper;//视图载体

@Nullable

@Override

public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle                 savedInstanceState) {

//这个view_flipper_base_layout其实就是个ViewFilpper

mView = (FrameLayout) inflater.inflate(R.layout.view_flipper_base_layout,null);

mViewFlipper = (ViewFlipper)mView.findViewById(R.id.view_flipper);

//getContentLayout是由具体容器来进行布局的,所以是个抽象方法。这个容器最重要的实现也就是这句了~

inflater.inflate(getContentLayout(),mViewFlipper,true);

return mView;

}

@Override

public void onActivityCreated(Bundle savedInstanceState) {

super.onActivityCreated(savedInstanceState);

initDialog(getDialog());

}

private void initDialog(Dialog dialog) {

dialog.setCanceledOnTouchOutside(false);

//对半浮层窗口属性进行设置,这里其实限定了半浮层必须位于底部,这也是常见的场景,事实上可以对这个位置进行抽象

Window window = dialog.getWindow();

window.setGravity(Gravity.BOTTOM);

window.setWindowAnimations(getStyle());

window.setLayout(WindowManager.LayoutParams.MATCH_PARENT,WindowManager.LayoutParams.MATCH_PARENT);

window.setBackgroundDrawable(new ColorDrawable());

}

这样就初始化了半浮层基类容器。这个容器设置了半浮层窗口的位置、style等。然后,最重要的是抽象了一个布局接口getContentLayout,用于自定义半浮层视图布局。

3.3.2 具体半浮层容器

//实现抽象容器

public class ConcreteDialogFragment extends BaseDialogFragment {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

BusHolder.getBus().register(this);//解耦的bus事件来了,下面会介绍

}

//@Subscribe是otto的语法,不了解的自行google。这里receiveFlipperRequestEvent

@Subscribe

public void receiveFlipperRequestEvent(FlipperRequestEvent event) {

if (event.showNext) {//是否发生切换

//根据索引定位目标页面

setNextDisplayedChild(event.whichChild);

return;

}

if (mViewFlipper.getChildAt(0) ==mViewFlipper.getCurrentView()) {

dismiss();

return;

}

setPreviousDisplayedChild(event.whichChild);

}

@Subscribe

public void updateView(UpdateViewEvent event) {

//视图控制中心进行不同页面之间的事件派发,目标页面接收事件后根据需要进行视图更新或其他处理。

IViewController viewController = (IViewController) getChildViewInViewFlipper(event.whichChild);

viewController.updateView(event);

}

@Override

public void onPause() {

super.onPause();

//此处用来清除每次pause后的弹出动画,如果需要每次都展示进入动画,则可以屏蔽该代码

if (getDialog() !=null) {

getDialog().getWindow().setWindowAnimations(R.style.window_exit_style);

}

}

@Override

protected int getContentLayout() {//这就是具体的布局实现

return R.layout.demo_base_container;//可参见文章给出的git源码

}

@Override

public void onDestroy() {

super.onDestroy();

BusHolder.getBus().unregister(this);

}}

3.3.3 bus 封装

这里对otto的Bus实例进行了一层包裹,确保其为单例即可,因为只需要一个实例就可完成不同业务方的注册、注销。单例相比大家都会写,但是一个相对规范、线程安全的单例却不太容易,本文实现如下(可供参考):

public class BusHolder {

private BusHolder() {

}

public static final Bus getBus() {

return BusInstance.sBus;

}

private static class BusInstance {

private static Bus sBus =new Bus();

}}

ok,到这里整个框架就实现完了,接下来的测试我就不在这里贴出来了,参见git地址即可:https://github.com/hanxiao2018/half-float

简书地址:https://www.jianshu.com/p/293b051985e3

android 半浮层框架相关推荐

  1. android 半浮层框架,GitHub - Jodragon/AnyLayer: Android稳定高效的浮层创建管理框架

    AnyLayer Android稳定高效的浮层创建管理框架. 可取代系统自带Dialog/Popup/BottomSheet等弹窗,可实现单Activity架构的Toast提示,可定制任意样式的Gui ...

  2. Android O Treble框架笔记(基于高通845平台)

    Android O Treble框架笔记(基于高通845平台) tags: android 文章目录 Android O Treble框架笔记(基于高通845平台) @[toc] **0 前言** * ...

  3. 关于Android adb实现框架和应用

    关于Android adb实现框架和应用 链接: https://pan.baidu.com/s/1tMSw6OnbgQz5GH2E8i6JKw 提取码: 7a5u 另外我的相关培训视频请看: 欢迎观 ...

  4. Android图片缓存框架Glide

    Android图片缓存框架Glide Glide是Google提供的一个组件.它具有获取.解码和展示视频剧照.图片.动画等功能.它提供了灵活的API,帮助开发者将Glide应用在几乎任何网络协议栈中. ...

  5. 15类Android通用流行框架

    15类Android通用流行框架 Android流行框架 缓存 DiskLruCache Java实现基于LRU的磁盘缓存 图片加载 Android Universal Image Loader 一个 ...

  6. Android接口和框架学习

    Android接口和框架学习 缩写: HAL:HardwareAbstraction Layer,硬件抽象层 CTS:CompatibilityTest Suite,兼容性测试套件 Android让你 ...

  7. [Android]Android端ORM框架——RapidORM(v2.1)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/6020412.html [Android]Android端ORM ...

  8. android原生组件,XUI: 一个简洁而优雅的Android原生UI框架,解放你的双手!

    XUI 一个简洁而又优雅的Android原生UI框架,解放你的双手!还不赶紧点击使用说明文档,体验一下吧! 涵盖绝大部分的UI组件:TextView.Button.EditText.ImageView ...

  9. 淘宝开源Android容器化框架Atlas开发者指南

    Atlas 由阿里巴巴移动团队自研,以容器化思路解决大规模团队协作问题,实现并行开发.快速迭代和动态部署,适用于 Android 4.x 以上系统版本的大小型 App 开发. 该框架于2017年3月1 ...

  10. Android常用ui,XUI: 一个简洁而优雅的Android原生UI框架,解放你的双手!

    XUI 一个简洁而又优雅的Android原生UI框架,解放你的双手!还不赶紧点击使用说明文档,体验一下吧! 涵盖绝大部分的UI组件:TextView.Button.EditText.ImageView ...

最新文章

  1. 档案盒正面标签制作_包材工艺丨浅述模内标签印刷及材料的选择
  2. python网上编程课程-程序设计入门—Python
  3. Tornado使用mako 模板总结
  4. 【数据结构与算法】之深入解析“Excel表列序号和表列名称”的求解思路与算法示例
  5. 使用Nito.AsyncEx实现异步锁
  6. Linux内核:内存从BIOS->e820->memblock->node/zone基本流程
  7. int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
  8. 在DBGrid中实现Copy、Paste功能 - DELPHI
  9. nginx ---- nginx.conf核心配置文件
  10. asp跟JAVA语言有关系吗_asp程序员如何转行为J2EE之java基础上(11)
  11. 计算机硬盘使用寿命,固态硬盘怎么测剩余寿命?SSD固态硬盘使用寿命检测方法...
  12. 【visio】visio软件安装
  13. 自定义validator
  14. excel汇总报表如何做?
  15. 听说你还不了解微前端?[收藏=学会]
  16. 测坐标天幕靶软件设计_测速天幕靶检定装置与检定方法
  17. Chrome google flash过期
  18. SpringBoot 封装返回类报错:No converter found for return value of type
  19. 【ECCV 2020】UDA with Noise Resistible Mutual-Training for Person Re-identification (NRMT)
  20. ISP Tuning—高通Chromatix6

热门文章

  1. addr2line 回复“问号”问题的解决和一些发现
  2. python ppt_从 Python 中学习 PPT 制作技巧
  3. Angular动态加载组件报错:No component factory found for XXXXComponent. Did you add it to
  4. 操作系统死锁 四个必要条件
  5. Installation for COMSOl(安装COMSOL)
  6. csv excel 对比
  7. 智能随访系统:提升患者综合服务能力和就医体验,提高医院品牌价值与服务质量
  8. 毫米和像素怎么换算_像素和厘米怎么换算?
  9. 图像算法工程师面试题
  10. ERP原理与应用名词解释