HarmonyOS實(shí)戰(zhàn)—服務(wù)卡片初體驗(yàn)

最近看到很多博客網(wǎng)站上出現(xiàn)了HarmonyOS的征文活動(dòng),看到那些精美的獎(jiǎng)品讓我也安耐不住開了,當(dāng)然獎(jiǎng)品的誘惑當(dāng)然是抵擋不住我對技術(shù)的狂熱追求,對于開發(fā)者而言技術(shù)沒有頂峰沒有終點(diǎn)。那么今天給大家做一個(gè)卡片服務(wù)開發(fā)的經(jīng)驗(yàn)分享,如果有什么地方說的不對的請各位開發(fā)者進(jìn)行指正,如果有什么問題也可以發(fā)私信或者直接在帖子中留言,我也會(huì)及時(shí)回復(fù)大家。

一、什么是服務(wù)卡片

服務(wù)卡片(以下簡稱“卡片”)是FA的一種界面展示形式,將FA的重要信息或操作直接放置到卡片中,用戶通過直接操作卡片就可以達(dá)到應(yīng)用的使用體驗(yàn),這樣做大大減少了應(yīng)用的使用層級性。
卡片常用于嵌入到其他應(yīng)用中作為其界面的一部分顯示(也可以使用原子化服務(wù)將應(yīng)用保存到服務(wù)中心中,這種方式不需要安裝應(yīng)用),并支持拉起頁面,發(fā)送消息等基礎(chǔ)的交互功能。

原子化服務(wù)在下個(gè)帖子中介紹。

示例如下圖所示。


image

為了開發(fā)者能夠便于理解,官方將服務(wù)卡片分為三方面:

  • 卡片使用方
    顯示卡片內(nèi)容的宿主應(yīng)用,控制卡片在宿主中展示的位置。
  • 卡片管理服務(wù)
    用于管理系統(tǒng)中所添加卡片的常駐代理服務(wù),包括卡片對象的管理與使用,以及卡片周期性刷新等。
  • 卡片提供方
    提供卡片顯示內(nèi)容的HarmonyOS應(yīng)用或原子化服務(wù),控制卡片的顯示內(nèi)容、控件布局以及控件點(diǎn)擊事件

卡片使用方和提供方不要求常駐運(yùn)行,在需要添加/刪除/請求更新卡片時(shí),卡片管理服務(wù)會(huì)拉起卡片提供方獲取卡片信息。

二、服務(wù)卡片的運(yùn)作機(jī)制

文字描述滯后,先上圖。(圖片由官網(wǎng)提供,此處借用一下)


image

從圖中可以清楚的看到卡片服務(wù)的整體通信層都是由"RPC"負(fù)責(zé),通過通信適配層構(gòu)成了數(shù)據(jù)發(fā)送接收的通道。

卡片管理服務(wù)

  • 周期性刷新:在卡片添加后,根據(jù)卡片的刷新策略啟動(dòng)定時(shí)任務(wù)周期性觸發(fā)卡片的刷新。
  • 卡片緩存管理:在卡片添加到卡片管理服務(wù)后,對卡片的視圖信息進(jìn)行緩存,以便下次獲取卡片時(shí)可以直接返回緩存數(shù)據(jù),降低時(shí)延。
  • 卡片生命周期管理:對于卡片切換到后臺(tái)或者被遮擋時(shí),暫停卡片的刷新;以及卡片的升級/卸載場景下對卡片數(shù)據(jù)的更新和清理。
  • 卡片使用方對象管理:對卡片使用方的RPC對象進(jìn)行管理,用于使用方請求進(jìn)行校驗(yàn)以及對卡片更新后的回調(diào)處理。

卡片提供方

  • 創(chuàng)建卡片實(shí)例并實(shí)現(xiàn)onCreateForm、onUpdateForm和onDeleteForm處理創(chuàng)建卡片、更新卡片以及刪除卡片等請求,提供相應(yīng)的卡片服務(wù)。

三 、服務(wù)卡片的實(shí)現(xiàn)(java)

第一步、下載、安裝、配置 DevEco Studio

點(diǎn)擊查看

第二步、運(yùn)行DevEco Studio創(chuàng)建新項(xiàng)目

因?yàn)橐褂胘ava語言編寫,所以此處要選擇支持Java語言的空頁面。


image

選擇成功進(jìn)入新建項(xiàng)目配置界面。


image

