16

Android-Kotlin VS Flutter-Dart - 自定义控制摇杆

 4 years ago
source link: https://juejin.im/post/5e2694f36fb9a02fef3a6c7d
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
2020年01月21日 阅读 3204

Android-Kotlin VS Flutter-Dart - 自定义控制摇杆

比较Kotlin OR Flutter 谁好, 就像在比较[程序员]和[画家]谁[唱歌]比较好听

Kotlin是语言,完美平复了我对Java的恨铁不成钢的心情。
Flutter是框架,完美实现了我一套代码,六端运行的梦想。

Flutter是和Android一个等级的,它们都是运行在设备上的框架
Kotlin是和Dart一个等级的,它们都是新时代的编程语言  
那谁更香? 别问,问就都香。 

如果你还在Kotlin和Flutter之间犹豫不定  
那我就为你指条路: 去研究[数据结构和算法分析]
研究到想吐的时候再来选择,如果还是在Kotlin和Flutter犹豫不定  
那我就为你指条路: 去研究[数据结构和算法分析]
研究到想吐的时候再来选择,如果还是在Kotlin和Flutter犹豫不定  
那我就为你指条路: 去研究[数据结构和算法分析]
研究到想吐的时候再来选择,如果还是在Kotlin和Flutter犹豫不定  
...
这样最终你就会成为一个[数据结构和算法分析]的大师
而这样的大师拥有同时掌握Kotlin和Flutter的耐力和技术支持
复制代码

通过本篇想说明: 不是什么技术好不好,而是你能干什么。

就像作文模板、中文汉字你都认识,却无法写出流芳百世的佳作
限制你的并非是语言/框架本身,而是你的思维分析和解决问题的能力

王侯将相宁有种乎? 何必贴上好坏的标签,非争个天下第一? 香不就行了吗!

KotlinFlutter
16fc2d65cdcd53cd?imageslim
16fc66f76e4792c0?imageslim

Android-Kotlin 篇

一、自定义控件

1.类的定义
 [1] 类通过[class]关键字定义,类名[大驼峰]  
 [2] 构造器关键字[constructor],可直接跟在类名后  
 [3] 继承通过 : 指定父类
复制代码
class HandleView constructor(context: Context, attrs: AttributeSet? = null) : View(context, attrs){

}
复制代码

2.初始函数与变量定义
[1] 通过[var]关键字指定变量, 
[2] 通过[privite]关键字修饰私有权限
[3] 创建对象[不需要] new 关键字
[4] 一条语句的末尾[不需要] ; 
[5] init代码块内可以盛放数据初始逻辑
[6] 对于对象的get/set方法,可使用简写方式
复制代码
class HandleView constructor(context: Context, attrs: AttributeSet? = null) : View(context, attrs){

    private var paint:Paint = Paint()

    init {
        paint.color = Color.BLUE
        paint.isAntiAlias=true
        paint.style=Paint.Style.FILL
    }
}
复制代码

3.方法的覆写
[1] 方法的关键字[fun]
[2] 复写的关键字[override]
[3] 入参格式 [名称:类型 ]
复制代码
class HandleView constructor(context: Context, attrs: AttributeSet? = null) : View(context, attrs){
   //英雄所见...
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawCircle(100f,100f,50f,paint)
    }
}
复制代码

4.使用自定义控件

这里先直接放在setContentView里看下效果

1
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(HandleView(this))
    }
}
复制代码

二、事件响应

如下图,在移动时会触发事件,根据事件来处理小圆的坐标

当超越区域时,对其进行限定。放手时回到中心

16fc2d65cdcd53cd?imageslim

1.变量介绍

zoneR:背景区域大小,即限定摇杆的区域
handleR:摇杆大小 centerX,centerY摇杆中心偏移

1
private var zoneR: Float = 150f
private var handleR: Float = 80f
priate var centerX: Float = 0f
private var centerY: Float = 0f
复制代码

2.绘制圆

绘制圆时,移动了一下画布,将画布左上角和中心重合

