在Android上对客户端-服务器应用程序进行声明式编程。第2部分

前面的文章中简要地显示了Android的客户端-服务器应用程序的声明式编程的优点与势在必行比较。

现在,我们将编写一个小但足够的项目来评估DePro库的有效性。它是图书馆教育实例之一的一部分下图显示了我们描述的所有屏幕的设计:

图片 图片 图片
DRAWER屏幕CATALOG屏幕PRODUCT_LIST屏幕

图片 图片 图片
 CATALOG_           DESCRIPT          CHARACTERISTIC

图片 图片 图片
 FITNESS           FITNESS_          


通过这些屏幕,通常可以理解其功能。侧面菜单包含“目录”和“健身”两个项目,当您选择一个项目时,将显示相应的屏幕。 “目录”屏幕包含一个水平的“新闻”列表。当您单击任何“新产品”时,将显示一个包含该产品说明的屏幕。描述由两个选项卡组成:“描述”和“特征”。同样在“目录”屏幕上,还有一个下拉列表“目录”。当您单击向下箭头时,目录将打开(关闭)。单击整行时,将显示PRODUCT_LIST屏幕,其中包含目录中所选项目的产品列表。

当您单击“健身”项目时,将显示带有服务列表的健身屏幕。该列表取决于微调器中选择的俱乐部。当您尝试退出该应用程序时,将显示一个警告对话框。

服务器将json格式的数据传输到应用程序。每个画面的数据结构如下所述。在通过API用DePro编写的应用程序中,仅使用URL,仅通过数据结构才能正确设置名称(id)视图,因为绑定是通过名称执行的。值得注意的是,数据不是真实数据的完整切片。因此,名称,图像可能没有连接。特别地,产品图像总共20个。因此,许多产品中都可能有相同的图片。

屏幕示例的API
CATALOG () 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库功能的更多信息

All Articles