點(diǎn)擊Finish進(jìn)入項(xiàng)目。
image

第三步、添加卡片模板

創(chuàng)建模板:右擊“Entry”→“New”→“Server Widget

image

image

設(shè)置模板


image

第四步、查看卡片服務(wù)配置

項(xiàng)目目錄


image

由圖可見:添加卡片模板后在原來項(xiàng)目中增加一個(gè)widget文件夾,并在文件夾中出現(xiàn)了三個(gè)文件FormControllerManager.java、FormController.java、CardWidgetImpl.java。三個(gè)文件對應(yīng)了卡片服務(wù)的運(yùn)行機(jī)制,通過關(guān)系的對應(yīng)可以清楚的了解到代碼運(yùn)行原理

FormControllerManager.java(卡片控制器管理【卡片服務(wù)管理】)

public class FormControllerManager {
    private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG, 0x0, FormControllerManager.class.getName());
    private static final String PACKAGE_PATH = "com.example.carddemo.widget";
    private static final String SHARED_SP_NAME = "form_info_sp.xml";
    private static final String FORM_NAME = "formName";
    private static final String DIMENSION = "dimension";
    private static FormControllerManager managerInstance = null;
    private final HashMap<Long, FormController> controllerHashMap = new HashMap<>();
    private final Context context;
    private final Preferences preferences;

    /**
     * 初始化構(gòu)造器
     *
     * @param context instance of Context.
     */
    private FormControllerManager(Context context) {
        this.context = context;
        DatabaseHelper databaseHelper = new DatabaseHelper(this.context.getApplicationContext());
        preferences = databaseHelper.getPreferences(SHARED_SP_NAME);
    }

    /**
     * FormControllerManager 實(shí)例化
     *
     * @param context instance of Context.
     * @return FormControllerManager instance.
     */
    public static FormControllerManager getInstance(Context context) {
        if (managerInstance == null) {
            synchronized (FormControllerManager.class) {
                if (managerInstance == null) {
                    managerInstance = new FormControllerManager(context);
                }
            }
        }
        return managerInstance;
    }

    /**
     * 通過構(gòu)造器將傳入的卡片信息進(jìn)行封裝,返回卡片服務(wù)
     *
     * @param formId    form id.
     * @param formName  form name.
     * @param dimension form dimension
     * @return FormController form controller
     */
    public FormController createFormController(long formId, String formName, int dimension) {
        synchronized (controllerHashMap) {
            if (formId < 0 || formName.isEmpty()) {
                return null;
            }
            HiLog.info(TAG,
                    "saveFormId() formId: " + formId + ", formName: " + formName + ", preferences: " + preferences);
            if (preferences != null) {
                ZSONObject formObj = new ZSONObject();
                formObj.put(FORM_NAME, formName);
                formObj.put(DIMENSION, dimension);
                preferences.putString(Long.toString(formId), ZSONObject.toZSONString(formObj));
                preferences.flushSync();
            }
            // Create controller instance.
            FormController controller = newInstance(formName, dimension, context);
            // Cache the controller.
            if (controller != null) {
                if (!controllerHashMap.containsKey(formId)) {
                    controllerHashMap.put(formId, controller);
                }
            }
            return controller;
        }
    }

    /**
     * 獲取卡片控制器實(shí)例
     *
     * @param formId form id.
     * @return the instance of form controller.
     */
    public FormController getController(long formId) {
        synchronized (controllerHashMap) {
            if (controllerHashMap.containsKey(formId)) {
                return controllerHashMap.get(formId);
            }
            Map<String, ?> forms = preferences.getAll();
            String formIdString = Long.toString(formId);
            if (forms.containsKey(formIdString)) {
                ZSONObject formObj = ZSONObject.stringToZSON((String) forms.get(formIdString));
                String formName = formObj.getString(FORM_NAME);
                int dimension = formObj.getIntValue(DIMENSION);
                FormController controller = newInstance(formName, dimension, context);
                controllerHashMap.put(formId, controller);
            }
            return controllerHashMap.get(formId);
        }
    }

    private FormController newInstance(String formName, int dimension, Context context) {
        FormController ctrInstance = null;
        if (formName == null || formName.isEmpty()) {
            HiLog.error(TAG, "newInstance() get empty form name");
            return ctrInstance;
        }
        try {
            String className = PACKAGE_PATH + "." + formName.toLowerCase(Locale.ROOT) + "."
                    + getClassNameByFormName(formName);
            Class<?> clazz = Class.forName(className);
            if (clazz != null) {
                Object controllerInstance = clazz.getConstructor(Context.class, String.class, Integer.class)
                        .newInstance(context, formName, dimension);
                if (controllerInstance instanceof FormController) {
                    ctrInstance = (FormController) controllerInstance;
                }
            }
        } catch (NoSuchMethodException | InstantiationException | IllegalArgumentException | InvocationTargetException
                | IllegalAccessException | ClassNotFoundException | SecurityException exception) {
            HiLog.error(TAG, "newInstance() get exception: " + exception.getMessage());
        }
        return ctrInstance;
    }

    /**
     * 從數(shù)組中獲取所有卡片id
     *
     * @return form id list
     */
    public List<Long> getAllFormIdFromSharePreference() {
        List<Long> result = new ArrayList<>();
        Map<String, ?> forms = preferences.getAll();
        for (String formId : forms.keySet()) {
            result.add(Long.parseLong(formId));
        }
        return result;
    }

    /**
     * 刪除卡片服務(wù)
     *
     * @param formId form id
     */
    public void deleteFormController(long formId) {
        synchronized (controllerHashMap) {
            preferences.delete(Long.toString(formId));
            preferences.flushSync();
            controllerHashMap.remove(formId);
        }
    }

    private String getClassNameByFormName(String formName) {
        String[] strings = formName.split("_");
        StringBuilder result = new StringBuilder();
        for (String string : strings) {
            result.append(string);
        }
        char[] charResult = result.toString().toCharArray();
        charResult[0] = (charResult[0] >= 'a' && charResult[0] <= 'z') ? (char) (charResult[0] - 32) : charResult[0];
        return String.copyValueOf(charResult) + "Impl";
    }
}

