1 需求场景
android开发过程中,常常想借助前端开发的理念,通过功能菜单+弹窗(iframe)实现功能模块的组织,尤其是横屏模式的APP应用。但android的PopupWindow类并不能满足此需求。因此探索了当前的方式。
2 功能概述
本文章探索了基于android的一种承载模块化功能的容器窗体(后称widget或容器窗体)实现方案。该widget方案,一个widget仅需一个Java类、一个布局资源文件,较为简单。
本widget方案实现后,可以统一管理、调度基于widget的功能模块,摆脱一些复杂的界面布局设计,将更多精力关注到业务实现。
实现过程中需要为各widget指定一个统一的父容器(通常是一个全屏的activity),各widget可以统一外观、统一状态管理、统一事件回调、自定义具体widget的位置、高度宽度、内部UI等,配合菜单的使用可以方便一些场景的功能实现。
3 实现过程
3.1 父容器的一些设置
本widget方案,需要一个父容器来承载各个widget,并基于父容器来设置widget的位置。在实现过程中,我将MainAcitvity(一个全屏的activity)作为widget的父容器,并将所有widget的填充到id为content_main的顶层组件中。以下是MainActivity的布局实现示例:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/content_main"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<!-- 其他UI的实现 正常编写即可 -->
</androidx.constraintlayout.widget.ConstraintLayout>
3.2 widget基类WidgetBase的实现
通过编写抽象基类WidgetBase可以统一管理widget的状态、事件、变量,实现公共功能。各widget需要实现此基类。以下是WidgetBase的实现:
public abstract class WidgetBase {
public Activity context; //widget父容器上下文
public String widgetName; //widget名称
public Boolean openState = false; //widget的打开状态
public ViewGroup widgetView; //widget的布局顶层容器
public ConstraintLayout widgetParent; //widget的父容器,即上面章节中的content_main
public Integer widgetWidth = 200; //widget默认宽度
public Integer widgetHeight = 200; //widget默认高度
public WidgetBase thisWidget = null; //当前widget的一个复本引用
public Drawable iconDrawable = null; //窗体标题栏图标
public WidgetEvents events = null; //事件回调
public Boolean holdUI = true; //是否保持状态(在其他widget打开时,或holdUI=true则本widget隐藏,反之关闭销毁)
public WidgetBase(Activity context, String widgetName, WidgetEvents events) {
this.widgetName = widgetName;
this.context = context;
this.widgetParent = ((MainActivity)context).findViewById(R.id.content_main);
this.events = events;
thisWidget = this;
}
//初始化标题栏相关UI及事件
public void initHeaderUI(){
((ImageView)this.widgetView.findViewById(R.id.widget_header_icon)).setImageDrawable(iconDrawable);
((TextView)this.widgetView.findViewById(R.id.widget_header_title)).setText(this.widgetName);
this.widgetView.findViewById(R.id.widget_header_close).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
</