在前面的文章中简要地显示了Android的客户端-服务器应用程序的声明式编程的优点与势在必行比较。现在,我们将编写一个小但足够的项目来评估DePro库的有效性。它是图书馆教育实例之一的一部分。下图显示了我们描述的所有屏幕的设计:

DRAWER屏幕CATALOG屏幕PRODUCT_LIST屏幕

CATALOG_ DESCRIPT CHARACTERISTIC

FITNESS FITNESS_
通过这些屏幕,通常可以理解其功能。侧面菜单包含“目录”和“健身”两个项目,当您选择一个项目时,将显示相应的屏幕。 “目录”屏幕包含一个水平的“新闻”列表。当您单击任何“新产品”时,将显示一个包含该产品说明的屏幕。描述由两个选项卡组成:“描述”和“特征”。同样在“目录”屏幕上,还有一个下拉列表“目录”。当您单击向下箭头时,目录将打开(关闭)。单击整行时,将显示PRODUCT_LIST屏幕,其中包含目录中所选项目的产品列表。当您单击“健身”项目时,将显示带有服务列表的健身屏幕。该列表取决于微调器中选择的俱乐部。当您尝试退出该应用程序时,将显示一个警告对话框。服务器将json格式的数据传输到应用程序。每个画面的数据结构如下所述。在通过API用DePro编写的应用程序中,仅使用URL,仅通过数据结构才能正确设置名称(id)视图,因为绑定是通过名称执行的。值得注意的是,数据不是真实数据的完整切片。因此,名称,图像可能没有连接。特别地,产品图像总共20个。因此,许多产品中都可能有相同的图片。屏幕示例的APICATALOG () URL depro/cron/news, GET. . .
{
"product_id":4610,
"catalog_id":15984,
"product_name":" 20 6 ( 1*20) APRO",
"catalog_code":"ZRG-20kit",
"picture":"depro/cronimg/picture_1.jpeg",
"bar_code":"4824041010653",
"oem":"",
"price":175.98,
"brand":"APRO",
"product_code":"032578",
"gift":0,
"bonus":0,
"new_product":1,
"quantity":5
},
. . .
URL depro/cron/catalog.
URL depro/cron/catalog_ex catalog_id id , .
:
[
{
"catalog_id":15510,
"parent_id":0,
"catalog_name":""
},
{
"catalog_id":15584,
"parent_id":0,
"catalog_name":""
},
. . .
]
PRODUCT_LIST URL depro/cron/product_list : expandedLevel catalog_id.
expandedLevel — (0, 1 2), catalog_id — id .
0 1 , , .
GET
()
URL depro/cron/product_barcode
barcode_scanner.
URL depro/cron/product_search product_name. LIKE. product_name .
.
DESCRIPT
: URL depro/cron/product_id product_id. GET.
{
"product_id":2942,
"catalog_id":15594,
"product_name":" 2110, 2111, 2112, Sens '' ",
"catalog_code":"23.3828"
,"picture":"depro/cronimg/picture_16.jpeg",
"bar_code":"2000000148472",
"oem":"2112-3851010",
"price":103.02,
"brand":", . , ",
"product_code":"027729",
"gift":1,
"bonus":0,
"new_product":0,
"quantity":16
}
: URL depro/cron/product_analog product_id. GET.
[
{
"product_id":561,
"catalog_id":15587,
"product_name":" 2110, 2111, 2112 ( 16 . ) AURORA",
"catalog_code":"WP-LA2112",
"picture":"depro/cronimg/picture_12.jpeg",
"bar_code":"2900011680711",
"oem":"2112-1307010",
"price":188.16,
"brand":"AURORA, Poland",
"product_code":"016807",
"gift":0,
"bonus":1,
"new_product":0,
"quantity":15
},
. . .
]
CHARACTERISTIC URL depro/cron/product_charact product_id. GET.
[
{
"prop_id":2764,
"product_id":2942,
"name":"",
"value":", . , "
},
{
"prop_id":2765,
"product_id":2942,
"name":" ",
"value":","
},
. . .
]
现在考虑我们编写应用程序的步骤。1.在工作室中,我们将创建一个新项目。名称可以自行决定。2.资源部署。如第一篇文章中所述,资源文件(XML)通常使用。因此,为了不浪费时间在已知事物上,我们只需下载所有必要的资源。解压缩生成的res_example.zip文件。在工作室中,删除res文件夹的全部内容。将解压缩后的res文件夹的内容传输到项目的res文件夹。然后,考虑到Android Studio的功能,您可能需要清除项目和/或运行“无效的缓存/重新启动”命令。3.库的连接在模块的build.gradle文件的dependencies部分中,您需要指定: implementation 'github.com/deprosystem/depro:compon:3.0.1'
在模块的build.gradle文件的android部分中,您需要指定: packagingOptions {
exclude 'META-INF/DEPENDENCIES'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
选择minSdkVersion属性时,您需要考虑该库支持minSdkVersion =17。更改build.gradle之后,需要同步项目。4.创建必要的类(文件)。使用库时,仅使用4个类:MyDeclareScreens-描述了所有屏幕;MyParams-为应用程序设置必要的参数;MyApp-DePro库已启动;MainActivity-开始活动。您可以使用自己的类名。创建类MyDeclareScreens.java。其内容如下:public class MyDeclareScreens extends DeclareScreens {
public final static String
MAIN = "main", DRAWER = "DRAWER", CATALOG = "CATALOG",
DESCRIPT = "DESCRIPT", CHARACTERISTIC = "CHARACTERISTIC",
PRODUCT_LIST = "PRODUCT_LIST", PRODUCT_DESCRIPT = "PRODUCT_DESCRIPT",
FITNESS = "FITNESS";
@Override
public void declare() {
activity(MAIN, R.layout.activity_main)
.navigator(finishDialog(R.string.attention, R.string.finishOk))
.drawer(R.id.drawer, R.id.content_frame, R.id.left_drawer, null, DRAWER);
fragment(DRAWER, R.layout.fragment_drawer)
.menu(model(menu), view(R.id.recycler));
fragment(CATALOG, R.layout.fragment_catalog)
.navigator(handler(R.id.back, VH.OPEN_DRAWER))
.component(TC.RECYCLER_HORIZONTAL, model(Api.NEWS_PROD).pagination().progress(R.id.progr),
view(R.id.recycler_news, R.layout.item_news_prod),
navigator(start(PRODUCT_DESCRIPT)))
.component(TC.RECYCLER, model(Api.CATALOG),
view(R.id.recycler, "expandedLevel", new int[]{R.layout.item_catalog_type_1,
R.layout.item_catalog_type_2, R.layout.item_catalog_type_3})
.expanded(R.id.expand, R.id.expand, model(Api.CATALOG_EX, "catalog_id")),
navigator(start(PRODUCT_LIST)));
activity(PRODUCT_LIST, R.layout.activity_product_list, "%1$s", "catalog_name").animate(AS.RL)
.navigator(back(R.id.back))
.component(TC.RECYCLER, model(Api.PRODUCT_LIST, "expandedLevel,catalog_id"),
view(R.id.recycler, R.layout.item_product_list)
.visibilityManager(visibility(R.id.bonus_i, "bonus"),
visibility(R.id.gift_i, "gift"),
visibility(R.id.newT, "new_product")),
navigator(start(PRODUCT_DESCRIPT)));
activity(PRODUCT_DESCRIPT, R.layout.activity_product_descript, "%1$s", "catalog_name").animate(AS.RL)
.navigator(back(R.id.back))
.setValue(item(R.id.product_name, TS.PARAM, "product_name"))
.component(TC.PAGER_F, view(R.id.pager, DESCRIPT, CHARACTERISTIC)
.setTab(R.id.tabs, R.array.descript_tab_name));
fragment(DESCRIPT, R.layout.fragment_descript)
.component(TC.PANEL, model(Api.PRODUCT_ID, "product_id"),
view(R.id.panel).visibilityManager(visibility(R.id.bonus, "bonus")))
.component(TC.RECYCLER, model(Api.ANALOG_ID_PRODUCT,"product_id"),
view(R.id.recycler, R.layout.item_product_list).noDataView(R.id.not_analog)
.visibilityManager(visibility(R.id.bonus_i, "bonus"),
visibility(R.id.gift_i, "gift"),
visibility(R.id.newT, "new_product")),
navigator(start(0, PRODUCT_DESCRIPT, PS.RECORD),
handler(0, VH.BACK))) ;
fragment(CHARACTERISTIC, R.layout.fragment_characteristic)
.component(TC.RECYCLER, model(Api.CHARACT_ID_PRODUCT, "product_id"),
view(R.id.recycler, "2", new int[] {R.layout.item_property, R.layout.item_property_1}));
fragment(FITNESS, R.layout.fragment_fitness)
.navigator(handler(R.id.back, VH.OPEN_DRAWER))
.component(TC.SPINNER, model(JSON, getString(R.string.clubs)),
view(R.id.spinner, R.layout.item_spin_drop, R.layout.item_spin_hider))
.component(TC.RECYCLER, model(Api.FITNESS, "clubId"),
view(R.id.recycler, R.layout.item_fitness), null).eventFrom(R.id.spinner);
}
Menu menu = new Menu()
.item(R.drawable.list, R.string.m_catalog, CATALOG, true)
.divider()
.item(R.drawable.ic_aura, R.string.fitness, FITNESS);
}
稍后我们将描述所有使用的DePro构造。要连接导入,请使用alt + Enter键。红色也会突出显示Api类,其中设置了所有请求的地址。其内容将在后面给出。创建类MyParams.java。在大多数情况下,默认设置就足够了。在本例中,我们将仅设置基本URL。public class MyParams extends AppParams {
@Override
public void setParams() {
baseUrl = "https://deprosystem.com/";
}
}
将MainActivity类的工作室创建的内容更改为以下内容:public class MainActivity extends BaseActivity {
@Override
public String getNameScreen() {
return MyDeclareScreens.MAIN;
}
}
在MainActivity的清单中,您可以指定纵向。原则上,该库支持屏幕旋转,但是在此版本中,对于activity()类型的屏幕,除了开始之外,还规定了纵向方向。创建MyApp类:public class MyApp extends Application {
private static MyApp instance;
private Context context;
public static MyApp getInstance() {
if (instance == null) {
instance = new MyApp();
}
return instance;
}
@Override
public void onCreate() {
super.onCreate();
instance = this;
context = getApplicationContext();
DeclareParam.build(context)
.setAppParams(new MyParams())
.setDeclareScreens(new MyDeclareScreens());
}
}
这是设置MyParams和MyDeclareScreens的onCreate方法中的常规单例。请记住在清单中描述MyApp。最后,创建Api类:public class Api {
public static final String CATALOG = "depro/cron/catalog",
NEWS_PROD = "depro/cron/news_prod",
PRODUCT_LIST = "depro/cron/product_list",
PRODUCT_ID = "depro/cron/product_id",
ANALOG_ID_PRODUCT = "depro/cron/product_analog",
CHARACT_ID_PRODUCT = "depro/cron/product_charact",
FITNESS = "depro/cron/fitness",
CATALOG_EX = "depro/cron/catalog_ex";
}
5.现在,您可以运行该应用程序来执行。如果正确输入所有内容,则在应用程序启动时,将显示所有指定的屏幕(并且可以工作)。如我们所见,整个应用程序只有五个简单的类。其中四个是辅助的。它们的内容与屏幕数无关。屏幕的描述(通常不简单)也占用了一些空间(少于80行)。现在我们表明,该描述不仅很大,而且也不复杂。为此,我们描述了所用库组件的操作。如第一篇文章所述,库中的屏幕可以是活动屏幕,也可以是片段屏幕,它们可以设置屏幕名称(行)和布局。到屏幕的链接由它们的名称组成。使用哪种类型的屏幕的选择以通常的方式进行。主屏幕因此,从设计中可以看出,开始屏幕具有一个侧面菜单和一个片段容器。在R.layout.activity_main的标记中,设置了一个标准的DrawerLayout。因此,在MAIN屏幕的描述中,我们指定了抽屉组件,我们将DrawerLayout本身的ID及其侧边栏和片段的容器传递给了该抽屉组件。我们还指出了屏幕名称(DRAWER),该名称将显示在侧栏中。 finishDialog导航器指示退出应用程序之前,您需要发出确认对话框。“ DRAWER”屏幕仅包含一个菜单组件,在模型中为其指定了Menu类型的模型,并且在RecyclerView类型的标记元素的id视图中将显示菜单。菜单本身指示菜单启动时,“ CATALOG”菜单项将显示在片段容器中。“目录”屏幕包含一个水平的“新列表”。从服务器接收数据时,他的模型使用分页,并在R.id.progr中显示进度。如果您的Internet速度很快,那么您可能不会注意到面板的进度。如果要观看,可以切换到较慢的Internet,或更改R.id.progr的背景颜色。在这种情况下,您至少会看到它闪烁。对于分页,默认情况下使用AppParams中指定的参数。该视图具有R.id.recycler_news以及RecyclerView和项目布局。单击列表中的任何项目时,将启动PRODUCT_DESCRIPT屏幕。目录列表是一个下拉列表。公开级别在“ expandedLevel”字段中定义。如果未与源数据一起传输,则库本身将对此进行处理。相同的参数设置了在每个公开级别使用列表中的哪种布局。该列表是下拉列表,具有扩展的功能(...),该模型将在目录中设置为接收来自服务器的下一级数据。该模型指定Api.CATALOG_EX请求的地址和请求参数“ catalog_id”的名称。在扩展中,还指示了R.id.expand -通过单击将在其中扩展列表的项中的标记元素,以及在扩展时将动画地旋转180度的元素的id -关闭列表。最后,导航器指示当您单击列表项时,将调出PRODUCT_LIST屏幕。导航器指向整个屏幕,它指示当您单击R.id.back元素时,将打开一个侧面板。PRODUCT_LIST屏幕指定一个屏幕时,指示在标题中显示catalog_name参数的当前值,并且输出动画从右到左(AS.RL)。列表描述已经为我们所熟悉。呈现的唯一内容是可视性管理器,它根据相应数据的值控制标记元素的可视性。与整个屏幕相关的导航器指示,当您单击R.id.back元素时,将返回上一个屏幕。PRODUCT_DESCRIPT屏幕对我们来说新来的是PAGER_F类型的组件。它与常规ViewPager关联。显示给他的是要显示的屏幕列表(片段),并且已连接TabLayout(setTab)。PRODUCT_DESCRIPT屏幕出现在DESCRIPTION选项卡中。PANEL类型的组件显示由模型获得的数据。 “模拟”列表显示类似产品的列表(如果有),或者使用noDataView()功能显示消息“模拟丢失”。特性屏幕显示产品功能列表。FITNESS(健身)屏幕SPINNER(旋转)类型的组件显示一列球杆,并可以选择一个球杆。俱乐部列表在模型中设置为json字符串。类的类别列表是正常的。eventFrom(...)函数指示在更改与R.id.spinner(在我们的示例中为Spiner)关联的组件时,有必要更新数据。可以在Github上查看本文讨论的应用程序的代码。本文仅供参考。文档中提供了DePro库功能的更多信息。