FormController.java(卡片控制器【卡片提供方】)

public abstract class FormController {
    protected final Context context;
    protected final String formName;
    protected final int dimension;
    public FormController(Context context, String formName, Integer dimension) {
        this.context = context;
        this.formName = formName;
        this.dimension = dimension;
    }
    /**
     * 創(chuàng)建卡片信息提供者
     */
    public abstract ProviderFormInfo bindFormData();
    /**
     * 更新卡片信息
     */
    public abstract void updateFormData(long formId, Object... vars);
    /**
     * 在接收服務(wù)小部件消息事件時(shí)調(diào)用
     */
    public abstract void onTriggerFormEvent(long formId, String message);
    /**
     * 獲取路由的目標(biāo)界面
     */
    public abstract Class<? extends AbilitySlice> getRoutePageSlice(Intent intent);
}

CardWidgetImpl.java(卡片應(yīng)用【卡片使用方】)

public class CardWidgetImpl extends FormController {
    public static final int DIMENSION_1X2 = 1;
    public static final int DIMENSION_2X4 = 3;
    private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG, 0x0, CardWidgetImpl.class.getName());
    private static final int DEFAULT_DIMENSION_2X2 = 2;
    private static final Map<Integer, Integer> RESOURCE_ID_MAP = new HashMap<>();
    static {
        RESOURCE_ID_MAP.put(DIMENSION_1X2, ResourceTable.Layout_form_grid_pattern_cardwidget_1_2);
        RESOURCE_ID_MAP.put(DEFAULT_DIMENSION_2X2, ResourceTable.Layout_form_grid_pattern_cardwidget_2_2);
        RESOURCE_ID_MAP.put(DIMENSION_2X4, ResourceTable.Layout_form_grid_pattern_cardwidget_2_4);
    }

    public CardWidgetImpl(Context context, String formName, Integer dimension) {
        super(context, formName, dimension);
    }

    //創(chuàng)建好卡片服務(wù)后,在界面展示卡片
    @Override
    public ProviderFormInfo bindFormData() {
        HiLog.info(TAG, "bind form data when create form");
        return new ProviderFormInfo(RESOURCE_ID_MAP.get(dimension), context);
    }

    //更新卡片信息
    @Override
    public void updateFormData(long formId, Object... vars) {
        HiLog.info(TAG, "update form data timing, default 30 minutes");
    }

    //卡片中內(nèi)容手勢觸發(fā)方法
    @Override
    public void onTriggerFormEvent(long formId, String message) {
        HiLog.info(TAG, "handle card click event.");
    }

    @Override
    public Class<? extends AbilitySlice> getRoutePageSlice(Intent intent) {
        HiLog.info(TAG, "get the default page to route when you click card.");
        return null;
    }
}

