当前位置:   article > 正文

鸿蒙系统的自定义手势密码功能实现

鸿蒙系统的自定义手势密码功能实现

简述:在最经开发鸿蒙app的过程中,需要做一个手势密码登录页面,虽然鸿蒙官网上提供了相应的组件,但是并不能满足我们的业务需求,所有我们只能自己自定义控件。先来看看我自己自定义手势密码页面的效果,大体满足了实际的业务需求。

先看下效果图

具体的实现步骤如下:

1. 确定ui容器的选择

手势密码一般都是九宫格的方式排列,很明显这里最优的选择容器是RelativeContainer

这个容器就相当于Android的RelativeLayout,用法也很相似,不清楚的朋友可以自行去官网查看

用法,这里就不一一讲解了。

大致的布局模型图如下

一般情况下手势密码都是9个点,但是不排除有二班的情况,为了防止设置的的4*4或者5*5的情况出现,所以我在布局的地方采用了一种更灵活的方式,逻辑可能有些许绕,不过静下心来也是很容易看懂的,直接上代码:

  1. ForEach(this.ViewArr, (item: ChildViewModel, index: number) => {
  2. GestureLockView({
  3. clickImage: this.clickImage,
  4. defaultImage: this.defaultImage,
  5. mGestureLockViewWidth: this.mGestureLockViewWidth,
  6. mMarginBetweenLockView: this.mMarginBetweenLockView,
  7. childViewModel: item
  8. })
  9. .alignRules({
  10. top: {
  11. //如果是第一行,则以自身为基础,如果不是第一行,为在上一行元素的底部
  12. anchor: (index < this.mCount ? '__container__' : ('view_' + (index - this.mCount))),
  13. //如果是第一行则头部为VerticalAlign.Top,否则头部与前一行尾部对齐
  14. align: (index < this.mCount ? VerticalAlign.Top : VerticalAlign.Bottom)
  15. },
  16. //如果是第一列则为以自身为基础,如果不是,则以前一个元素为基础,在他的右侧
  17. left: {
  18. anchor: (index % this.mCount === 0 ? '__container__' : ('view_' + (index - 1))),
  19. //如果是第一列那么头对应HorizontalAlign.Start,否则头部就与前面一列的尾部对齐
  20. align: (index % this.mCount === 0 ? HorizontalAlign.Start : HorizontalAlign.End)
  21. }
  22. })
  23. .margin({
  24. left: this.mMarginBetweenLockView,
  25. top: this.mMarginBetweenLockView,
  26. right: this.mMarginBetweenLockView,
  27. bottom: this.mMarginBetweenLockView,
  28. })
  29. .id(item.viewId)
  30. .onAreaChange((oldValue: Area, newValue: Area) => {
  31. item.left = newValue.position.x?.valueOf() as number;
  32. item.top = newValue.position.y?.valueOf() as number;
  33. item.right = item.left + (newValue.width.valueOf() as number)
  34. item.bottom = item.top + (newValue.height.valueOf() as number)
  35. item.centX = ((item.left + item.right) / 2.0) + ""
  36. item.centY = ((item.top + item.bottom) / 2.0) + ""
  37. })
  38. ;
  39. })

注释也详细,这里就不讲解了

2.自定义画布和path组件的摆放

通过上面的步骤我们实现了手势密码的布局,接下来我们需要实现手指与屏幕交互的功能(画连接线)。这里需要通过画布和path组件来实现来实现,首先我们需定义一个画布组件

  1. Canvas(this.canvasContext)
  2. .width(this.mWidth)
  3. .height(this.mWidth)
  4. .id("Canvas")

拿到画布组件后,需要将画布组件盖在RelativeContainer上,紧着这我们需要获取一个path组件,然后将path组件盖在画布组件上面

  1. Line()
  2. .startPoint([this.startPointX, this.startPointY])
  3. .endPoint([this.endPointX, this.endPointY])
  4. .strokeWidth(px2vp(5))
  5. .stroke(this.lineColor)
  6. .id("line")

这样我们所有的组件摆放动作就完成了。

3.实现手指与屏幕的交互

我们想要在屏幕上的画布上画线,我们必须要直到我们所画直线的起点坐标和终点坐标,鸿蒙系统给我们提供了组件的触摸事件,我们只需要从触摸事件的回调函数中获取手指点击屏幕的坐标值,然后再将这两个值作为path的起点。

