当前位置:   article > 正文

鸿蒙基础知识-自定义组件_鸿蒙自定义组件的组件化特点

鸿蒙自定义组件的组件化特点

一.概述

在实际的开发过程中,往往系统自己自带的组件是不能满足开发需求的,需要自己按照系统的规则去定制自己的组件;

鸿蒙os中的所有的组件的基类直接或者间接Component类,像Button,text,Image等等,能容纳别的组件的组件我们一般称之为容器类组件,这类组件的基类直接或者间接是ComponentContainer,像DirectionalLayout,DependentLayout等等;

二.自定义Component组件

1.继承Component类

  1. public class TestComponent extends Component {
  2. public TestComponent(Context context) {
  3. super(context);
  4. }
  5. public TestComponent(Context context, AttrSet attrSet) {
  6. super(context, attrSet);
  7. }
  8. public TestComponent(Context context, AttrSet attrSet, String styleName) {
  9. super(context, attrSet, styleName);
  10. }
  11. public TestComponent(Context context, AttrSet attrSet, int resId) {
  12. super(context, attrSet, resId);
  13. }
  14. }

创建TestComponent继承Component并覆写其中的构造方案,如果当前的自定义组件需要使用到xml中,那需要至少复现一个带AttrSet参数的方法,因为xml中的属性需要用这个类获取和设置,是不是跟android的感觉一样,后面会有跟多的相似的地方;

2.设置绘制的回调

(1)首先创建一个绘制的task

  1. private DrawTask mDrawTask = new DrawTask() {
  2. @Override
  3. public void onDraw(Component component, Canvas canvas) {
  4. TestComponent.this.onDraw(component,canvas);
  5. }
  6. };

 

(2)实现上一步骤中的onDraw方法

  1. private void onDraw(Component component, Canvas canvas) {
  2. //todo 这里就是使用canvas绘制我们需要内容
  3. }

(3)在构造方法中加入Task

  1. public TestComponent(Context context) {
  2. super(context);
  3. addDrawTask(mDrawTask);
  4. }
  5. public TestComponent(Context context, AttrSet attrSet) {
  6. super(context, attrSet);
  7. addDrawTask(mDrawTask);
  8. }

这里只是展示了两个构造方案中调用addDrawTask,但不是只能这么用,因为鸿蒙os在绘制我们的组件的时候,会调用我们add进去的这个task,所以我们提前把我们业务逻辑add进去而已;

这样你只需要在private void onDraw中绘制我们的内容就可以了;

 

