Android-Kotlin VS Flutter-Dart - 自定義控制搖桿

張風捷特烈發表於2020-01-21
前言

比較Kotlin OR Flutter 誰好, 就像在比較[程式設計師]和[畫家]誰[唱歌]比較好聽
Kotlin是語言,完美平復了我對Java的恨鐵不成鋼的心情。
Flutter是框架,完美實現了我一套程式碼,六端執行的夢想。

Flutter是和Android一個等級的,它們都是執行在裝置上的框架
Kotlin是和Dart一個等級的,它們都是新時代的程式語言  
那誰更香? 別問,問就都香。 

如果你還在Kotlin和Flutter之間猶豫不定  
那我就為你指條路: 去研究[資料結構和演算法分析]
研究到想吐的時候再來選擇,如果還是在Kotlin和Flutter猶豫不定  
那我就為你指條路: 去研究[資料結構和演算法分析]
研究到想吐的時候再來選擇,如果還是在Kotlin和Flutter猶豫不定  
那我就為你指條路: 去研究[資料結構和演算法分析]
研究到想吐的時候再來選擇,如果還是在Kotlin和Flutter猶豫不定  
...
這樣最終你就會成為一個[資料結構和演算法分析]的大師
而這樣的大師擁有同時掌握Kotlin和Flutter的耐力和技術支援
複製程式碼

通過本篇想說明: 不是什麼技術好不好,而是你能幹什麼。
就像作文模板、中文漢字你都認識,卻無法寫出流芳百世的佳作
限制你的並非是語言/框架本身,而是你的思維分析和解決問題的能力

王侯將相寧有種乎? 何必貼上好壞的標籤,非爭個天下第一? 香不就行了嗎!

Kotlin Flutter
Android-Kotlin VS Flutter-Dart - 自定義控制搖桿
Android-Kotlin VS Flutter-Dart - 自定義控制搖桿

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裡看下效果

Android-Kotlin VS Flutter-Dart - 自定義控制搖桿

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(HandleView(this))
    }
}
複製程式碼

二、事件響應

如下圖,在移動時會觸發事件,根據事件來處理小圓的座標
當超越區域時,對其進行限定。放手時回到中心

Android-Kotlin VS Flutter-Dart - 自定義控制搖桿


1.變數介紹

zoneR:背景區域大小,即限定搖桿的區域
handleR:搖桿大小 centerX,centerY搖桿中心偏移

Android-Kotlin VS Flutter-Dart - 自定義控制搖桿

private var zoneR: Float = 150f
private var handleR: Float = 80f
priate var centerX: Float = 0f
private var centerY: Float = 0f
複製程式碼

2.繪製圓

繪製圓時,移動了一下畫布,將畫布左上角和中心重合

Android-Kotlin VS Flutter-Dart - 自定義控制搖桿

[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.事件處理

核心就是處理好小圓的圓心在畫布座標系的位置。
分為在圓內和圓外兩種情況:
觸點在域內,根據觸點位置確定搖桿圓心位置
觸點在域外,搖桿圓心位置在域的邊緣遊走

Android-Kotlin VS Flutter-Dart - 自定義控制搖桿

在parser方法裡,實現通過atan2獲取夾角(圖左)
然後轉化為通常的座標系(圖左),由於再根據畫布座標系校正90°

Android-Kotlin VS Flutter-Dart - 自定義控制搖桿

[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

Android-Kotlin VS Flutter-Dart - 自定義控制搖桿

[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;
}
複製程式碼

3 畫板
[1] ..級聯操作,相當於該物件
複製程式碼

Android-Kotlin VS Flutter-Dart - 自定義控制搖桿

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.處理邏輯

邏輯和上面是一毛一樣的,所以,你悟到了什麼?

Android-Kotlin VS Flutter-Dart - 自定義控制搖桿

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.監聽器回撥

Android-Kotlin VS Flutter-Dart - 自定義控制搖桿

[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 未允禁轉
我的公眾號:程式設計之王
聯絡我--郵箱:1981462002@qq.com --微信:zdl1994328
~ END ~

相關文章