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 »

自签名SSL证书

现在大家对互联网安全越来越重视了,Chrome、Safari等浏览器更是直接把http请求的网站标记为不安全网站,还有IOS应用直接就关闭了http的请求,只支持https。所以现在越来越多的网站、服务都开始使用https进行加密传输。

HTTPS

https其实就是http基础上加了SSL协议,通过SSL数字证书来进行服务器身份认证和通信的加密。所以我们的服务或者网站要升级到https就需要有SSL数字证书。数字证书中的公钥能够和服务器上的私钥配对使用,实现数据传输过程中的加密和解密。

CA

这个数字证书其实大家都可以自己生成的,既然这样,我们访问某个网站咋知道这个网站是别人伪造的还是真的?这时候“权威”机构就出来了,就是常说的CA(数字证书签发机构)专门签发证书的第三方机构,你作为网站主到这个第三方机构去申请证书,CA确认你的申请信息无误后就会给你颁发证书,这个证书浏览器接收到后因为是“权威”结构颁发的所以会认为是安全的、可信任的。

自签名证书

CA证书需要“权威”机构颁发,既然是“权威”的,价格就不菲,对很多人来说是笔很大的开销。但是为了数据传输的安全性,我们内部使用的服务还是可以用自签名证书来开启https协议的。就是前面说的自己通过一些工具生成的数字证书,来进行数据传输的加密解密。

Read more »

Swift使用URLSession进行文件下载

总体的内容分两部分:

URLSession的使用

首先是创建一个请求:

 //创建请求
var urlRequest = URLRequest(url: URL(string: url)!)
urlRequest.httpMethod = "GET"//下载图片用GET请求

然后创建URLSession,session创建需要传入3个参数。

// session会话
session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)

最后开始启动下载任务:

// 创建下载任务
let downloadTask = session?.dataTask(with: urlRequest)
// 开始下载
downloadTask?.resume()

Read more »

Flutter 学习10:NestedScrollView、SliverAppBar、TabBar

  1. Flutter 学习1:开发环境、开发工具、初始化一个项目
  2. Flutter 学习2:从main.dart文件说起
  3. Flutter 学习3:转场、导航
  4. Flutter 学习4:集成到原有的项目中
  5. Flutter 学习5:开发Dart包和插件包
  6. Flutter 学习6:绘制动画
  7. Flutter 学习7:Dart语言基础
  8. Flutter 学习8:BottomSheet
  9. Flutter 学习9:Positioned、Transform等控件使用以及手势控制
  10. Flutter 学习10:NestedScrollView、SliverAppBar、TabBar

前面说过Flutter提供了太多各种各样的Widget,给开发者带来了非常棒的开发体验,很多功能和效果只要拿官方提供的Widget来用就行了。 今天说的这几个Widget如果是Android开发者应该是比较熟悉了,在Android开发中应该会经常用到。

NestedScrollView、SliverAppBar

这两个Widget要放一起说,因为NestedScrollView其实跟Android中的一样,它是一个ScrollView,但是在Android中它可以和Toolbar配合进行交互,可以做出很多效果,比如折叠Toolbar、页面滚动的时候隐藏Toolbar等效果。在Flutter里面也是一样,不过这里用SliverAppBar来代替Toolbar的位置。

Read more »

Flutter 学习9:Positioned、Transform等控件使用以及手势控制

  1. Flutter 学习1:开发环境、开发工具、初始化一个项目
  2. Flutter 学习2:从main.dart文件说起
  3. Flutter 学习3:转场、导航
  4. Flutter 学习4:集成到原有的项目中
  5. Flutter 学习5:开发Dart包和插件包
  6. Flutter 学习6:绘制动画
  7. Flutter 学习7:Dart语言基础
  8. Flutter 学习8:BottomSheet
  9. Flutter 学习9:Positioned、Transform等控件使用以及手势控制
  10. Flutter 学习10:NestedScrollView、SliverAppBar、TabBar

上次说了一个BottomSheet的控件,非常实用的一个弹出控件。Flutter提供了很多有意思的控件,好好利用这些控件就能搭建出任何你想要的效果。

Positioned

看这个名字就大概能了解到这个是定位用的,但是它不是随便哪里都能定位的,它必须配合Stack控件用。它通过 [left], [right], [width],[top], [bottom], [height] 这六个属性将Stack的某个子控件定位到特定的位置上。

    Stack(
        children: <Widget>[
          Positioned(
            top: 150,
            left: 30,
            child: Container(
              color: Colors.red,
              width: 200,
              height: 200,
            ),
          ),
          Positioned(
            left: 40,
            top: 200,
            width: 50,
            height: 50,
            child: Container(
              color: Colors.teal,
            ),
          )
        ],
      )

Screen Shot 2019-01-31 at 14.14.28-w417

Transform

Transform已经有写好的三种用法分别是scale、rotate、translate 。用起来非常方便,一个个来看看怎么用

Transform.scale

它就一个参数scale。在上面代码的基础上,我们把那个红色方块用Transform.scale包起来就可以放大缩小了。