MainAbility的變化,新增了一些方法。

public class MainAbility extends Ability {
    public static final int DEFAULT_DIMENSION_2X2 = 2;
    public static final int DIMENSION_1X2 = 1;
    public static final int DIMENSION_2X4 = 3;
    public static final int DIMENSION_4X4 = 4;
    private static final int INVALID_FORM_ID = -1;
    private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG, 0x0, MainAbility.class.getName());
    private String topWidgetSlice;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setMainRoute(MainAbilitySlice.class.getName());
        if (intentFromWidget(intent)) {
            topWidgetSlice = getRoutePageSlice(intent);
            if (topWidgetSlice != null) {
                setMainRoute(topWidgetSlice);
            }
        }
        stopAbility(intent);
    }

    //創(chuàng)建卡片信息并返回卡片內(nèi)容
    @Override
    protected ProviderFormInfo onCreateForm(Intent intent) {
        HiLog.info(TAG, "onCreateForm");
        long formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, INVALID_FORM_ID);
        String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);
        int dimension = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, DEFAULT_DIMENSION_2X2);
        HiLog.info(TAG, "onCreateForm: formId=" + formId + ",formName=" + formName);
        FormControllerManager formControllerManager = FormControllerManager.getInstance(this);
        FormController formController = formControllerManager.getController(formId);
        //通過調(diào)用布局Id將卡片布局與卡片信息進(jìn)行綁定
        formController = (formController == null) ? formControllerManager.createFormController(formId,
                formName, dimension) : formController;
        if (formController == null) {
            HiLog.error(TAG, "Get null controller. formId: " + formId + ", formName: " + formName);
            return null;
        }
        return formController.bindFormData();
    }

    //更新卡片信息
    @Override
    protected void onUpdateForm(long formId) {
        HiLog.info(TAG, "onUpdateForm");
        super.onUpdateForm(formId);
        FormControllerManager formControllerManager = FormControllerManager.getInstance(this);
        FormController formController = formControllerManager.getController(formId);
        formController.updateFormData(formId);
    }

    //刪除卡片
    @Override
    protected void onDeleteForm(long formId) {
        HiLog.info(TAG, "onDeleteForm: formId=" + formId);
        super.onDeleteForm(formId);
        FormControllerManager formControllerManager = FormControllerManager.getInstance(this);
        formControllerManager.deleteFormController(formId);
    }

    //卡片手勢觸發(fā)
    @Override
    protected void onTriggerFormEvent(long formId, String message) {
        HiLog.info(TAG, "onTriggerFormEvent: " + message);
        super.onTriggerFormEvent(formId, message);
        FormControllerManager formControllerManager = FormControllerManager.getInstance(this);
        FormController formController = formControllerManager.getController(formId);
        formController.onTriggerFormEvent(formId, message);
    }

    @Override
    public void onNewIntent(Intent intent) {
        if (intentFromWidget(intent)) { // Only response to it when starting from a service widget.
            String newWidgetSlice = getRoutePageSlice(intent);
            if (topWidgetSlice == null || !topWidgetSlice.equals(newWidgetSlice)) {
                topWidgetSlice = newWidgetSlice;
                restart();
            }
        }
    }

    private boolean intentFromWidget(Intent intent) {
        long formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, INVALID_FORM_ID);
        return formId != INVALID_FORM_ID;
    }

    //跳轉(zhuǎn)至對應(yīng)的卡片界面
    private String getRoutePageSlice(Intent intent) {
        long formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, INVALID_FORM_ID);
        if (formId == INVALID_FORM_ID) {
            return null;
        }
        FormControllerManager formControllerManager = FormControllerManager.getInstance(this);
        FormController formController = formControllerManager.getController(formId);
        if (formController == null) {
            return null;
        }
        Class<? extends AbilitySlice> clazz = formController.getRoutePageSlice(intent);
        if (clazz == null) {
            return null;
        }
        return clazz.getName();
    }
}

配置文件


image

在這里插入圖片描述

第五步、編寫卡片界面

image

分別修改form_grid_pattern_cardwidget_1_2、form_grid_pattern_cardwidget_2_2.xml和form_grid_pattern_cardwidget_2_4.xml文件。

form_grid_pattern_cardwidget_1_2.xml

