认识组件与布局
HarmonyOS提供了Ability和AbilitySlice两个基础类,一个有界面的Ability可以由一个或多个AbilitySlice构成,AbilitySlice主要用于承载单个页面的具体逻辑实现和界面UI,是应用显示、运行和跳转的最小单元。AbilitySlice通过setUIContent为界面设置布局。
组件需要进行组合,并添加到界面的布局中。在Java UI框架中,提供了两种编写布局的方式:
- 在代码中创建布局:用代码创建Component和ComponentContainer对象,为这些对象设置合适的布局参数和属性值,并将Component添加到ComponentContainer中,从而创建出完整界面。
- 在XML中声明UI布局:按层级结构来描述Component和ComponentContainer的关系,给组件节点设定合适的布局参数和属性值,代码中可直接加载生成此布局。
这两种方式创建出的布局没有本质差别,在XML中声明布局,在加载后同样可在代码中对该布局进行修改。
组件的分类
组件类别 | 组件名称 | 功能描述 |
---|---|---|
布局类 | PositionLayout、DirectionalLayout、StackLayout、DependentLayout、TableLayout、AdaptiveBoxLayout | 提供了不同布局规范的组件容器,例如以单一方向排列的DirectionalLayout、以相对位置排列的DependentLayout、以确切位置排列的PositionLayout等。 |
显示类 | Text、Image、Clock、TickTimer、ProgressBar | 提供了单纯的内容显示,例如用于文本显示的Text,用于图像显示的Image等。 |
交互类 | TextField、Button、Checkbox、RadioButton/RadioContainer、Switch、ToggleButton、Slider、Rating、ScrollView、TabList、ListContainer、PageSlider、PageFlipper、PageSliderIndicator、Picker、TimePicker、DatePicker、SurfaceProvider、ComponentProvider | 提供了具体场景下与用户交互响应的功能,例如Button提供了点击响应功能,Slider提供了进度选择功能等。 |
事件
常见的事件:
- 单击事件
- 双击事件
- 滑动事件
- 长按事件
- …….
单击事件
定义实现类
- 通过id找到组件。
- 给按钮组件设置单击事件。
- 写一个类实现ClickedListener接口并重写onClick方法。
- 编写onClick方法体。
public class MainAbilitySlice extends AbilitySlice {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
Button button = (Button) this.findComponentById(ResourceTable.Id_button1);
button.setClickedListener(new MyListener());
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}
class MyListener implements Component.ClickedListener{
@Override
public void onClick(Component component) {
Button button = (Button)component;
button.setText("被点击了!!");
}
}
当前类作为实现类
public class MainAbilitySlice extends AbilitySlice implements Component.ClickedListener {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
Button button = (Button) this.findComponentById(ResourceTable.Id_button1);
button.setClickedListener(this);
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
@Override
public void onClick(Component component) {
Button button = (Button) component;
button.setText("被点击了!!");
}
}
匿名内部类
public class MainAbilitySlice extends AbilitySlice {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
Button button = (Button) this.findComponentById(ResourceTable.Id_button1);
button.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
Button button = (Button) component;
button.setText("被点击了!!");
}
});
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}
也可用lambda表达式
public class MainAbilitySlice extends AbilitySlice {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
Button button = (Button) this.findComponentById(ResourceTable.Id_button1);
button.setClickedListener(component -> {
Button button1 = (Button) component;
button1.setText("被点击了!!");
});
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}
弊端:写的代码只能使用一次
方法引用
public class MainAbilitySlice extends AbilitySlice {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
Button button = (Button) this.findComponentById(ResourceTable.Id_button1);
button.setClickedListener(this::myOnClick);
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
public void myOnClick(Component component) {
Button button = (Button) component;
button.setText("被点击了!!");
}
}
以上四种方法,常用的是当前类作为实现类、方法引用
双击事件
与单击事件类似,代码实现略
- 通过id找到组件。
- 给按钮组件设置双击事件。
- 本类实现DoubleClickedListener接口重写。
- 重写onDoubleClick方法体。
长按事件
代码实现略
- 通过id找到组件。
- 给按钮组件设置长按事件。
- 本类实现LongClickedListener接口。
- 重写onLongClicked方法。
滑动事件
滑动包括三个动作:
- 按下不松
- 移动
- 松开
因此有按下位置,松开位置,代码如下
public class MainAbilitySlice extends AbilitySlice implements Component.TouchEventListener {
Text text;
int count = 0;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
text = (Text) findComponentById(ResourceTable.Id_text1);
DirectionalLayout dl = (DirectionalLayout) findComponentById(ResourceTable.Id_dl);
dl.setTouchEventListener(this);
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
/**
* @param component 表示滑动的那个组件(布局也是一种组件)
* 实际上此时代表的就是那个DirectionalLayout这个布局对象。
* @param touchEvent 动作对象(按下、滑动、抬起)
* @return
*/
@Override
public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
count++;
int action = touchEvent.getAction();
if (action == TouchEvent.PRIMARY_POINT_DOWN) {
text.setText("按下"+count);
} else if (action == TouchEvent.POINT_MOVE) {
text.setText("滑动"+count);
} else if (action == TouchEvent.PRIMARY_POINT_UP) {
text.setText("松开"+count);
}
return true;
}
}
通过按下位置,松开位置,可以判断滑动方向。
手机中的坐标轴
一般来说很少用到Z轴,所以我们大部分时间只关注X,Y轴X坐标不变,Y坐标变大:下滑
X坐标不变,Y坐标变小:上滑
Y坐标不变,X坐标变大:右滑
Y坐标不变,X坐标变小:左滑
/**
* @param component 表示滑动的那个组件(布局也是一种组件)
* <p>
* 实际上此时代表的就是那个DirectionalLayout这个布局对象。
* @param touchEvent 动作对象(按下、滑动、抬起)
* @return boolean 布尔
* 如果为true,表示所有的动作都会触发当前方法并执行对应代码。
* 如果为false,表示只有第一个动作会触发当前方法并执行对应代码。
* 后续的动作就不会触发当前方法了。
* 按下 --- 移动 --- 松开
*/
@Override
public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
count++;
//获取当前手指对屏幕进行的操作(按下,滑动,抬起)
int action = touchEvent.getAction();
if (action == TouchEvent.PRIMARY_POINT_DOWN) {
//获取按下时手指的位置(坐标)
MmiPoint point = touchEvent.getPointerPosition(0);
startX = point.getX();
startY = point.getY();
//text.setText(x + "---" + y);
text.setText("按下");
} else if (action == TouchEvent.POINT_MOVE) {
MmiPoint point = touchEvent.getPointerPosition(0);
float x = point.getX();
float y = point.getY();
text.setText(x + "---" + y);
text.setText("移动");
} else if (action == TouchEvent.PRIMARY_POINT_UP) {
text.setText("松开");
MmiPoint point = touchEvent.getPointerPosition(0);
float endX = point.getX();
float endY = point.getY();
//text.setText(x + "---" + y);
//拿着按下时手指的位置跟松开时手指的位置进行比较就可以
if (endX > startX && Math.abs(endY - startY) < 100) {
text.setText("右滑");
} else if (endX < startX && Math.abs(endY - startY) < 100) {
text.setText("左滑");
} else if (endY > startY && Math.abs(endX - startX) < 100) {
text.setText("下滑");
} else if (endY < startY && Math.abs(endX - startX) < 100) {
text.setText("上滑");
}
}
return true;
}
onTouchEvent()方法的返回值
是boolean类型:
- 如果为true,表示所有的动作都会触发当前方法并执行对应代码。
- 如果为false,表示只有第一个动作会触发当前方法并执行对应代码。后续的动作就不会触发当前方法了。
按下 — 移动 — 松开,这是正常的流程,若返回值为false,则只会触发按下,移动与松开不会触发。