var myScale = 1.0;
void _scale(bool plus) {
    if(plus) {
      myScale += 0.1;
    }else {
      myScale -= 0.1;
    }
    setState(() {});
  }
  ...
    Stack(
        children: <Widget>[
          Positioned(
            top: 150,
            left: 30,
            child: Transform.scale(
              scale: myScale,
              child: Container(
              color: Colors.red,
              width: 200,
              height: 200,
            ),
            ),
          ),
          Positioned(
            left: 40,
            top: 200,
            width: 50,
            height: 50,
            child: Container(
              color: Colors.teal,
            ),
          ),
          Positioned(
            bottom: 20,
            right: 40,
            child: Container(
              width: 200,
              height: 64,
              child: Row(
                children: <Widget>[
                  FlatButton(
                    child: Icon(Icons.add_circle),
                    onPressed: () => _scale(true),
                  ),
                  FlatButton(
                    onPressed:  () => _scale(false),
                    child: Icon(Icons.remove_circle),
                  )
                ],
              ),
            ),
          )
        ],
      )

Transform.2019-01-31 15_16_14

Transform.rotate

它是用来旋转一定的角度用的,也只有一个参数angle。

    Positioned(
            left: 40,
            top: 200,
            width: 50,
            height: 50,
            child: Transform.rotate(
              angle: math.pi / 2, //90度
              child: Container(
                color: Colors.teal,
                child: Center(child: Text('中国', style: TextStyle(color: Colors.white),)),
              ),
            ),
          )

Screen Shot 2019-01-31 at 15.38.26-w417

Transform.translate

最后一个是变化位置,有点类似Positioned的,就是x轴y轴的移动。

    Positioned(
            left: 40,
            top: 200,
            width: 50,
            height: 50,
            child: Transform.translate(
              offset: Offset(130, 0),//x轴方向移动130
              child: Container(
                color: Colors.teal,
                child: Center(child: Text('中国', style: TextStyle(color: Colors.white),)),
              ),
            ),
          )

Screen Shot 2019-01-31 at 15.51.17-w417

手势控制

做移动开发的看到这里其实很容易就能想到我们经常用到的图片查看器,图片可以用双指放大缩小,单指移动位置。 那用上面两个控件结合手势控制的GestureDetector是否就可以实现图片查看器的功能了呢,试试!

网上随便找了一个小姐姐的图片,我随便找你们随便看!放大缩小移动的效果如下:

ScreenRecording_01-31-2019 17-30-23.2019-01-31 17_33_36

下面是实现的代码:

class _MyHomePageState extends State<MyHomePage> {
  var myScale = 1.0;
  var myPosition = Offset(0.0, 0.0);
  var mySize = Size(0.0, 0.0);
  var lastPosition = Offset(0.0, 0.0);
  var startMovePosition = Offset(0.0, 0.0);
  var edgePosition = Offset(0.0, 0.0);
  int numPointers = 0;
  var lastScale = 1.0; // 多次放大缩小的时候保存上一次的结果。
  var scaling = false;

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    mySize = size * myScale;
    final dx = -(mySize.width - size.width);
    final dy = -(mySize.height - size.height);
    edgePosition = Offset(dx, dy);
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Listener(
            onPointerDown: (_) => numPointers++,
            onPointerUp: (_) => numPointers--,
            child: GestureDetector(
              child: Stack(
                children: <Widget>[
                  Positioned(
                    top: myPosition.dy,
                    left: myPosition.dx,
                    width: mySize.width,
                    height: mySize.height,
                    child: Transform.scale(
                      scale: myScale,
                      child: Image.network(
                        'http://img.muliba.net/post/timg.jpeg',
                        fit: BoxFit.cover,
                      ),
                    ),
                  ),
                ],
              ),
              onScaleStart: _scaleStart,
              onScaleEnd: _scaleEnd,
              onScaleUpdate: _scaleUpdate,
            )));
  }

  void _scaleStart(ScaleStartDetails detail) {
    if (numPointers == 1 && !scaling) {
    lastPosition = myPosition; //标记下位置
    startMovePosition = detail.focalPoint;
    }
    if (numPointers == 2) {
      if (!scaling) {
        scaling = true;
      }
    }
  }

  void _scaleUpdate(ScaleUpdateDetails detail) {
    if (numPointers == 1 && !scaling) {
    final distance = detail.focalPoint - startMovePosition; // 移动距离
    final newPosition = lastPosition + distance; //移动
    var x = newPosition.dx;
    if (x > 0) {
      x = 0.0;
    }
    if (x < edgePosition.dx) {
      x = edgePosition.dx;
    }
    var y = newPosition.dy;
    if (y > 0) {
      y = 0.0;
    } else if (y < edgePosition.dy) {
      y = edgePosition.dy;
    }
    myPosition = Offset(x, y); //移动
    setState(() {});
    }
    if (numPointers == 2) {
      if (scaling) {
        var newScale = lastScale * detail.scale;
        if (newScale < 1.0) {
          newScale = 1.0;
        } else if (newScale > 5.0) {
          newScale = 5.0;
        }
        myScale = newScale;
        // 计算大小
        setState(() {});
      }
    }
  }

  void _scaleEnd(ScaleEndDetails detail) {
    if (scaling) {
      lastScale = myScale;
      scaling = false;
    }
  }
}

是不是很方便实用,Flutter确实让开发越来越简单了!👍

Read more »