<?xml version="1.0" encoding="utf-8"?><DependentLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:remote="true">
    <Image
        ohos:height="match_parent"
        ohos:width="match_parent"
        ohos:background_element="#1281f0"
        ohos:scale\_mode="clip\_center"/>
    <DirectionalLayout
        ohos:height="match_parent"
        ohos:width="match_parent"
        ohos:alignment="center"
        ohos:orientation="horizontal">
        <Text
            ohos:id="$+id:tv_name"
            ohos:height="match_content"
            ohos:width="match_content"
            ohos:text="$string:cardwidget_title"
            ohos:text_color="#E5FFFFFF"
            ohos:text_size="16fp"
            ohos:text_weight="1200"/>
        <Text
            ohos:height="match_content"
            ohos:width="match_content"
            ohos:text="3350"
            ohos:text_color="#E5FFFFFF"
            ohos:text_size="16fp"
            ohos:text_weight="1200"/>
    </DirectionalLayout></DependentLayout>

form_grid_pattern_cardwidget_2_2.xml

<?xml version="1.0" encoding="utf-8"?><DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:background_element="#FFFFFFFF"
    ohos:remote="true"
    ohos:orientation="vertical">
    <DirectionalLayout
        ohos:id="$+id:tv_top"
        ohos:height="match_parent"
        ohos:width="match_parent"
        ohos:weight="1"
        ohos:orientation="horizontal">
        <DirectionalLayout
            ohos:height="match_parent"
            ohos:width="match_parent"
            ohos:weight="1"
            ohos:orientation="vertical"
            ohos:alignment="center">
            <Text
                ohos:height="match_content"
                ohos:width="match_content"
                ohos:text="消耗熱量"
                ohos:top_margin="5vp"
                ohos:text_size="10fp"
                ohos:text_weight="500">
            </Text>
            <Text
                ohos:height="60vp"
                ohos:width="60vp"
                ohos:text="2.1 Kcal"
                ohos:text_alignment="center"
                ohos:text_color="#1281f0"
                ohos:background\_element="$media:aa\_1"
                ohos:text_size="10fp"
                ohos:text_weight="500"/>
        </DirectionalLayout>
        <DirectionalLayout
            ohos:height="match_parent"
            ohos:width="match_parent"
            ohos:weight="1"
            ohos:orientation="vertical"
            ohos:alignment="center">
            <Text
                ohos:height="match_content"
                ohos:width="match_content"
                ohos:text="實(shí)時(shí)心率"
                ohos:top_margin="5vp"
                ohos:text_size="10fp"
                ohos:text_weight="500">
            </Text>
            <Text
                ohos:height="60vp"
                ohos:width="60vp"
                ohos:text="99/min"
                ohos:text_alignment="center"
                ohos:text_color="#1281f0"
                ohos:background\_element="$media:aa\_1"
                ohos:text_size="10fp"
                ohos:text_weight="500"/>
        </DirectionalLayout>
    </DirectionalLayout>
    <DirectionalLayout
        ohos:id="$+id:tv_blow"
        ohos:height="60vp"
        ohos:width="match_parent"
        ohos:align\_parent\_bottom="true"
        ohos:background_element="#1281f0"
        ohos:alignment="center"
        ohos:orientation="vertical">
        <DirectionalLayout
            ohos:height="match_content"
            ohos:width="match_parent"
            ohos:alignment="center"
            ohos:orientation="horizontal">
            <Text
                ohos:id="$+id:tv_name"
                ohos:height="match_content"
                ohos:width="match_content"
                ohos:text="$string:cardwidget_title"
                ohos:text_color="#E5FFFFFF"
                ohos:text_size="16fp"
                ohos:text_weight="600"/>
            <Text
                ohos:height="match_content"
                ohos:width="match_content"
                ohos:text="3350"
                ohos:text_color="#E5FFFFFF"
                ohos:text_size="16fp"
                ohos:text_weight="1000"/>
        </DirectionalLayout>
        <Text
            ohos:height="match_content"
            ohos:width="match_content"
            ohos:text="$string:cardwidget_introduction"
            ohos:text_color="#ffffff"
            ohos:text_size="14fp"
            ohos:text_weight="800"
            ohos:top_margin="7vp"/>
    </DirectionalLayout></DirectionalLayout>

form_grid_pattern_cardwidget_2_4.xml