例如我们绘制一个画线的画板:绘制的代码如下

  1. import ohos.agp.components.AttrSet;
  2. import ohos.agp.components.Component;
  3. import ohos.agp.render.Canvas;
  4. import ohos.agp.render.Paint;
  5. import ohos.agp.render.Path;
  6. import ohos.agp.utils.Color;
  7. import ohos.agp.utils.Point;
  8. import ohos.app.Context;
  9. import ohos.hiviewdfx.HiLog;
  10. import ohos.hiviewdfx.HiLogLabel;
  11. import ohos.multimodalinput.event.MmiPoint;
  12. import ohos.multimodalinput.event.TouchEvent;
  13. public class TestComponent extends Component {
  14. private static final String TAG = "TestComponent";
  15. private DrawTask mDrawTask = (component, canvas) -> TestComponent.this.onDraw(component,canvas);
  16. private TouchEventListener mTouchEventListener = (component, touchEvent) -> TestComponent.this.onTouchEvent(component, touchEvent);
  17. private Paint mPaint;
  18. private Path mPath;
  19. private Point mPrePoint;
  20. private Point mPreCtrlPoint;
  21. public TestComponent(Context context) {
  22. super(context);
  23. initPaint();
  24. addDrawTask(mDrawTask);
  25. setTouchEventListener(mTouchEventListener);
  26. }
  27. public TestComponent(Context context, AttrSet attrSet) {
  28. super(context, attrSet);
  29. initPaint();
  30. addDrawTask(mDrawTask);
  31. setTouchEventListener(mTouchEventListener);
  32. }
  33. public TestComponent(Context context, AttrSet attrSet, String styleName) {
  34. super(context, attrSet, styleName);
  35. }
  36. public TestComponent(Context context, AttrSet attrSet, int resId) {
  37. super(context, attrSet, resId);
  38. }
  39. private void initPaint() {
  40. //初始化paint
  41. mPaint = new Paint();
  42. mPaint.setColor(Color.WHITE);
  43. mPaint.setStrokeWidth(5f);
  44. mPaint.setStyle(Paint.Style.STROKE_STYLE);
  45. }
  46. private void onDraw(Component component, Canvas canvas) {
  47. canvas.drawPath(mPath, mPaint);
  48. }
  49. public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
  50. switch (touchEvent.getAction()) {
  51. case TouchEvent.PRIMARY_POINT_DOWN: {
  52. //鸿蒙Log工具
  53. HiLog.debug(new HiLogLabel(0, 0, TAG), "TouchEvent.PRIMARY_POINT_DOWN");
  54. //获取点信息
  55. MmiPoint point = touchEvent.getPointerPosition(touchEvent.getIndex());
  56. mPath.reset();
  57. mPath.moveTo(point.getX(), point.getY());
  58. mPrePoint.position[0] = point.getX();
  59. mPrePoint.position[1] = point.getY();
  60. mPreCtrlPoint.position[0] = point.getX();
  61. mPreCtrlPoint.position[1] = point.getY();
  62. //PRIMARY_POINT_DOWN 一定要返回true
  63. return true;
  64. }
  65. case TouchEvent.PRIMARY_POINT_UP:
  66. break;
  67. case TouchEvent.POINT_MOVE: {
  68. HiLog.debug(new HiLogLabel(0, 0, TAG), "TouchEvent.POINT_MOVE");
  69. MmiPoint point = touchEvent.getPointerPosition(touchEvent.getIndex());
  70. Point currCtrlPoint = new Point((point.getX() + mPrePoint.position[0]) / 2,
  71. (point.getY() + mPrePoint.position[1]) / 2);
  72. //绘制三阶贝塞尔曲线
  73. mPath.cubicTo(mPrePoint, mPreCtrlPoint, currCtrlPoint);
  74. mPreCtrlPoint.position[0] = currCtrlPoint.position[0];
  75. mPreCtrlPoint.position[1] = currCtrlPoint.position[1];
  76. mPrePoint.position[0] = point.getX();
  77. mPrePoint.position[1] = point.getY();
  78. //更新显示
  79. invalidate();
  80. break;
  81. }
  82. }
  83. return false;
  84. }
  85. }

上面就实现自定义普通组件的过程

三.自定义ComponentContainer组件

1.继承ComponentContainer类

  1. public class TestContainer extends ComponentContainer {
  2. public TestContainer(Context context) {
  3. super(context);
  4. }
  5. public TestContainer(Context context, AttrSet attrSet) {
  6. super(context, attrSet);
  7. }
  8. public TestContainer(Context context, AttrSet attrSet, String styleName) {
  9. super(context, attrSet, styleName);
  10. }
  11. }

跟自定义Component一样继承容器类控件的基类,并实现其中的构造方法

2.添加测量,布局,绘制的回调

因为容器类组件是能容纳其他组件的特殊组件,所以它不仅仅需要关心自己,也需要关心它内部的子组件,故我们需要关注的方法变成了三个:

(1)初始化测量,布局,绘制的回调

  1. //测量的回调
  2. private EstimateSizeListener mEstimateSizeListener = new EstimateSizeListener() {
  3. @Override
  4. public boolean onEstimateSize(int widthEstimateConfig, int heightEstimateConfig) {
  5. return TestContainer.this.onEstimateSize(widthEstimateConfig,heightEstimateConfig);
  6. }
  7. };
  8. //布局的回调
  9. private ArrangeListener mArrangeListener = new ArrangeListener() {
  10. @Override
  11. public boolean onArrange(int left, int top, int width, int height) {
  12. return TestContainer.this.onArrange(left,top,width,height);
  13. }
  14. };
  15. //绘制的回调
  16. private DrawTask mDrawTask = new DrawTask() {
  17. @Override
  18. public void onDraw(Component component, Canvas canvas) {
  19. onDraw(component,canvas);
  20. }
  21. };

里面的回调,下面会讲解,先把基本步骤搞定;

(2)添加回调

  1. public TestContainer(Context context) {
  2. super(context);
  3. //add测量的回调
  4. setEstimateSizeListener(mEstimateSizeListener);
  5. //add布局的回调
  6. setArrangeListener(mArrangeListener);
  7. //add绘制的回调
  8. addDrawTask(mDrawTask);
  9. }
  10. public TestContainer(Context context, AttrSet attrSet) {
  11. super(context, attrSet);
  12. //add测量的回调
  13. setEstimateSizeListener(mEstimateSizeListener);
  14. //add布局的回调
  15. setArrangeListener(mArrangeListener);
  16. //add绘制的回调
  17. addDrawTask(mDrawTask);
  18. }

这就是自定义的ComponentContainer的基本步骤

 

上面步骤中

setEstimateSizeListener(mEstimateSizeListener); 这个是设置测量的回调,里面是一个接口,参数是int类型,分别是widthEstimateConfig和heightEstimateConfig

这两个值也是分为Mode和value的,获取这Mode和Value使用的是EstimateSpec这个类,如下:

  1. int wideMode = EstimateSpec.getMode(widthEstimateConfig);
  2. int wideSize = EstimateSpec.getSize(widthEstimateConfig);
  3. int heightMode = EstimateSpec.getMode(heightEstimateConfig);
  4. int heightSize = EstimateSpec.getSize(heightEstimateConfig);

Mode的值一共有如下三种:

NOT_EXCEED :在此模式下,已为子组件指定最大大小;

PRECISE :在这种模式下,父组件已经确定了子组件的确切尺寸;

UNCONSTRAINT :在这种模式下,父组件对子组件没有任何约束,这意味着子组件可以是所需的任何大小;

 

四.对比android,加深理解

 

                                               android和鸿蒙对比

 android鸿蒙
基类View(普通组件)ViewGroup(容器类组件)Component(普通组件)ComponentContainer(容器类组件)
普通组件覆写onDraw方法

添加绘制的任务(回调)

添加方法 addDrawTask   

任务类 DrawTask

容器类组件覆写测量方法onMeasure

添加测量的回调 

设置方法 setEstimateSizeListener

回调接口的方法 onEstimateSize

覆写布局方法onLayout

添加布局的回调 

设置方法 setArrangeListener

回调接口的方法 onArrange

覆写绘制方法onDraw

添加绘制的任务(回调)

添加方法 addDrawTask   

任务类 DrawTask

Mode值UNSPECIFIED:父容器没有对当前View有任何限制,当前View可以任意取尺寸UNCONSTRAINT:在这种模式下,父组件对子组件没有任何约束,这意味着子组件可以是所需的任何大小
EXACTLY:当前的尺寸就是当前View应该取的尺寸PRECISE:在这种模式下,父组件已经确定了子组件的确切尺寸
AT_MOST:当前尺寸是当前View能取的最大尺寸NOT_EXCEED:在此模式下,已为子组件指定最大大小

 

 

 

 

 

 

 

 

总结,以上就是鸿蒙自定义组件的基础内容,由于当前还看不到系统的源码,具体的实现逻辑还有待开源后补充;

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/612648
推荐阅读
相关标签
  

闽ICP备14008679号