主列表页面
鸿蒙应用:多设备闹钟帮你把多个房间的娃同时叫醒!
项目地址:https://github.com/Madixin/CrazyClock
文章介绍及教程:https://blog.csdn.net/sd2131512/article/details/117571607
本节我们将开发闹钟的主列表页面。这个页面包含底部TabList的里闹钟和设置两个tab,点击闹钟时显示闹钟列表,点击设置时显示关于,设备等信息。
1.开发主页面的xml资源文件
1.1 在string.json中添加资源文件
{
"string": [
{
"name": "app_name",
"value": "多设备闹钟"
},
{
"name": "clock",
"value": "闹钟"
},
{
"name": "device",
"value": "设备"
},
{
"name": "about",
"value": "关于"
},
{
"name": "setting",
"value": "设置"
}
]
}
2.在resources/base/graphic下添加图片资源文件闹钟,设置,展开的图标。这3个图标可从iconfont中下载png格式,然后使用IDE的Svg to xml功能引入。
3.在resources/base/graphic下添加layout资源文件list_divider.xml,用于列表的分割线
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle">
<solid ohos:color="#ffeeee"/>
</shape>
4.在resources/base/layoutx下添加layout资源文件item_clock.xml,用于自定义那种列表,里面包含了两个Text,一个是Switch控件和一个分隔线Component。
item_clock.xml
<?xml version="1.0" encoding="utf-8"?>
<DependentLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_content"
ohos:width="match_parent"
ohos:orientation="vertical">
<Text
ohos:id="$+id:item_text_time"
ohos:height="match_content"
ohos:width="match_content"
ohos:layout_alignment="center"
ohos:left_margin="20vp"
ohos:padding="4vp"
ohos:text="Time"
ohos:text_size="20fp"/>
<Text
ohos:id="$+id:item_text_name"
ohos:height="match_content"
ohos:width="match_content"
ohos:below="$id:item_text_time"
ohos:layout_alignment="center"
ohos:left_margin="20vp"
ohos:padding="4vp"
ohos:text="Name"
ohos:text_size="20fp"/>
<Switch
ohos:id="$+id:item_switch_state"
ohos:height="30vp"
ohos:width="60vp"
ohos:align_parent_right="true"
ohos:right_margin="20vp"
ohos:top_margin="20vp"
ohos:text_state_off="OFF"
ohos:text_state_on="ON"/>
<Component
ohos:height="1vp"
ohos:width="match_parent"
ohos:background_element="$graphic:list_divider"
ohos:below="$id:item_text_name"/>
</DependentLayout>
5.在entry/src/main/config.json中修改界面主题风格,去掉窗口Title显示。
"metaData": {
"customizeData": [
{
"name": "hwc-theme",
"value": "androidhwext:style/Theme.Emui.NoTitleBar"
}
]
}
6.最后完成主列表界面的布局。其中通过控制两个DependentLayout的显示和隐藏,分别实现闹钟和设置两个tab页的切换。
<?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:orientation="vertical">
<DirectionalLayout
ohos:id="$+id:layout_clock"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical"
ohos:above="$id:tab_bottom_list">
<DependentLayout
ohos:height="56vp"
ohos:width="match_parent"
ohos:padding="10vp"
>
<Text
ohos:id="$+id:text_clockname"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="$string:app_name"
ohos:text_size="24fp"
ohos:align_parent_left="true"
ohos:vertical_center="true"
ohos:left_margin="20vp"
/>
<Image
ohos:id="$+id:image_add_clock"
ohos:height="24vp"
ohos:width="24vp"
ohos:right_margin="20vp"
ohos:foreground_element="$media:add"
ohos:align_parent_right="true"
ohos:vertical_center="true"/>
</DependentLayout>
<ListContainer
ohos:id="$+id:list_clock_view"
ohos:height="match_parent"
ohos:width="match_parent"/>
</DirectionalLayout>
<DirectionalLayout
ohos:id="$+id:layout_setting"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical"
ohos:above="$id:tab_bottom_list"
ohos:visibility="hide">
<DependentLayout
ohos:height="56vp"
ohos:width="match_parent"
ohos:padding="10vp"
>
<Text
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="$string:app_name"
ohos:text_size="24fp"
ohos:align_parent_left="true"
ohos:vertical_center="true"
ohos:left_margin="20vp"
/>
</DependentLayout>
<DependentLayout
ohos:height="match_content"
ohos:width="match_parent">
<Text
ohos:id="$+id:text_aboutLabel"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="$string:about"
ohos:text_size="20fp"
ohos:layout_alignment="center"
ohos:top_margin="10vp"
ohos:bottom_margin="10vp"
ohos:left_margin="20vp"/>
<Image
ohos:id="$+id:icon_right_about"
ohos:height="24vp"
ohos:width="12vp"
ohos:align_parent_right="true"
ohos:right_margin="20vp"
ohos:top_margin="10vp"
ohos:image_src="$graphic:right_grey"/>
<Component
ohos:height="1vp"
ohos:width="match_parent"
ohos:background_element="$graphic:list_divider"
ohos:below="$id:text_aboutLabel"/>
</DependentLayout>
<DependentLayout
ohos:height="match_content"
ohos:width="match_parent">
<Text
ohos:id="$+id:text_deviceLabel"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="$string:device"
ohos:text_size="20fp"
ohos:layout_alignment="center"
ohos:top_margin="10vp"
ohos:bottom_margin="10vp"
ohos:left_margin="20vp"/>
<Image
ohos:id="$+id:icon_right_device"
ohos:height="24vp"
ohos:width="12vp"
ohos:align_parent_right="true"
ohos:right_margin="20vp"
ohos:top_margin="10vp"
ohos:image_src="$graphic:right_grey"/>
<Component
ohos:height="1vp"
ohos:width="match_parent"
ohos:background_element="$graphic:list_divider"
ohos:below="$id:text_deviceLabel"/>
</DependentLayout>
</DirectionalLayout>
<TabList
ohos:id="$+id:tab_bottom_list"
ohos:align_parent_bottom="true"
ohos:top_margin="10vp"
ohos:tab_margin="24vp"
ohos:tab_length="140vp"
ohos:text_size="20fp"
ohos:height="56vp"
ohos:width="match_parent"
ohos:layout_alignment="center"
ohos:orientation="horizontal"
ohos:text_alignment="left|vertical_center"
ohos:normal_text_color="#FF881515"
ohos:selected_text_color="#FFEC0B0B"
ohos:selected_tab_indicator_color="#FF1F0606"
ohos:selected_tab_indicator_height="2vp"
>
</TabList>
</DependentLayout>
2.实现MainAbilitySlice里listContainer和TabList的数据
2.1 创建实体类Clock,这个类包含了闹钟对象的所有属性
Clock.java
package com.madixin.clock.setting.model;
public class Clock {
private int id;//主键
private String name;//名称
private int hour;//小时
private int minute;//分钟
private int bell;//铃声
private int duration;//持续时间
private boolean isEnable;//是否开启
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHour() {
return hour;
}
public void setHour(int hour) {
this.hour = hour;
}
public int getMinute() {
return minute;
}
public void setMinute(int minute) {
this.minute = minute;
}
public int getBell() {
return bell;
}
public void setBell(int bell) {
this.bell = bell;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
public boolean isEnable() {
return isEnable;
}
public void setEnable(boolean enable) {
isEnable = enable;
}
}
2.2 创建ListViewClockItemProvider类继承BaseItemProvider,并实现其相关接口,它用于将clock对象转换成自定义列表项,其中内部类ViewHolder用于缓存自定义列表项的空间,避免每次都使用findComponentById查找控件。
ListViewClockItemProvider.java
package com.madixin.clock.setting.provider;
import com.madixin.clock.setting.ResourceTable;
import com.madixin.clock.setting.model.Clock;
import ohos.aafwk.ability.AbilitySlice;
import ohos.agp.components.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
public class ListViewClockItemProvider extends BaseItemProvider {
private AbilitySlice slice;
private List<Clock> dataList = new LinkedList<>();
private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
public ListViewClockItemProvider(AbilitySlice abilitySlice) {
this.slice = abilitySlice;
}
@Override
public int getCount() {
return dataList.size();
}
@Override
public Object getItem(int i) {
return dataList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
public List<Clock> getDataList() {
return dataList;
}
public void setDataList(List<Clock> dataList) {
this.dataList = dataList;
}
@Override
public Component getComponent(int position, Component component, ComponentContainer componentContainer) {
final Component cpt;
ViewHolder viewHolder;
if (component == null) {
cpt = LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_item_clock, null, false);
viewHolder = new ViewHolder();
viewHolder.textName = (Text) cpt.findComponentById(ResourceTable.Id_item_text_name);
viewHolder.textTime = (Text) cpt.findComponentById(ResourceTable.Id_item_text_time);
viewHolder.switchState = (Switch) cpt.findComponentById(ResourceTable.Id_item_switch_state);
cpt.setTag(viewHolder);
} else {
cpt = component;
viewHolder = (ViewHolder) component.getTag();
}
Clock clock = dataList.get(position);
viewHolder.textName.setText(clock.getName());
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, clock.getHour());
calendar.set(Calendar.MINUTE, clock.getMinute());
viewHolder.textTime.setText(sdf.format(calendar.getTime()));
viewHolder.switchState.setChecked(clock.isEnable());
return cpt;
}
class ViewHolder {
Text textName;
Text textTime;
Switch switchState;
}
}
2.3 在MainAbilitySlice的onstart方法里分别实现initTablist和initListContainer方法,动态添加listcontainer和tablist的数据。其中使用到了ClockManager的添加和查询方法,可先创建ClockManager类,并实现空的返回。
package com.madixin.clock.setting.slice;
import com.madixin.clock.common.util.LogUtil;
import com.madixin.clock.setting.ResourceTable;
import com.madixin.clock.setting.manager.ClockManager;
import com.madixin.clock.setting.model.Clock;
import com.madixin.clock.setting.provider.ListViewClockItemProvider;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Component;
import ohos.agp.components.Image;
import ohos.agp.components.ListContainer;
import ohos.agp.components.TabList;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ElementScatter;
import java.util.LinkedList;
import java.util.List;
public class MainAbilitySlice extends AbilitySlice {
private static final String TAG = MainAbilitySlice.class.getName();
private Image imageAddClock;
private ListContainer listClockContainer;
private ListViewClockItemProvider listViewClockItemProvider;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
initTablist();
initListContainer();
}
private void initListContainer() {
listClockContainer = (ListContainer) this.findComponentById(ResourceTable.Id_list_clock_view);
listViewClockItemProvider = new ListViewClockItemProvider(this);
List<Clock> clockList = getAllClocks();
if (clockList.size() == 0) {
Clock clock1 = new Clock();
clock1.setName("zzz闹钟");
clock1.setBell(0);
clock1.setHour(6);
clock1.setDuration(10);
clock1.setMinute(66);
clock1.setEnable(true);
ClockManager.getInstance(this.getApplicationContext()).createNewClock(clock1);
Clock clock2 = new Clock();
clock2.setName("madixin闹钟");
clock2.setBell(0);
clock2.setHour(8);
clock2.setDuration(10);
ClockManager.getInstance(this.getApplicationContext()).createNewClock(clock2);
clockList.add(clock1);
clockList.add(clock2);
}
listViewClockItemProvider.setDataList(clockList);
listClockContainer.setItemProvider(listViewClockItemProvider);
listClockContainer.setReboundEffect(true);
}
private List<Clock> getAllClocks() {
return new LinkedList<>();
}
private void initTablist() {
this.getApplicationContext();
Element icon = ElementScatter.getInstance(this.getContext()).parse(ResourceTable.Graphic_clock);
TabList tabList = (TabList) this.findComponentById(ResourceTable.Id_tab_bottom_list);
TabList.Tab tab = tabList.new Tab(this.getContext());
tab.setText(ResourceTable.String_clock);
tab.setIconElement(icon);
tab.setPadding(130, 0, 0, 0);
tabList.addTab(tab);
TabList.Tab tab2 = tabList.new Tab(this.getContext());
tab2.setText(ResourceTable.String_setting);
//tab2.setPadding(12, 0, 12, 0);
Element icon2 = ElementScatter.getInstance(this.getContext()).parse(ResourceTable.Graphic_setting);
tab2.setIconElement(icon2);
tab2.setPadding(130, 0, 0, 0);
tabList.addTab(tab2);
tabList.selectTab(tab);
tabList.setFixedMode(true);
AbilitySlice curSlice = this;
tabList.addTabSelectedListener(new TabList.TabSelectedListener() {
Component clockComponent = curSlice.findComponentById(ResourceTable.Id_layout_clock);
Component settingComponent = curSlice.findComponentById(ResourceTable.Id_layout_setting);
@Override
public void onSelected(TabList.Tab tab) {
LogUtil.info(TAG, "onSelected: " + tab.getPosition());
switch (tab.getPosition()) {
case 0:
clockComponent.setVisibility(Component.VISIBLE);
settingComponent.setVisibility(Component.HIDE);
break;
case 1:
clockComponent.setVisibility(Component.HIDE);
settingComponent.setVisibility(Component.VISIBLE);
break;
}
}
@Override
public void onUnselected(TabList.Tab tab) {
}
@Override
public void onReselected(TabList.Tab tab) {
}
});
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}
2.4 如果想后续方便的模拟数据以及预览界面,建议使用DevEco Studio的MockPreview功能:首先需要在gradle中引入依赖,然后添加MockMainAbilitySlice类继承PreviewerMock,并使用@PreviewerMockMethod去mock initTablist和getAllClocks两个方法,这样就能方便地使用mockpreview预览主列表界面了。
implementation group:'com.huawei.deveco',name:"previewer-mock-core",version:'1.0.0.1'
package com.madixin.clock.setting.mock;
import com.huawei.asm.core.PreviewerMock;
import com.huawei.asm.core.annotation.PreviewerMockMethod;
import com.madixin.clock.setting.ResourceTable;
import com.madixin.clock.setting.model.Clock;
import com.madixin.clock.setting.slice.MainAbilitySlice;
import ohos.agp.components.Component;
import ohos.agp.components.TabList;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ElementScatter;
import java.util.LinkedList;
import java.util.List;
public class MockMainAbilitySlice extends PreviewerMock {
private static final String TAG = MockMainAbilitySlice.class.getName();
@PreviewerMockMethod
public void initTablist(MainAbilitySlice mainAbilitySlice) {
Element icon = ElementScatter.getInstance(mainAbilitySlice.getContext()).parse(ResourceTable.Graphic_clock);
TabList tabList = (TabList) mainAbilitySlice.findComponentById(ResourceTable.Id_tab_bottom_list);
TabList.Tab tab = tabList.new Tab(mainAbilitySlice.getContext());
tab.setText(ResourceTable.String_clock);
tab.setIconElement(icon);
tab.setPadding(130, 0, 0, 0);
tabList.addTab(tab);
TabList.Tab tab2 = tabList.new Tab(mainAbilitySlice.getContext());
tab2.setText(ResourceTable.String_setting);
Element icon2 = ElementScatter.getInstance(mainAbilitySlice.getContext()).parse(ResourceTable.Graphic_setting);
tab2.setIconElement(icon2);
tab2.setPadding(130, 0, 0, 0);
tabList.addTab(tab2);
tabList.setFixedMode(true);
tabList.selectTab(tab);
tabList.addTabSelectedListener(new TabList.TabSelectedListener() {
@Override
public void onSelected(TabList.Tab tab) {
Component clockComponent = mainAbilitySlice.findComponentById(ResourceTable.Id_layout_clock);
Component settingComponent = mainAbilitySlice.findComponentById(ResourceTable.Id_layout_setting);
switch (tab.getPosition()) {
case 0:
clockComponent.setVisibility(Component.VISIBLE);
settingComponent.setVisibility(Component.HIDE);
break;
case 1:
clockComponent.setVisibility(Component.HIDE);
settingComponent.setVisibility(Component.VISIBLE);
break;
}
}
@Override
public void onUnselected(TabList.Tab tab) {
}
@Override
public void onReselected(TabList.Tab tab) {
}
});
}
@PreviewerMockMethod
public List<Clock> getAllClocks(MainAbilitySlice mainAbilitySlice) {
List<Clock> clockList = new LinkedList<>();
Clock clock1 = new Clock();
clock1.setName("早起闹钟");
clock1.setHour(7);
clock1.setMinute(55);
clock1.setEnable(true);
Clock clock2 = new Clock();
clock2.setName("上班闹钟");
clock2.setHour(8);
clock2.setMinute(0);
clock2.setEnable(false);
Clock clock3 = new Clock();
clock3.setName("下班闹钟");
clock3.setHour(9);
clock3.setMinute(7);
//clock3.setTime("09:00");
clockList.add(clock1);
clockList.add(clock2);
clockList.add(clock3);
return clockList;
}
@PreviewerMockMethod
public void createNewClock(MainAbilitySlice mainAbilitySlice) {
}
}
3.小结
本节完成了主列表页面的开发,以及mockpreview的预览。
下一步我们将开发闹钟创建界面。
4.代码地址:
github,提交记录:66f74fdb058f58fb6d640f94017532f9174ac392