<?xml version="1.0" encoding="utf-8"?><DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:background_element="#FFFFFFFF"
    ohos:orientation="horizontal"
    ohos:remote="true">
    <DirectionalLayout
        ohos:height="match_parent"
        ohos:width="match_parent"
        ohos:weight="1"
        ohos:orientation="vertical">
        <DirectionalLayout
            ohos:height="match_parent"
            ohos:width="match_parent"
            ohos:weight="1"
            ohos:orientation="horizontal">
            <DirectionalLayout
                ohos:height="match_parent"
                ohos:width="match_parent"
                ohos:weight="1"
                ohos:orientation="vertical"
                ohos:alignment="center">
                <Text
                    ohos:height="match_content"
                    ohos:width="match_content"
                    ohos:text="消耗熱量"
                    ohos:top_margin="5vp"
                    ohos:text_size="10fp"
                    ohos:text_weight="500">
                </Text>
                <Text
                    ohos:height="60vp"
                    ohos:width="60vp"
                    ohos:text="2.1 Kcal"
                    ohos:text_alignment="center"
                    ohos:text_color="#1281f0"
                    ohos:background\_element="$media:aa\_1"
                    ohos:text_size="10fp"
                    ohos:text_weight="500"/>
            </DirectionalLayout>
            <DirectionalLayout
                ohos:height="match_parent"
                ohos:width="match_parent"
                ohos:weight="1"
                ohos:orientation="vertical"
                ohos:alignment="center">
                <Text
                    ohos:height="match_content"
                    ohos:width="match_content"
                    ohos:text="實(shí)時(shí)心率"
                    ohos:top_margin="5vp"
                    ohos:text_size="10fp"
                    ohos:text_weight="500">
                </Text>
                <Text
                    ohos:height="60vp"
                    ohos:width="60vp"
                    ohos:text="99/min"
                    ohos:text_alignment="center"
                    ohos:text_color="#1281f0"
                    ohos:background\_element="$media:aa\_1"
                    ohos:text_size="10fp"
                    ohos:text_weight="500"/>
            </DirectionalLayout>
        </DirectionalLayout>
        <DirectionalLayout
            ohos:height="60vp"
            ohos:width="match_parent"
            ohos:align\_parent\_bottom="true"
            ohos:background_element="#1281f0"
            ohos:alignment="center"
            ohos:orientation="vertical">
            <DirectionalLayout
                ohos:height="match_content"
                ohos:width="match_parent"
                ohos:alignment="center"
                ohos:orientation="horizontal">
                <Text
                    ohos:height="match_content"
                    ohos:width="match_content"
                    ohos:text="$string:cardwidget_title"
                    ohos:text_color="#E5FFFFFF"
                    ohos:text_size="16fp"
                    ohos:text_weight="600"/>
                <Text
                    ohos:height="match_content"
                    ohos:width="match_content"
                    ohos:text="3350"
                    ohos:text_color="#E5FFFFFF"
                    ohos:text_size="16fp"
                    ohos:text_weight="1000"/>
            </DirectionalLayout>
            <Text
                ohos:height="match_content"
                ohos:width="match_content"
                ohos:text="$string:cardwidget_introduction"
                ohos:text_color="#ffffff"
                ohos:text_size="14fp"
                ohos:text_weight="800"
                ohos:top_margin="7vp"/>
        </DirectionalLayout>
    </DirectionalLayout>
    <DirectionalLayout
        ohos:height="match_parent"
        ohos:width="match_parent"
        ohos:weight="1">
    </DirectionalLayout></DirectionalLayout>

第六步、運(yùn)行程序、查看效果

點(diǎn)擊圖標(biāo)上劃,出現(xiàn)卡片。


image

點(diǎn)擊右上角圖釘按鈕,將卡片放在屏幕中。


image

長按應(yīng)用,出現(xiàn)服務(wù)卡片。
image

點(diǎn)擊服務(wù)卡片選擇界面,上下滑動(dòng)可選擇卡片內(nèi)容。


image

點(diǎn)擊添加到桌面,則將卡片添加到桌面中。
image

至此,卡片服務(wù)應(yīng)用就以全部開發(fā)完成,后續(xù)會(huì)對卡片內(nèi)部進(jìn)行相關(guān)編寫。使其進(jìn)行動(dòng)態(tài)的刷新及動(dòng)態(tài)的獲取數(shù)據(jù)。

跳轉(zhuǎn)項(xiàng)目地址

喜歡的請為作者點(diǎn)個(gè)喜歡或者贊賞~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容