1
[1] 当函数返回值只要一行时,可以直接用 = 
复制代码
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    canvas.translate(maxR(), maxR())
    paint.alpha = 100
    canvas.drawCircle(0f, 0f, zoneR, paint)
    paint.alpha = 150
    canvas.drawCircle(centerX, centerY, handleR, paint)
}

fun maxR():Float = zoneR + handleR
复制代码

3.事件处理

核心就是处理好小圆的圆心在画布坐标系的位置。

分为在圆内和圆外两种情况:
触点在域内,根据触点位置确定摇杆圆心位置
触点在域外,摇杆圆心位置在域的边缘游走

1

在parser方法里,实现通过atan2获取夹角(图左)

然后转化为通常的坐标系(图左),由于再根据画布坐标系校正90°

1
[1] when 用于条件分支,相当于switch
复制代码
override fun onTouchEvent(event: MotionEvent): Boolean {
    when (event.action) {
        MotionEvent.ACTION_UP -> reset()
        else -> parser(event)
    }
    invalidate()
    return true
}

private fun reset() {
    centerX = 0f
    centerY = 0f
}

private fun parser(event: MotionEvent) {
    centerX = event.x - maxR()
    centerY = event.y - maxR()
    var rad = atan2(centerX, centerY)
    if(centerX<0){
        rad += 2*PI.toFloat()
    }
    val thta = rad- PI.toFloat()/2  //旋转坐标系90度
    if (sqrt(centerX * centerX + centerY * centerY) > zoneR) {
        centerX =  zoneR * cos(thta)
        centerY = -zoneR * sin(thta)
    }
}
复制代码

4.监听器回调

现在数据是在View内部的,需要将它们暴露出去,比如旋转的角度,位移百分比

Java中设置监听,还要啰嗦一堆弄接口,校验回调。
由于Kotlin函数也是类型,回调起来就so easy

1
[1] (rad:Float, offset:Float) -> Unit 代表一种函数类型
复制代码
lateinit var onHandleListener:(rad:Float, offset:Float) -> Unit

private fun parser(event: MotionEvent) {
    //英雄所见...
    val len = sqrt(centerX * centerX + centerY * centerY)
    onHandleListener(rad,len/zoneR)
}
复制代码

[1] Kotlin 的lambda 表达式形式如下
复制代码
handleView.onHandleListener= {
    rad,offset ->
    Log.e("MainActivity","角度${rad*180/Math.PI},位移:${offset}")        
}
复制代码

Flutter-Dart 篇

一、自定义组件

1. 继承自StatefulWidget

很明显,移动中需要改变状态,使用基础自继承自StatefulWidget

 [1] 类通过[class]关键字定义,类名[大驼峰]  
 [2] 继承通过 [extends] 指定父类
 [3] final 修饰不可变量
 [4] 构造函数通过 {this.XXX} 初始化变量,可通过具名传参
 [5] 函数只要一行时,通过 => 简写
复制代码
class HandleWidget extends StatefulWidget {
  final double zoneR;
  final double handleR;

  HandleWidget(
      {this.zoneR = 60.0,
      this.handleR = 30.0});

  @override
  _HandleWidgetState createState() => _HandleWidgetState();
}

复制代码

2.状态类中操作状态
[1] 覆写可使用 @override 注解
[2] get关键字XXX,可以像一样属性访问XXX
[3] 私有包/类/字段 通过_XXX指定
复制代码
class _HandleWidgetState extends State<HandleWidget> {
  double zoneR;
  double handleR;
  double centerX=0.0;
  double centerY=0.0;
  
  @override
  void initState() {
    zoneR=widget.zoneR;
    handleR=widget.handleR;
    super.initState();
  }
  
  @override
  Widget build(BuildContext context) {
    return CustomPaint(
          painter: _HandleView(zoneR: zoneR, handleR: handleR, centerX: centerX, centerY: centerY)
          )
    );
  }
  double get maxR => zoneR + handleR;
}
复制代码