那么怎么实现两个按钮之间的连线呢?这就需要通过画布来实现了,这里我熟悉两个特别重要的api,一个是moveTo,一个是LineTo,这两个api和Android中path中的moveTo,pathTo含义基本一样。moveTo(x,y)的意思是将路径的起点设置为这个函数的x,y中,我们需要画的路劲就需要这个x,y为起点,LineTo这个函数的意思是设计此path的终点,并且将此终点设置为下一段path的起点。熟悉了这两个重要的api,我们就可以接着就可以干活了

当手指触碰到第一个按钮的时候,记下按钮的圆心的坐标值,然后将它通过画布设置到moveTo(x,y)函数中,当手指滑动到第二手势密码按钮时,我们再将第二个手势密码的按钮的坐标传入到LineTo(x,y)中,这就完成了两个按钮之间的连线,当手指触碰到第三个按钮时,我们将第三个按钮的坐标的值设置到LineTo(x,y)中,这样就实现了第二个点和第三个点相连。以此类推,这样就实现了手势密码之间各个按钮之间的连线。

当然这这只是实现了各个按钮之间的连线,我们经常使用手势密码的时候,会看到一个引导线,也就是我们经常说的方向线。这个功能的实现我们需要用到Line组件了。

同样当手指触碰到第一个按钮的时候,记下按钮的圆心的坐标值,然后将此坐标值设置到Line组件的startPoint,当手指触碰的实时坐标传入到Line的endPoint中,这样就实现了引导线的功能。

还是直接上代码吧。

  1. .onTouch((event: TouchEvent) => {
  2. let x: number = ((event.touches[0].x) < 0 ? 0 : (event.touches[0].x))
  3. x = (x > this.mWidth ? this.mWidth : x)
  4. let y: number = ((event.touches[0].y) < 0 ? 0 : (event.touches[0].y))
  5. y = (y > this.mWidth ? this.mWidth : y)
  6. switch (event.type) {
  7. //按下
  8. case TouchType.Down:
  9. this.reset();
  10. this.canvasContext.beginPath()
  11. this.canvasContext.lineWidth = px2vp(5)
  12. this.canvasContext.strokeStyle = this.lineColor
  13. break;
  14. //移动
  15. case TouchType.Move:
  16. //获取当前的点击的ChildViewModel
  17. let childViewModel: ChildViewModel | undefined = this.getChildIdByPos(x, y, this.ViewArr)
  18. if (childViewModel != undefined) {
  19. let passWord: number = childViewModel.password;
  20. this.ViewArr[childViewModel.indexView].isClick = true;
  21. if (!this.passwordArr.includes(passWord)) {
  22. this.passwordArr.push(passWord)
  23. this.mLastPathX = childViewModel.left / 2 + childViewModel.right / 2;
  24. this.mLastPathY = childViewModel.top / 2 + childViewModel.bottom / 2;
  25. // 设置引线的起点坐标
  26. this.startPointX = this.mLastPathX;
  27. this.startPointY = this.mLastPathY;
  28. if (this.passwordArr.length == 1) // 当前添加为第一个
  29. {
  30. this.canvasContext.moveTo(this.mLastPathX, this.mLastPathY);
  31. } else
  32. // 非第一个,将两者使用线连上
  33. {
  34. this.canvasContext.lineTo(this.mLastPathX, this.mLastPathY);
  35. this.canvasContext.stroke()
  36. }
  37. }
  38. }
  39. //设置引导线的终点坐标
  40. this.endPointX = (this.startPointX == 0 ? 0 : x);
  41. this.endPointY = (this.startPointY == 0 ? 0 : y);
  42. break;
  43. //抬起
  44. case TouchType.Up:
  45. //设置引导线的终点坐标
  46. this.endPointX = (this.startPointX == 0 ? 0 : this.mLastPathX);
  47. this.endPointY = (this.startPointY == 0 ? 0 : this.mLastPathY);
  48. if (this.passwordArr.length < this.minLength) {
  49. if (this.passwordArr.length > 0) {
  50. if (this.callBack != undefined) {
  51. this.callBack(ResultCode.fail, "至少连接" + this.minLength + "个点");
  52. }
  53. }
  54. this.reset();
  55. } else {
  56. let passWord: string = "";
  57. for (let passwordArrElement of this.passwordArr) {
  58. passWord += passwordArrElement;
  59. }
  60. if (this.callBack != undefined) {
  61. this.callBack(ResultCode.success, passWord);
  62. }
  63. }
  64. break;
  65. }

至此自定义手势密码控件的大体功能就实现了,如有错误欢迎各位大佬指点

源码地址:myCode: 鸿蒙系统自定义手势密码的实现

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

闽ICP备14008679号