是时候停止使用console.log()啦

因为有更好的选择!

作为一个前端开发者你是不是经常使用console.log()来测试你的代码?这当然没有错,但是你也许不知道在console这个对象中还有很多令人惊奇的方法可以使用。这篇文章中我会介绍下如何有效使用这些非常棒的方法。


为什么使用console对象?

这个console对象是JavaScript提供对浏览器调试控制台进行访问,你可以在调试控制台上打印一些信息,比如你的代码变量值。这个经常作为开发者调试代码的一种手段。我觉得大部分开发者只会使用console.log()这个方法来打印对象,但是它还有好几个别的有用的方法。

1. console.log()

这个方法用来打印数据到调试控制台上,它可以传入任何类型的数据,比如 string,array,object,boolean等等。 样例:

console.log('JavaScript');
console.log(7);
console.log(true);
console.log(null);
console.log(undefined);
console.log([1, 2, 3]);
console.log({a: 1, b: 2, c: 3});

-w417

Read more »

Flutter中StreamBuilder的用法

先大概说说Stream的概念,这个Stream就是类似平常大家用的比较多的RxJava、RxSwift等框架的思路,它是一个异步数据的源,它可以接受任何数据,通过StreamController可以给这个Stream添加数据,如果这个Stream有监听者就会接收到数据。 代码如下:

import 'dart:async';

void main() {
  // 初始化一个Stream controller
  final StreamController ctrl = StreamController();
  // 添加一个监听
  ctrl.stream.listen((data) => print('$data'));
  // 往Stream中添加数据
  ctrl.sink.add('这个是字符串'); //字符串类型
  ctrl.sink.add(1234);//int 类型
  ctrl.sink.add({'name': '姓名', 'content': 'json对象也行'});//对象类型
  ctrl.sink.add(123.45); //double类型
  // StreamController用完后需要释放
  ctrl.close();
}

Read more »

flutter数据监听器

最近用Flutter做功能的时候,发现了这么一个控件ValueListenableBuilder,它也是一个Widget,从它的名字看应该是监听数据,并且用新的数据生成Widget,它的参数就三个:

const ValueListenableBuilder({
    @required this.valueListenable,
    @required this.builder,
    this.child,
  }) 

参数说明

示例

从上面那些说明看来这个控件的作用就是作为局部刷新用的,可能我们整个页面上只是一个小参数变化了,那有了这个Widget就不需要用setState(() {});来刷新整个页面。

做个简单的示例,这个示例是flutter默认生成的项目改写的:

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  final ValueNotifier<int> _notifier = ValueNotifier<int>(0);

  void _incrementCounter() {
    _counter++;
    _notifier.value++;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ValueListenableBuilder(
                valueListenable: _notifier,
                builder: (BuildContext context, int value, Widget child) {
                  return Text(
                    'You have pushed the button this many times:$value',
                  );
                }),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), 
   );
  }
}

20190614-1.2019-06-14 15_36_31

Read more »

flutter 白板工具

平常桌面上都放着一些草稿纸,因为经常要整理思路、画画草图啥的。这不是电子时代嘛,就觉得在手机、pad上也可以这样写写画画,我看了有很多这种类似的app,功能很简单就一个白板,直接就可以画,工具栏大概包括选画笔颜色、选笔尖大小、选橡皮擦等。

于是我用Flutter简单实现了一个,效果看下图,可以选颜色和笔尖大小: 20190606-whiteboard.2019-06-06 09_48_24

主要思路

整体看这个功能实现不难,其实就是一个普通画板,画板我们可以用一个CustomPainter来做,关于CustomPainter我前面有文章有介绍过《Flutter 学习6:绘制动画》

CustomPainter 根据拖动手势,把手指滑动过的坐标画出来,其实就是把点连成线,并且得知道当时的画笔颜色和大小。

那拖动手势当然用GestureDetector啦,拖动的时候用onPanUpdate事件来获取坐标并且把坐标连成线,每次绘画结束就用拖动结束事件来处理onPanEnd

Read more »

android做一个类似钉钉的日历时间选择器

最近做一个日期选择器,仿照钉钉上的,它有三种模式:日期选择、日期间隔、日期+时间。日期是用一个月历选择的,时间是滚动的TimePicker。 大概的样子是这样的:

  1. 日期选择

    1558069562450588.2019-05-17 13_12_38

  2. 日期间隔

1558069567543421.2019-05-17 13_13_27

  1. 日期+时间 1558069562438557.2019-05-17 13_11_08

Read more »

IOS应用切换主题

应用切换主题,或者叫换皮肤,这在现在的应用中还是蛮常见,还有一些比如节日主题的需求也是一样,都需要动态的切换颜色、背景、图片等内容,这种情况其实应用开发开始的时候规划蛮重要的,不然等到应用开发到一定程度再考虑往往工作量就会很大,修改的地方很多。

UIAppearance

开始考虑的时候发现官方有提供一个叫UIAppearance的代理类,说是处理全局统一管理IOS控件的样式的。这个代理方式的思路蛮好的,把官方提供的所有IOS控件都进行处理,可以统一设置各种控件自身的样式。而且代码没有侵入性。 比如:

// 设置主题色
UIApplication.shared.delegate?.window??.tintColor = theme.mainColor
// 设置NavigationBar的样式
UINavigationBar.appearance().barStyle = .black
// 设置TabBar背景
UITabBar.appearance().backgroundImage = UIImage(named: "barBackground")
...

所以有这个代理类,我们只要定义好主题,然后切换的时候设置一下就都变过来了。 如下定义主题:

enum Theme: Int {
    case Red = 0
    case Blue = 1
    case Green = 2
    
    //主题色
    var mainColor: UIColor {
        switch self {
        case .Red:
            return UIColor.red
        case .Blue:
            return UIColor.blue
        case .Green:
            return UIColor.green
        }
    }
    ...
 }

切换主题:

static func applyTheme(theme: Theme) {
        // 保存当前主题
        UserDefaults.standard.set(theme.rawValue, forKey: SelectedThemeKey)
        UserDefaults.standard.synchronize()
        
        // 设置各个组件的主题色
        let sharedApplication = UIApplication.shared
        sharedApplication.delegate?.window??.tintColor = theme.mainColor
        UIApplication.shared.delegate?.window??.tintColor = theme.mainColor
  
        let navBar = UINavigationBar.appearance()
        navBar.barStyle = theme.barStyle
        navBar.tintColor = theme.barTextColor
        navBar.barTintColor = theme.mainColor
      
        let tabBar = UITabBar.appearance()
        tabBar.barStyle = theme.barStyle
        tabBar.barTintColor = theme.tabBarBackgroundColor
        
        UISwitch.appearance().tintColor = theme.mainColor


    }

UIAppearance.2019-04-22 13_15_49

可以看到直接设置window的tintColor是能马上提现的,但是通过UIAppearance设置的就不行,它只能在view放到window之前设置才有效。比如上面我们设置好了主题,切换TabController内部的UIViewController,设置好的NavigationBar的背景颜色就体现出来了。

UIAppearance它其实有一个大统一的思路,它把所有同一类型的UI都进行了统一管理,但是实际业务比这个复杂的多,所以用它来做切换皮肤的功能难度还是很大的。

SwiftTheme

后来在GitHub上找到了一个国人写的换肤框架,SwiftTheme。 先看效果: switch

Read more »

Flutter Widget - PageView的使用

PageView 在Android中也是很常用的一个控件,可以配合TabBar、BottomBar做页面切换。也有很多广告Banner页用PageView来实现。Flutter也有这个Widget,用法更灵活方便。

 PageView({
    Key key,
    this.scrollDirection = Axis.horizontal,
    this.reverse = false,
    PageController controller,
    this.physics,
    this.pageSnapping = true,
    this.onPageChanged,
    ...
  }) 

参数说明

默认构造

这个默认构造,一般用在多个页面切换的场景下,只要放入固定的几个页面就行了。 例子代码:

PageView(
      children: <Widget>[
        Container(
          color: Colors.red,
          child: Center(child: Text('第一页', style: TextStyle(color: Colors.white),),),
        ),
        Container(
          color: Colors.blue,
          child: Center(child: Text('第二页', style: TextStyle(color: Colors.white),),),
        ),
        Container(
          color: Colors.green,
          child: Center(child: Text('第三页', style: TextStyle(color: Colors.white),),),
        ),
        Container(
          color: Colors.blueGrey,
          child: Center(child: Text('第四页', style: TextStyle(color: Colors.white),),),
        )
      ],
    )

1554884412508646.2019-04-10 16_25_39

Read more »

Flutter使用自定义的Icon

Flutter默认开启了uses-material-designScreen Shot 2019-03-29 at 13.02.50 这使得我们在开发中可以轻松使用很多定义好的图标,非常方便,也节约了很多设计或者找图的时间。

但需求无止境嘛,虽然这个material design的Icons库里面已经有很多图标了,有些需求或者场景下想找到一个符合语境的图标还是很难。不过最近发现了一个网站 Flutter Icon ,可以通过上传svg文件生成自定义字体文件,然后就可以使用自定义的Icon了。

Read more »

Android系统的指纹识别API使用

Android的指纹识别系统API是从Android M开始支持的。有专门的FingerprintManager类来提供指纹识别的能力,但是最新的Android P开始这个类被废弃了,取而代之的是BiometricPrompt类,从名字上看是生物识别,说明提供了更多的识别的支持,比如人脸识别、虹膜识别?我随便猜的😄

所以你的应用要使用指纹识别来进行验证的话,如果你的工程compileSdkVersion大于等于Android P就需要写两种调用方式,如果低于Android P,但是大于等于Android M就只需要写一种调用方式就行了。

FingerprintManager

FingerprintManager是从API 23开始的所以使用的时候必须判断系统版本:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    val fingerprintManager = context.getSystemService(FingerprintManager::class.java)
}

Read more »

指纹识别验证和人脸识别验证-IOS

本文代码是基于Swift4.2。

TouchID和FaceID的验证,在IOS中调用本身比较简单。因为现在要么是FaceID,要么是TouchID,再老一点的就是啥都没有。这种机器应该没人用了吧?不过还有是IOS系统版本的支持。TouchID是从IOS8.0开始的,FaceID是从IOS11.0开始的。

引入模块,初始化环境

要使用验证模块先要引入:

import LocalAuthentication

然后初始化对象:

let context = LAContext.init()

Read more »