[1] ..级联操作,相当于该对象
复制代码
1
class _HandleView extends CustomPainter {
  var _paint = Paint();
  var zoneR;
  var handleR;
  var centerX;
  var centerY;
  _HandleView({this.zoneR, this.handleR, this.centerX, this.centerY}) {
    _paint
      ..color = Colors.blue
      ..style = PaintingStyle.fill
      ..isAntiAlias = true;
  }
  double get maxR => zoneR + handleR;
  @override
  void paint(Canvas canvas, Size size) {
    canvas.translate(maxR, maxR);
    _paint.color = _paint.color.withAlpha(100);
    canvas.drawCircle(Offset(0, 0), zoneR, _paint);
    _paint.color = _paint.color.withAlpha(150);
    canvas.drawCircle(Offset(centerX, centerY), handleR, _paint);
  }
  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}
复制代码

二、事件处理

通过GestureDetector进行嵌套,即可让任意组件响应事件

@override
Widget build(BuildContext context) {
  return GestureDetector(
    onPanEnd: (d) => reset(),
    onPanUpdate: (d) => parser(d.localPosition),
    child: Container(
      width: MediaQuery.of(context).size.width,
      height: MediaQuery.of(context).size.height,
      child: CustomPaint(
        painter: _HandleView(
            zoneR: zoneR,
            handleR: handleR,
            centerX: centerX,
            centerY: centerY),
      ),
    ),
  );
}
复制代码

2.处理逻辑

逻辑和上面是一毛一样的,所以,你悟到了什么?

16fc66f76e4792c0?imageslim
reset() {
  centerX = 0;
  centerY = 0;
  setState(() {});
}

parser(Offset offset) {
  centerX = offset.dx - maxR;
  centerY = offset.dy - maxR;
  var rad = atan2(centerX, centerY);
  if (centerX < 0) {
    rad += 2 * pi;
  }
  var thta = rad - pi / 2; //旋转坐标系90度
  if (sqrt(centerX * centerX + centerY * centerY) > zoneR) {
    centerX = zoneR * cos(thta);
    centerY = -zoneR * sin(thta);
  }
  setState(() {});
}
复制代码

3.监听器回调
1
[1] 调用函数对象 Function(double,double) onHandleListener; 
复制代码
class HandleWidget extends StatefulWidget {
  final double zoneR;
  final double handleR;
  final Function(double,double) onHandleListener;

  HandleWidget({this.zoneR = 60.0, this.handleR = 30.0,this.onHandleListener});
  @override
  _HandleWidgetState createState() => _HandleWidgetState();
}
复制代码
parser(Offset offset) {
  //英雄所见...
  var len = sqrt(centerX * centerX + centerY * centerY);
  if(widget.onHandleListener!=null) widget.onHandleListener(rad,len/zoneR);
  setState(() {});
}
复制代码

两种实现的核心是什么?也就那一个解析的过程

如果过程都明白,你管它是Kotlin还是Flutter,就是js也可以在浏览器上画出来
你需要学的从不是使用框架/语言的能力,而是思维分析解决问题的能力
限制你的并非是框架/语言,而是你贫瘠的想象力、控制力、创造力
Flutter 可以用两天就能上手,Kotlin用一天就能了解语法
之后的是你的固有瓶颈,而非框架/语言的问题,知道做什么是最重要的
好了,如果你还在犹豫,你可以去研究[数据结构和算法分析]了,吐了再回来。


Kotlin和Dart的语法都非常简洁。Android本身的View体系比较臃肿,毕竟放在xml里,沟通起来需要费些劲

Flutter组件出来起来非常灵活,复用非常棒。最好的是属性可以很容易修改
Android里自定义View的属性挺麻烦,增加、删除、修改都费劲
Kotlin无可挑剔,除了移动端,Spring运用,还能玩js
Flutter也无可挑剔,UI写起来非常爽,可以处理数据,管理状态,六端同开,前途无量
还在犹豫Flutter移动开发的人,现在Flutter对于桌面的支持,已经远远超过了你的想象。

@张风捷特烈 2020.01.21 未允禁转
我的公众号:编程之王
联系我--邮箱:[email protected] --微信:zdl1994328
~ END ~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK