精华内容
下载资源
问答
  • 主要是java8中新的Date和Time,探讨新Date类和Time类背后的设计原则,有所需要的小伙伴希望帮助到你
  • 有的用不上,虽然说它们的出现是更促进页面更有语义,更好的 seo,但其实当页面禁用样式后,它们的展现跟 div 是没撒差别,有同学可能会说,既然一样,又不影响页面的最终展现,不管是 article 还是 section 用就...
  • 无线传感器网络中原始的最小暴露路径问题没有考虑对路径的实际限制条件,提出一种要求经过某一...通过在多种情况下的仿真实验发现,设计的带约束条件优化模型和混合人工蜂群求解算法有效解决提出的最小暴露路径问题。
  • 在对WDM波长路由光网络的路由和波长分配算法进行研究的基础上,提出了一种新的自适应动态路由...该算法有效地利用网络资源,保证业务分布的均衡,较好地兼顾网络资源分配的合理性,有效地改善了全网的平均阻塞性能。
  • [Unity3d]unity中新老动画系统

    千次阅读 2014-02-27 13:10:18
     如果说你需要在程序中判断当前动画的一些信息,可以使用 GetCurrentAnimatorStateInfo(0),我查了一下Animator不直接拿到当前播放动画的名称, 只能拿到它对应的Has值,也就是说你需要将原始的动画名称转换成...

    Unity4的Mecanim动画很早以前就有体验过,迟迟没有加到项目中有两个原因,今天写这篇博客来记录我在做的过程中遇到的一些问题。

    1.以前的代码代码量比较多,修改起来动的地方太多了。

    2.使用Mecanim动画,还得需要美术的动画做配合才行。

    在3.x中播放动画的时候使用Play()或CrossFade(),直接播放动画 或淡入淡出播放动画。

    1 animation.Play("name");
    2 animation.CrossFade("name");

    也可以使用队列播放,让动画形成一个队列。

    1 animation.Play("name");

    2 animation.CrossFade("name");

    我举一个我现在项目的例子。主角攻击敌人是一套连招,连招一共分为4套动画。也就是当玩家连续按下4次攻击键时这四套动画是连续播放的,假如玩家只连续按下2次攻击,可能只会播放前两套动画。代码中你需要判断其中某个动画是否播放完毕,只有播放完毕才能继续播放下一个动画。

    1 if(animation.isPlaying)

    2 {
    3
    4 }
    5
    6 if(animation.IsPlaying("attack1"))
    7 {
    8
    9 }

    大家在仔细想想这个命题,我们可以把动画分成4中可能的队列,也只可能分为这几种队列。

    站立动画- 》攻击动画0 -》站立动画

    站立动画- 》攻击动画0 -》攻击动画1 -》站立动画

    站立动画- 》攻击动画0 -》攻击动画1 -》攻击动画2-》站立动画

    站立动画- 》攻击动画0 -》攻击动画1 -》攻击动画2-》攻击动画3-》站立动画

    此时如果用unity3以前的动画方式,无非就是上面这几种方法加上一些逻辑判断完成。现在Unity4加入了Mecanim动画,可以很好的帮我们解决这个问题。详细的动画使用教程我就不多说了,网上已经有很多人写过了。

    如下图所示,以前我们在使用模型的时候。一个原始模型,原始模型中没有动画。然后是动画模型,每一个动画都会依赖原始模型。动画的名称末尾用 名称 + @name来表示。 这样的做法使用起来非常方便,但是由于每一个动画都会依赖原始模型所以文件会非常大。

    屏幕快照 2013-04-16 下午2.14.57

    Unity4已经将默认模型与动态导入的类型做了修改,你会发现你的模型拖拽入Hierarchy视图中没有Animation组件而是Animator组件。如果你还是想在Unity4中使用以前的动画系统。你需要把每个模型和动画的类型改成 Rig-> Animation Type -> Legacy,如下图所示。

    屏幕快照 2013-04-16 下午2.19.04

    手动的改起来会非常的累。建议你将下面这条脚本放在项目Editor文件夹下(没有创建一个)。这样当你将模型或动画拖入Project视图中,程序会自动帮你修改它的类型,显然Unity已经不建议大家继续使用以前的动画系统了。

    01 using UnityEditor;
    02
    03 public class MyEditor : AssetPostprocessor
    04 {
    05 public void OnPreprocessModel()
    06 {
    07 ModelImporter modelImporter = (ModelImporter) assetImporter;
    08 modelImporter.animationType = ModelImporterAnimationType.Legacy;
    09 }
    10 }

    下面开始说说新的动画。在Animations选项卡中先勾掉Import Animation 点击Apply。如下图所示,在Rig选项卡中修改Animation Type的类型为Humanoid。 如果你希望现在选择的这个模型做为标准模型的话,在Avatar Definition中选择Create From This Model。点击下方的Configure可以预览你的骨骼。

    屏幕快照 2013-04-16 下午2.13.32

    让美术修改一下以前的动画,将动画中的原始模型去掉,这样还可以减少文件的大小。然后在Porject视图中找一个原始模型拖拽入右侧Preview中,可以看到这个模型已经播放奔跑动画。

    屏幕快照 2013-04-16 下午2.33.35

    此时换一个模型拖入同样可以预览奔跑效果。

    屏幕快照 2013-04-16 下午2.36.51

    如下图所示,在动画的.fbx中 因为动画需要用刚刚生成的骨骼。所以这里Avatar Definition中你需要选择Copy From Other Avatar 。在Source中选择刚刚生成的Avatar 以后所有动画都需要这样来设置。。

    屏幕快照 2013-04-16 下午4.38.57

    下面我们来让这个女模型和男模型共用男模型的那一套动画,在游戏视图中播放。在Project视图中选择Crate->AnimatorController。然后把Project中男模型和女模型都拖拽入Hierarchy视图中。 将刚刚创建的AnimatorController放置在Controller处。

    屏幕快照 2013-04-16 下午2.41.31

    此时在Unity导航菜单栏中选择Window -》 Animator。 将动画文件拖入Animator窗口中,你会发现两个模型都开始发生运动。如下图所示,黄颜色表示它为原始动画,也就是根动画。用箭头将它们一一前后相连,箭头实际上就是动画播放的条件。请注意看图中两个蓝色的箭头,A播放完后将会播放B动画,可是B却对应了两个箭头,也就是说B播放完后可以播放C也可以回过头来播放A。

    屏幕快照 2013-04-16 下午3.00.59

    那么B播放完到底是播放C还是播放A呢?用鼠标点击一下箭头,看看这这两个箭头的条件吧。分别点开BA 和BC的两个箭头,在右侧监测面板视图中你都会发现Conditions下有一个Exit Time的条件。根据动画的不同对应数值也会不同,我的数值是0.94。也就是当B动画播放0.94s后将播放下一个动画。默认BA和BC的动画时间是一样的,Unity会有限选择下一个动画,也就是A -》 B-》-》C-》D-》A这样循环播放下去。假设我现在需要动画是 A-》B-》A这样循环播放,只需要修改一下BA箭头的条件,将Exit Time改小一点只要比BC箭头上的小就可以。。 其它的播放虚列原理类似。

    屏幕快照 2013-04-16 下午3.09.50

    接着还有问题了,用时间来做动画切换的条件是不是有点太限制了。Animator还支持自定义条件,在Animator窗口的左下角处,点击“+”按钮就可以添加变量。这里我添加三组变量, float 、int、bool。

    屏幕快照 2013-04-16 下午3.17.50

    变量添加完毕后,继续点击箭头的条件,箭头上可以有一个条件 或者多个条件。如果是多个条件需要多个条件同时满足才可以。 Conditons左键是变量名称,中间是变量条件,右边是变量值。

    Greater 表示左边变量大于右边时触发

    Less 表示左边变量小于右边时触发

    Equals 表示左边变量等于右边时触发

    NotEquals表示左边变量不等于右边时触发。

    int 变量上述四种都有,float变量只有Greater 和Less, bool变量只有true和false。

    屏幕快照 2013-04-16 下午3.20.38

    此时我们在加深一下理解。选择AB的箭头,也就设置A动画切换B动画的条件。

    ft     Greater      5 表示 当ft的值大于5的时候触发。

    it      Less           3 表示 当 it的值小于3的时候触发。

    ib     true            表示 当ib的值等于true的时候触发。

    只有上述三种条件全部达成时将A动画将切换播放B动画。否则将一直停留在播放A动画处。

    屏幕快照 2013-04-16 下午3.26.38

    那么ft it ib的这三个变量到底在那里设置呢?如下图所示,才记得前面我们创建的三个变量吗? 这三个变量对应的值就是右边的 0.0 0 false 。在编辑器中你可以通过修改这三个数值来满足播放动画的条件。可是在代码中怎么办呢?

    屏幕快照 2013-04-16 下午3.17.50

    在代码中你可以这样来设置或变更它们的条件。 如果说你需要在程序中判断当前动画的一些信息,可以使用 GetCurrentAnimatorStateInfo(0),我查了一下Animator不能直接拿到当前播放动画的名称, 只能拿到它对应的Has值,也就是说你需要将原始的动画名称转换成Hash来判断。

    01 using UnityEngine;
    02 using System.Collections;
    03
    04 public class NewBehaviourScript : MonoBehaviour {
    05
    06 private Animator animator ;
    07 void Start()
    08 {
    09 //得到Animator对象
    10 animator = GetComponent<Animator>();
    11 }
    12
    13 void OnGUI()
    14 {
    15 if(GUILayout.Button("play",GUILayout.Width(50)))
    16 {
    17 //在这里设置变量的条件
    18 animator.SetFloat("ft",6f);
    19 animator.SetInteger("it",2);
    20 animator.SetBool("ib",true);
    21
    22 }
    23 }
    24
    25 void Update()
    26 {
    27 AnimatorStateInfo animatorState = animator.GetCurrentAnimatorStateInfo(0);
    28
    29 if(animatorState.IsName("Base Layer.Run_FastStop_Idle"))
    30 {
    31 Debug.Log("动画相等");
    32 }else
    33 {
    34 Debug.Log("动画不等");
    35 }
    36
    37 }
    38 }

    另外Mecanim还支持多个动画的混合。目前Mecanim还有一个最大的难题,也是文章最上面我说的需要美术配合的那部分。之前我们看到的动画都是应用于人型模型,也就是说它支持人形的骨骼, 举个例子我们的项目人和武器是两个骨骼,这样在用Mecanim就悲剧了。因为不同模型武器的骨骼不一样所以公用模型的话会出现武器位置不对的情况。最后我想到的办法就是美术将以前做的武器骨骼重新导出,每个人对应一套自己武器骨骼(或者一些特殊的骨骼)最后生成武器的动画 ,比如 站立动画、攻击动画、死亡动画等。当Mecanim播放动画的时候,同时在播放该模型对应的武器动画,我想这样就可以解决这个问题吧。

    展开全文
  • mysql中可以使用“ALTER TABLE”语句来增加表中新的列,语法格式“ALTER TABLE 表名 ADD 新字段名 数据类型 [约束条件];”;默认是在表的末尾添加新的列,如果配合使用FIRST关键字则可在开头添加新的列。mysql增加表...

    mysql中可以使用“ALTER TABLE”语句来增加表中新的列,语法格式“ALTER TABLE 表名 ADD 新字段名 数据类型 [约束条件];”;默认是在表的末尾添加新的列,如果配合使用FIRST关键字则可在开头添加新的列。

    2f55c25aa6d287e1377266ec874e51f2.png

    mysql增加表中新列的方法:

    1、在末尾添加新列(字段)

    MySQL 默认在表的最后位置添加新字段。

    一个完整的字段包括字段名、数据类型和约束条件。

    MySQL 在末尾添加字段的语法格式如下:ALTER TABLE 表名 ADD 新字段名 数据类型 [约束条件];

    对语法格式的说明如下:表名:为数据表的名字;

    新字段名:为所要添加的字段的名字;

    数据类型:为所要添加的字段能存储数据的数据类型;

    [约束条件] :是可选的,用来对添加的字段进行约束。

    这种语法格式默认在表的最后位置(最后一列的后面)添加新字段。

    示例:

    使用 DESC 查看一个 student 表的结构mysql> DESC student;

    +-------+-------------+------+-----+---------+-------+

    | Field | Type | Null | Key | Default | Extra |

    +-------+-------------+------+-----+---------+-------+

    | name | varchar(20) | YES | | NULL | |

    | sex | char(1) | YES | | NULL | |

    +-------+-------------+------+-----+---------+-------+

    2 rows in set (0.01 sec)

    在表的末尾添加一个 INT 类型的字段 agemysql> ALTER TABLE student ADD age INT(4);

    Query OK, 0 rows affected (0.16 sec)

    Records: 0 Duplicates: 0 Warnings: 0

    mysql> DESC student;

    +-------+-------------+------+-----+---------+-------+

    | Field | Type | Null | Key | Default | Extra |

    +-------+-------------+------+-----+---------+-------+

    | name | varchar(20) | YES | | NULL | |

    | sex | char(1) | YES | | NULL | |

    | age | int(4) | YES | | NULL | |

    +-------+-------------+------+-----+---------+-------+

    3 rows in set (0.00 sec)

    在开头添加新列(字段)

    如果希望在开头位置(第一列的前面)添加新字段,那么可以使用 FIRST 关键字,语法格式如下:ALTER TABLE 表名 ADD 新字段名 数据类型 [约束条件] FIRST;注:FIRST 关键字一般放在语句的末尾。

    示例:

    在表的第一列添加 INT 类型的字段 idmysql> ALTER TABLE student ADD id INT(4) FIRST;

    Query OK, 0 rows affected (0.14 sec)

    Records: 0 Duplicates: 0 Warnings: 0

    mysql> DESC student;

    +-------+-------------+------+-----+---------+-------+

    | Field | Type | Null | Key | Default | Extra |

    +-------+-------------+------+-----+---------+-------+

    | id | int(4) | YES | | NULL | |

    | name | varchar(20) | YES | | NULL | |

    | sex | char(1) | YES | | NULL | |

    | age | int(4) | YES | | NULL | |

    +-------+-------------+------+-----+---------+-------+

    4 rows in set (0.00 sec)

    在中间位置添加新列(字段)

    MySQL 除了允许在表的开头位置和末尾位置添加字段外,还允许在中间位置(指定的字段之后)添加字段,此时需要使用 AFTER 关键字,语法格式如下:ALTER TABLE 表名 ADD 新字段名 数据类型 [约束条件] AFTER 已经存在的字段名;

    AFTER 的作用是将新字段添加到某个已有字段后面。注意,只能在某个已有字段的后面添加新字段,不能在它的前面添加新字段

    更多编程相关知识,请访问:编程入门!!

    展开全文
  • 介绍了利用2个新的积分不等式求解同一积分的近似值,详细阐述了该近似...数值的计算表明,该方法可获得很高的精确度,并确定误差大小,通过将积分区间细分,可进一步提高估算值的精确度,同时也计算机编程提供了数学模型。
  • 研究了在区分服务体系结构中传统的标记算法,传统的标记算法不提供标记的准确性和按比例共享带宽的公平性。在此基础上,提出了一种改进的标记算法。该算法在进行数据包的标记时,同时考虑平均速率和瞬时速率两个...
  • 我们发现(i)通过LHCb协作在Λbπ+π-光谱中新观察到的Λb(6146/6152)共振可以用夸克模型中的λ模式Λb(1D)状态来解释。 应该强调的是,在未来的实验中,应通过更多的观察进一步阐明Λbπ+π-光谱中的结构是...
  • 使用Cloudera Manager部署安装的CDH和Impala,Hive中新增加的表,impala中查询不到,其原因是/etc/impala/conf下面没有hadoop和hive相关的配置文件: 将hadooop-core.xml,hdfs-site.xml,hive-site.xml拷贝到conf下...
         使用Cloudera Manager部署安装的CDH和Impala,Hive中新增加的表,impala中查询不到,其原因是/etc/impala/conf下面没有hadoop和hive相关的配置文件:

    将hadooop-core.xml,hdfs-site.xml,hive-site.xml拷贝到conf下,重启服务即可。
     
    展开全文
  • 硅藻是生物硅的主要来源,分离测定沉积物中硅藻来源的生物硅,更直接地将沉积物中生物硅含量与表层初级生产力联系起来.本研究通过不同种类和浓度的碱性提取液对硅藻、纯SiO2、硅藻土、海绵骨针进行Si的连续提取...
  • 例如,如果应用程序在Web上运行,你就不从/details/:id这样的路由中解析ID。 使用onGenerateRoute的高级命名路由 处理命名路由更灵活的方法是使用onGenerateRoute。这个API让你有能力处理所有的路径,这下面是完整...

    原文:Learning Flutter’s new navigation and routing system
    作者:John Ryan

    本文解释了Flutter新的NavigatorRouter API是如何工作。如果你关注Flutter的开放设计文档,你可能已经看到这些新功能被称为Navigator 2.0和Router。我们将探讨这些API如何实现对应用程序中的屏幕进行更精细的控制,以及如何使用它来解析路由。

    这些新的API并不是破坏性的更改,它只是增加了一个新的声明式API。在Navigator 2.0之前,很难push或pop多个页面,或者删除当前页面下面一个页面。如果你对Navigator目前的工作方式很满意,你可以继续以同样的方式(命令式的)使用它。

    Router提供了处理来自底层平台的路由并显示相应页面的能力。在本文中,Router被配置为解析浏览器URL以显示相应的页面。

    本文将帮助您选择哪种 Navigator 模式最适合您的应用程序,并解释了如何使用 Navigator 2.0 来解析浏览器 URL,并完全控制活动页面的堆栈。本文的例子展示了如何构建一个应用程序,以处理来自平台的传入路由并管理应用程序的页面。下面的GIF展示了这个示例程序的操作:

    示例程序的操作

    Navigator 1.0

    如果你正在使用Flutter,那么您可能正在使用Navigator并熟悉以下概念:

    • Navigator - 管理路由对象堆栈的小部件。
    • Route - 由Navigator管理的对象,表示屏幕,通常由MaterialPagerRoute这样的类实现。

    在Navigator 2.0之前,Route通过命名路由或匿名路由push和pop到Navigator的堆栈中。接下来的部分将简要回顾这两种方法。

    匿名路由

    大多数移动应用的页面都是彼此叠放在一起,就像堆栈一样。在Flutter中,使用Navigator很容易做到这一点。

    MaterialAppCupertinoApp已经使用Navigator。您可以使用Navigator.of()访问它,也可以使用Navigator.push()显示一个新页面,并使用Navigator.pop()返回上一个页面。

    mport 'package:flutter/material.dart';
    
    void main() {
      runApp(Nav2App());
    }
    
    class Nav2App extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: HomeScreen(),
        );
      }
    }
    
    class HomeScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: FlatButton(
              child: Text('View Details'),
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) {
                    return DetailScreen();
                  }),
                );
              },
            ),
          ),
        );
      }
    }
    
    class DetailScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: FlatButton(
              child: Text('Pop!'),
              onPressed: () {
                Navigator.pop(context);
              },
            ),
          ),
        );
      }
    }
    

    当调用push()时,DetailScreen被放置在HomeScreen的顶部,就像这样。
    在这里插入图片描述

    上一个页面(HomeScreen)仍然是Widget树的一部分,所以当DetailScreen可见时,与它相关的任何State对象都会保持不变。

    命名路由

    Flutter也支持命名路由,它在MaterialAppCupertinoApp中的routes参数定义。

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(Nav2App());
    }
    
    class Nav2App extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          routes: {
            '/': (context) => HomeScreen(),
            '/details': (context) => DetailScreen(),
          },
        );
      }
    }
    
    class HomeScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: FlatButton(
              child: Text('View Details'),
              onPressed: () {
                Navigator.pushNamed(
                  context,
                  '/details',
                );
              },
            ),
          ),
        );
      }
    }
    
    class DetailScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: FlatButton(
              child: Text('Pop!'),
              onPressed: () {
                Navigator.pop(context);
              },
            ),
          ),
        );
      }
    }
    

    这些路由必须是预先定义的。虽然您可以向命名路由传递参数,但不能从路由本身解析参数。例如,如果应用程序在Web上运行,你就不能从/details/:id这样的路由中解析ID。

    使用onGenerateRoute的高级命名路由

    处理命名路由更灵活的方法是使用onGenerateRoute。这个API让你有能力处理所有的路径,这下面是完整的示例:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(Nav2App());
    }
    
    class Nav2App extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          onGenerateRoute: (settings) {
            // Handle '/'
            if (settings.name == '/') {
              return MaterialPageRoute(builder: (context) => HomeScreen());
            }
    
            // Handle '/details/:id'
            var uri = Uri.parse(settings.name);
            if (uri.pathSegments.length == 2 &&
                uri.pathSegments.first == 'details') {
              var id = uri.pathSegments[1];
              return MaterialPageRoute(builder: (context) => DetailScreen(id: id));
            }
    
            return MaterialPageRoute(builder: (context) => UnknownScreen());
          },
        );
      }
    }
    
    class HomeScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: FlatButton(
              child: Text('View Details'),
              onPressed: () {
                Navigator.pushNamed(
                  context,
                  '/details/1',
                );
              },
            ),
          ),
        );
      }
    }
    
    class DetailScreen extends StatelessWidget {
      String id;
    
      DetailScreen({
        this.id,
      });
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text('Viewing details for item $id'),
                FlatButton(
                  child: Text('Pop!'),
                  onPressed: () {
                    Navigator.pop(context);
                  },
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class UnknownScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: Text('404!'),
          ),
        );
      }
    }
    

    这里的settingsRouteSettings的一个实例。name和arguments字段是调用Navigator.pushNamed时提供的值,或者是initialRoute设置的值。

    Navigator 2.0

    Navigator 2.0 API为框架增加了新的类,以便使应用的屏幕成为应用状态的函数,并从底层提供解析路由的能力(如Web URL)。下面是对新内容的概述:

    • Page — 一个不可更改的对象,用于设置Navigator的历史堆栈。
    • Router — 配置要由Navigator显示的页面列表。通常此页面列表根据平台或应用的状态变化而变化。
    • RouteInformationParser,它从RouteInformationProvider中获取RouteInformation,并将其解析为用户定义的数据类型。
    • RouterDelegate — 定义了Router如何学习应用状态变化以及如何响应这些变化的应用特定行为。它的工作是监听RouteInformationParser和应用状态,并利用当前的Pages列表构建Navigator
    • BackButtonDispatcher — 向Router报告返回按钮按下的情况。

    下图显示了RouterDelegate如何与RouterRouteInformationParser和应用的状态进行交互。

    在这里插入图片描述

    下面是这些部件如何交互的一个例子:

    1. 当平台发出一个新的路由(例如,“books/2”)时,RouteInformationParser将其转换为你在应用中定义的抽象数据类型T(例如名为BooksRoutePath的类)。
    2. RouterDelegatesetNewRoutePath方法是用这个数据类型调用的,必须更新应用程序状态以反映变化(例如,通过设置selectedBookId),并调用notifyListeners
    3. notifyListeners被调用时,它告诉Router重建RouterDelegate(使用其build()方法)。
    4. RouterDelegate.build()返回一个新的Navigator,其页面现在反映了应用状态的变化(例如selectedBookId)。

    Navigator 2.0 练习

    本节将带领你完成一个使用Navigator 2.0 API的练习。我们最终会得到一个可以与URL栏保持同步的应用,并处理来自应用和浏览器的返回按钮操作,如下图所示。

    示例程序的操作

    切换到master渠道创建一个支持web的Flutter项目,并将lib/main.dart的内容替换为以下内容:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(BooksApp());
    }
    
    class Book {
      final String title;
      final String author;
    
      Book(this.title, this.author);
    }
    
    class BooksApp extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => _BooksAppState();
    }
    
    class _BooksAppState extends State<BooksApp> {
      void initState() {
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Books App',
          home: Navigator(
            pages: [
              MaterialPage(
                key: ValueKey('BooksListPage'),
                child: Scaffold(),
              )
            ],
            onPopPage: (route, result) => route.didPop(result),
          ),
        );
      }
    }
    

    Pages

    Navigator的构造函数中有一个pages参数。如果列表中的Page发生变化,Navigator就会更新堆栈的路由来匹配。为了了解其工作原理,我们将构建一个显示书籍列表的应用程序。

    _BooksAppState中,保留两种状态:书籍列表和所选书籍。

    class _BooksAppState extends State<BooksApp> {
      // New:
      Book _selectedBook;
      bool show404 = false;
      List<Book> books = [
        Book('Stranger in a Strange Land', 'Robert A. Heinlein'),
        Book('Foundation', 'Isaac Asimov'),
        Book('Fahrenheit 451', 'Ray Bradbury'),
      ];
      
      // ...
    

    然后在_BooksAppState中,返回一个带有Page对象列表的Navigator

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Books App',
        home: Navigator(
          pages: [
            MaterialPage(
              key: ValueKey('BooksListPage'),
              child: BooksListScreen(
                books: books,
                onTapped: _handleBookTapped,
              ),
            ),
          ],
        ),
      );
    }
    void _handleBookTapped(Book book) {
        setState(() {
          _selectedBook = book;
        });
      }
    // ...
    class BooksListScreen extends StatelessWidget {
      final List<Book> books;
      final ValueChanged<Book> onTapped;
      BooksListScreen({
        @required this.books,
        @required this.onTapped,
      });
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: ListView(
            children: [
              for (var book in books)
                ListTile(
                  title: Text(book.title),
                  subtitle: Text(book.author),
                  onTap: () => onTapped(book),
                )
            ],
          ),
        );
      }
    }
    

    由于这个应用有两个页面,一个是书的列表页面,一个是显示详情的页面。如果选择了一本书(使用collection if),就增加第二个(详情)页面。

    pages: [
      MaterialPage(
        key: ValueKey('BooksListPage'),
        child: BooksListScreen(
          books: books,
          onTapped: _handleBookTapped,
        ),
      ),
      // New:
      if (show404)
        MaterialPage(key: ValueKey('UnknownPage'), child: UnknownScreen())
      else if (_selectedBook != null)
        MaterialPage(
            key: ValueKey(_selectedBook),
            child: BookDetailsScreen(book: _selectedBook))
    ],
    

    注意,页面的key是由Book对象的值定义的。这告诉Navigator,当Book对象不同时,这个MaterialPage对象和另一个对象是不同的。如果没有一个唯一的key,框架就无法决定何时在不同的Pages之间显示过渡动画。

    注意:如果你喜欢,你也可以扩展Page来自定义行为。例如,页面添加一个自定义的过渡动画:

    class BookDetailsPage extends Page {
      final Book book;
      
      BookDetailsPage({
        this.book,
      }) : super(key: ValueKey(book));
      
      Route createRoute(BuildContext context) {
        return PageRouteBuilder(
          settings: this,
          pageBuilder: (context, animation, animation2) {
            final tween = Tween(begin: Offset(0.0, 1.0), end: Offset.zero);
            final curveTween = CurveTween(curve: Curves.easeInOut);
            return SlideTransition(
              position: animation.drive(curveTween).drive(tween),
              child: BookDetailsScreen(
                key: ValueKey(book),
                book: book,
              ),
            );
          },
        );
      }
    }
    

    最后,只提供pages参数而不提供onPopPage回调是一个错误。每当Navigator.pop()被调用时,这个函数就会被调用。它应该用来更新状态(决定页面列表),而且它必须调用路由上的didPop来确定pop是否成功。

    onPopPage: (route, result) {
      if (!route.didPop(result)) {
        return false;
      }
    
      // Update the list of pages by setting _selectedBook to null
      setState(() {
        _selectedBook = null;
      });
    
      return true;
    },
    

    在更新应用程序状态之前,检查 didPop 是否失败是很重要的。

    使用setState通知框架调用build()方法,当_selectedBook为空时,该方法返回一个带有单页的列表。

    下面是完整的例子。

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(BooksApp());
    }
    
    class Book {
      final String title;
      final String author;
    
      Book(this.title, this.author);
    }
    
    class BooksApp extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => _BooksAppState();
    }
    
    class _BooksAppState extends State<BooksApp> {
      Book _selectedBook;
    
      List<Book> books = [
        Book('Stranger in a Strange Land', 'Robert A. Heinlein'),
        Book('Foundation', 'Isaac Asimov'),
        Book('Fahrenheit 451', 'Ray Bradbury'),
      ];
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Books App',
          home: Navigator(
            pages: [
              MaterialPage(
                key: ValueKey('BooksListPage'),
                child: BooksListScreen(
                  books: books,
                  onTapped: _handleBookTapped,
                ),
              ),
              if (_selectedBook != null) BookDetailsPage(book: _selectedBook)
            ],
            onPopPage: (route, result) {
              if (!route.didPop(result)) {
                return false;
              }
    
              // Update the list of pages by setting _selectedBook to null
              setState(() {
                _selectedBook = null;
              });
    
              return true;
            },
          ),
        );
      }
    
      void _handleBookTapped(Book book) {
        setState(() {
          _selectedBook = book;
        });
      }
    }
    
    class BookDetailsPage extends Page {
      final Book book;
    
      BookDetailsPage({
        this.book,
      }) : super(key: ValueKey(book));
    
      Route createRoute(BuildContext context) {
        return MaterialPageRoute(
          settings: this,
          builder: (BuildContext context) {
            return BookDetailsScreen(book: book);
          },
        );
      }
    }
    
    class BooksListScreen extends StatelessWidget {
      final List<Book> books;
      final ValueChanged<Book> onTapped;
    
      BooksListScreen({
        @required this.books,
        @required this.onTapped,
      });
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: ListView(
            children: [
              for (var book in books)
                ListTile(
                  title: Text(book.title),
                  subtitle: Text(book.author),
                  onTap: () => onTapped(book),
                )
            ],
          ),
        );
      }
    }
    
    class BookDetailsScreen extends StatelessWidget {
      final Book book;
    
      BookDetailsScreen({
        @required this.book,
      });
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                if (book != null) ...[
                  Text(book.title, style: Theme.of(context).textTheme.headline6),
                  Text(book.author, style: Theme.of(context).textTheme.subtitle1),
                ],
              ],
            ),
          ),
        );
      }
    }
    

    就目前而言,这个应用只能让我们以声明式定义页面的堆栈。我们无法处理平台的后退按钮,浏览器的URL也不会随着我们的Navigator而改变。

    Router

    到目前为止,该应用可以显示不同的页面,但不能处理来自底层平台的路由,比如用户更新浏览器中的URL。

    本节介绍如何实现RouteInformationParserRouterDelegate以及更新应用状态。一旦设置好,应用程序就会与浏览器的URL保持同步。

    数据类型

    RouteInformationParser会将路由信息解析成用户定义的数据类型,所以我们先定义一下。

    class BookRoutePath {
      final int id;
      final bool isUnknown;
    
      BookRoutePath.home()
          : id = null,
            isUnknown = false;
    
      BookRoutePath.details(this.id) : isUnknown = false;
    
      BookRoutePath.unknown()
          : id = null,
            isUnknown = true;
    
      bool get isHomePage => id == null;
    
      bool get isDetailsPage => id != null;
    }
    

    在这个应用中,应用中所有的路由都可以使用一个类来表示。相反,你也可以选择使用不同的类来实现一个超类,或者用另一种方式来管理路由信息。

    RouterDelegate

    接下来,添加一个扩展RouterDelegate的类。

    class BookRouterDelegate extends RouterDelegate<BookRoutePath>
        with ChangeNotifier, PopNavigatorRouterDelegateMixin<BookRoutePath> {
      @override
      Widget build(BuildContext context) {
        // TODO
        throw UnimplementedError();
      }
    
      @override
      // TODO
      GlobalKey<NavigatorState> get navigatorKey => throw UnimplementedError();
    
      @override
      Future<void> setNewRoutePath(BookRoutePath configuration) {
        // TODO
        throw UnimplementedError();
      }
    }
    

    RouterDelegate上定义的通用类型是BookRoutePath,其中包含决定显示哪些页面所需的所有状态。

    我们需要将一些逻辑从_BooksAppState转移到BookRouterDelegate上,并创建一个GlobalKey。在这个例子中,应用状态直接存储在RouterDelegate上,但也可以分离到另一个类中。

    class BookRouterDelegate extends RouterDelegate<BookRoutePath>
        with ChangeNotifier, PopNavigatorRouterDelegateMixin<BookRoutePath> {
      final GlobalKey<NavigatorState> navigatorKey;
    
      Book _selectedBook;
      bool show404 = false;
    
      List<Book> books = [
        Book('Stranger in a Strange Land', 'Robert A. Heinlein'),
        Book('Foundation', 'Isaac Asimov'),
        Book('Fahrenheit 451', 'Ray Bradbury'),
      ];
    
      BookRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>();
      // ...
    

    为了在URL中显示正确的路径,我们需要根据应用的当前状态返回一个BookRoutePath:

      BookRoutePath get currentConfiguration {
        if (show404) {
          return BookRoutePath.unknown();
        }
    
        return _selectedBook == null
            ? BookRoutePath.home()
            : BookRoutePath.details(books.indexOf(_selectedBook));
      }
    

    接下来,RouterDelegate中的build方法需要返回一个Navigator:

    @override
    Widget build(BuildContext context) {
      return Navigator(
        key: navigatorKey,
        pages: [
          MaterialPage(
            key: ValueKey('BooksListPage'),
            child: BooksListScreen(
              books: books,
              onTapped: _handleBookTapped,
            ),
          ),
          if (show404)
            MaterialPage(key: ValueKey('UnknownPage'), child: UnknownScreen())
          else if (_selectedBook != null)
            BookDetailsPage(book: _selectedBook)
        ],
        onPopPage: (route, result) {
          if (!route.didPop(result)) {
            return false;
          }
    
          // Update the list of pages by setting _selectedBook to null
          _selectedBook = null;
          show404 = false;
          notifyListeners();
    
          return true;
        },
      );
    }
    

    onPopPage回调现在使用notifyListeners而不是setState,因为这个类现在是一个ChangeNotifier,而不是一个widget。当 RouterDelegate 通知它的监听器时,同样会通知Router widget 。 RouterDelegatecurrentConfiguration 已经改变,需要再次调用其build 方法来构建新的 Navigator

    _handleBookTapped方法也需要使用notifyListeners而不是setState

     void _handleBookTapped(Book book) {
        _selectedBook = book;
        notifyListeners();
      }
    

    当一个新的路由被push时,Router会调用setNewRoutePath,这样我们的应用就有机会根据路由的变化来更新应用状态。

      @override
      Future<void> setNewRoutePath(BookRoutePath path) async {
        if (path.isUnknown) {
          _selectedBook = null;
          show404 = true;
          return;
        }
    
        if (path.isDetailsPage) {
          if (path.id < 0 || path.id > books.length - 1) {
            show404 = true;
            return;
          }
    
          _selectedBook = books[path.id];
        } else {
          _selectedBook = null;
        }
    
        show404 = false;
      }
    

    RouteInformationParser

    RouteInformationParser提供了一个钩子来解析传入的路由(RouteInformation),并将其转换为用户定义的类型(BookRoutePath)。使用Uri类来进行解析工作。

    class BookRouteInformationParser extends RouteInformationParser<BookRoutePath> {
      @override
      Future<BookRoutePath> parseRouteInformation(
          RouteInformation routeInformation) async {
        final uri = Uri.parse(routeInformation.location);
        // Handle '/'
        if (uri.pathSegments.length == 0) {
          return BookRoutePath.home();
        }
    
        // Handle '/book/:id'
        if (uri.pathSegments.length == 2) {
          if (uri.pathSegments[0] != 'book') return BookRoutePath.unknown();
          var remaining = uri.pathSegments[1];
          var id = int.tryParse(remaining);
          if (id == null) return BookRoutePath.unknown();
          return BookRoutePath.details(id);
        }
    
        // Handle unknown routes
        return BookRoutePath.unknown();
      }
    
      @override
      RouteInformation restoreRouteInformation(BookRoutePath path) {
        if (path.isUnknown) {
          return RouteInformation(location: '/404');
        }
        if (path.isHomePage) {
          return RouteInformation(location: '/');
        }
        if (path.isDetailsPage) {
          return RouteInformation(location: '/book/${path.id}');
        }
        return null;
      }
    }
    

    这个实现是针对这个应用的,而不是一般的路由解析解决方案。后面会有更多的介绍。

    为了使用这些新的类,我们使用新的MaterialApp.router构造函数,并传入我们的自定义实现。

        return MaterialApp.router(
          title: 'Books App',
          routerDelegate: _routerDelegate,
          routeInformationParser: _routeInformationParser,
        );
    

    这是完整的示例:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(BooksApp());
    }
    
    class Book {
      final String title;
      final String author;
    
      Book(this.title, this.author);
    }
    
    class BooksApp extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => _BooksAppState();
    }
    
    class _BooksAppState extends State<BooksApp> {
      BookRouterDelegate _routerDelegate = BookRouterDelegate();
      BookRouteInformationParser _routeInformationParser =
          BookRouteInformationParser();
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp.router(
          title: 'Books App',
          routerDelegate: _routerDelegate,
          routeInformationParser: _routeInformationParser,
        );
      }
    }
    
    class BookRouteInformationParser extends RouteInformationParser<BookRoutePath> {
      @override
      Future<BookRoutePath> parseRouteInformation(
          RouteInformation routeInformation) async {
        final uri = Uri.parse(routeInformation.location);
        // Handle '/'
        if (uri.pathSegments.length == 0) {
          return BookRoutePath.home();
        }
    
        // Handle '/book/:id'
        if (uri.pathSegments.length == 2) {
          if (uri.pathSegments[0] != 'book') return BookRoutePath.unknown();
          var remaining = uri.pathSegments[1];
          var id = int.tryParse(remaining);
          if (id == null) return BookRoutePath.unknown();
          return BookRoutePath.details(id);
        }
    
        // Handle unknown routes
        return BookRoutePath.unknown();
      }
    
      @override
      RouteInformation restoreRouteInformation(BookRoutePath path) {
        if (path.isUnknown) {
          return RouteInformation(location: '/404');
        }
        if (path.isHomePage) {
          return RouteInformation(location: '/');
        }
        if (path.isDetailsPage) {
          return RouteInformation(location: '/book/${path.id}');
        }
        return null;
      }
    }
    
    class BookRouterDelegate extends RouterDelegate<BookRoutePath>
        with ChangeNotifier, PopNavigatorRouterDelegateMixin<BookRoutePath> {
      final GlobalKey<NavigatorState> navigatorKey;
    
      Book _selectedBook;
      bool show404 = false;
    
      List<Book> books = [
        Book('Stranger in a Strange Land', 'Robert A. Heinlein'),
        Book('Foundation', 'Isaac Asimov'),
        Book('Fahrenheit 451', 'Ray Bradbury'),
      ];
    
      BookRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>();
    
      BookRoutePath get currentConfiguration {
        if (show404) {
          return BookRoutePath.unknown();
        }
        return _selectedBook == null
            ? BookRoutePath.home()
            : BookRoutePath.details(books.indexOf(_selectedBook));
      }
    
      @override
      Widget build(BuildContext context) {
        return Navigator(
          key: navigatorKey,
          pages: [
            MaterialPage(
              key: ValueKey('BooksListPage'),
              child: BooksListScreen(
                books: books,
                onTapped: _handleBookTapped,
              ),
            ),
            if (show404)
              MaterialPage(key: ValueKey('UnknownPage'), child: UnknownScreen())
            else if (_selectedBook != null)
              BookDetailsPage(book: _selectedBook)
          ],
          onPopPage: (route, result) {
            if (!route.didPop(result)) {
              return false;
            }
    
            // Update the list of pages by setting _selectedBook to null
            _selectedBook = null;
            show404 = false;
            notifyListeners();
    
            return true;
          },
        );
      }
    
      @override
      Future<void> setNewRoutePath(BookRoutePath path) async {
        if (path.isUnknown) {
          _selectedBook = null;
          show404 = true;
          return;
        }
    
        if (path.isDetailsPage) {
          if (path.id < 0 || path.id > books.length - 1) {
            show404 = true;
            return;
          }
    
          _selectedBook = books[path.id];
        } else {
          _selectedBook = null;
        }
    
        show404 = false;
      }
    
      void _handleBookTapped(Book book) {
        _selectedBook = book;
        notifyListeners();
      }
    }
    
    class BookDetailsPage extends Page {
      final Book book;
    
      BookDetailsPage({
        this.book,
      }) : super(key: ValueKey(book));
    
      Route createRoute(BuildContext context) {
        return MaterialPageRoute(
          settings: this,
          builder: (BuildContext context) {
            return BookDetailsScreen(book: book);
          },
        );
      }
    }
    
    class BookRoutePath {
      final int id;
      final bool isUnknown;
    
      BookRoutePath.home()
          : id = null,
            isUnknown = false;
    
      BookRoutePath.details(this.id) : isUnknown = false;
    
      BookRoutePath.unknown()
          : id = null,
            isUnknown = true;
    
      bool get isHomePage => id == null;
    
      bool get isDetailsPage => id != null;
    }
    
    class BooksListScreen extends StatelessWidget {
      final List<Book> books;
      final ValueChanged<Book> onTapped;
    
      BooksListScreen({
        @required this.books,
        @required this.onTapped,
      });
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: ListView(
            children: [
              for (var book in books)
                ListTile(
                  title: Text(book.title),
                  subtitle: Text(book.author),
                  onTap: () => onTapped(book),
                )
            ],
          ),
        );
      }
    }
    
    class BookDetailsScreen extends StatelessWidget {
      final Book book;
    
      BookDetailsScreen({
        @required this.book,
      });
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                if (book != null) ...[
                  Text(book.title, style: Theme.of(context).textTheme.headline6),
                  Text(book.author, style: Theme.of(context).textTheme.subtitle1),
                ],
              ],
            ),
          ),
        );
      }
    }
    
    class UnknownScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: Text('404!'),
          ),
        );
      }
    }
    

    在Chrome浏览器中运行这个示例,现在可以显示正在导航的路由,并在手动编辑URL时导航到正确的页面。

    TransitionDelegate

    您可以提供TransitionDelegate的自定义实现,自定义当页面列表发生变化时,路由如何出现在(或从)屏幕上。如果您需要自定义,请继续阅读,但如果您对默认行为满意,可以跳过本节。

    为导航器提供一个自定义的TransitionDelegate,定义所需的行为:

    // New:
    TransitionDelegate transitionDelegate = NoAnimationTransitionDelegate();
    
          child: Navigator(
            key: navigatorKey,
            // New:
            transitionDelegate: transitionDelegate,
    

    例如,下面的实现会禁用所有的过渡动画。

    class NoAnimationTransitionDelegate extends TransitionDelegate<void> {
      @override
      Iterable<RouteTransitionRecord> resolve({
        List<RouteTransitionRecord> newPageRouteHistory,
        Map<RouteTransitionRecord, RouteTransitionRecord>
            locationToExitingPageRoute,
        Map<RouteTransitionRecord, List<RouteTransitionRecord>>
            pageRouteToPagelessRoutes,
      }) {
        final results = <RouteTransitionRecord>[];
    
        for (final pageRoute in newPageRouteHistory) {
          if (pageRoute.isWaitingForEnteringDecision) {
            pageRoute.markForAdd();
          }
          results.add(pageRoute);
        }
    
        for (final exitingPageRoute in locationToExitingPageRoute.values) {
          if (exitingPageRoute.isWaitingForExitingDecision) {
            exitingPageRoute.markForRemove();
            final pagelessRoutes = pageRouteToPagelessRoutes[exitingPageRoute];
            if (pagelessRoutes != null) {
              for (final pagelessRoute in pagelessRoutes) {
                pagelessRoute.markForRemove();
              }
            }
          }
    
          results.add(exitingPageRoute);
        }
        return results;
      }
    }
    

    这个自定义的实现覆盖了resolve(),它负责将各种路由标记为推送、弹出、添加、完成或删除。

    • markForPush - 显示路由时有动画过渡。
    • markForAdd - 显示路由时没有动画过渡。
    • markForPop - 删除路由时有动画过渡,并用result完成它。在此上下文中,"完成 "意味着result对象被传递给AppRouterDelegateonPopPage回调。
    • markForComplete - 删除路由时没有动画过渡,并用result来完成它。
    • markForRemove - 删除路由时没有动画过渡,且没有完成。

    这个类只影响声明式API,这就是为什么点击后退按钮仍然显示过渡动画。

    本示例的工作方式:这个例子既关注新的路由,也关注退出屏幕的路由。它遍历newPageRouteHistory中的所有对象,并使用markForAdd将它们标记为不需要过渡动画的添加。接下来,它遍历locationToExitingPageRoute map 的值。如果它找到一个标记为isWaitingForExitingDecision的路由,那么它就调用markForRemove来表示该路由应该在没有过渡和没有完成的情况下被删除。

    下面是完整的示例(Gist)

    嵌套的路由器

    这个较大的演示程序展示了如何在一个Router中添加另一个Router。许多应用程序需要为BottomAppBar中的目的地提供路由,并为其上方的一堆视图提供路由,这需要两个导航器。为此,应用使用状态对象来存储应用特定的导航状态(选定的菜单索引和选定的Book对象)。这个例子还展示了如何配置哪个Router处理后退按钮。

    嵌套路由示例(Gist)

    接下来是什么

    本文探讨了如何在特定的应用中使用这些API,但也可以用来构建更高级别的API包。我们希望你能和我们一起探讨在这些功能之上构建的更高级的API能为用户做什么。

    展开全文
  • 我们在使用idea的时候,会遇到导入新项目的时候wu'f无法导入maven包,怎么点reimport都不好使,看我原来的maven配置: 将maven 改成他默认的即可:
  • 在同位素$ ^ {97} $ Ag中,发现了一种长寿命(1 / 2-)异构体状态,其激发确定为618(38)keV。 同时,这也是通过多次反射飞行时间质谱法首次发现核异构状态。 将测得的激发与大规模壳模型计算进行了比较,这...
  • Java8中新增加的集合类
  • 数字助听器中的多通道响度补偿算法,通常是在划分出的多个频段内分别进行补偿,这在一定程度上会导致共振峰结构的破坏,造成...结果表明,该方法有效提高语音响度和改善听损患者的言语识别率,具有一定的应用价值。
  • 新的相机响应曲线函数标定算法,运用了最小二乘法和B样条曲线拟合,有助于HDR的合成
  • unity 5.3 中新增加的 Attribute

    千次阅读 2015-11-26 09:24:37
    注: 标记 [RuntimeInitializeOnLoadMethod] 的方法的执行顺序是不保证的。 using UnityEngine; class MyClass { [RuntimeInitializeOnLoadMethod] static void OnRuntimeMethodLoad () { Debug.Log("After ...
  • Impala不查询到Hive中新增加的表

    千次阅读 2014-11-20 19:31:45
    使用Cloudera Manager部署安装的CDH和Impala,Hive中新增加的表,impala中查询不到,其原因是/etc/impala/conf下面没有hadoop和hive相关的配置文件:  将hadooop-core.xml,hdfs-site.xml,hive-site.xml拷贝到...
  • 新画的热风焊盘(PCB Thermal Relif)改了路径,要想在Pad Designer中找到,方法是在allegro中Setup-User Preference Editor-Paths-Library-psmpath Items中添加新的路径。
  • 但是被final标记后在任何地方都不被override,对于lib设计者来说希望的结果是在module内可以被override,而在import到外部后不被override 这就是open产生的原因,现在的public表示在其他module中不可以被...
  • 今天就来说一说如何在Github中新发布自己的代码吧。 在windows下: 第一步:在GIthub中注册自己的账号和密码(p.s 账号名后期能够更改) https://github.com/ 第二步:下载Git 工具包并安装 ...
  • 但是这些jar包都在 ![图片说明](https://img-ask.csdn.net/upload/202003/28/1585374758_330699.png)![图片说明](https://img-ask.csdn.net/upload/202003/28/1585374786_397564.png)
  • Ubuntu 18.04 中新的DNS设置方法

    千次阅读 2019-09-07 10:13:11
    Ubuntu 18.04 DNS设置 Ubuntu在新版本中已经更改了网络设置,如何更改DNS? Ubuntu在新版本中已经更改了网络...网络设置生效,如果有问题,可以过加上debug参数查看错误信息: sudo netplan --debug apply
  • 爱数案例本项目由爱数投递并参与“数据猿年度金猿策划活动——2021大数据产业创新服务企业榜单及奖项”评选。数据智能产业创新服务媒体——聚焦数智· 改变商业中新天津生态城是中国、新加坡两国...
  • html5 中新的datalist 自动下拉提示输入框
  • Unity5中新的Shader

    万次阅读 2016-05-20 15:53:59
    一、Unity5中新的Shader体系简析   Unity5和之前的书写模式有了一定的改变。Unity5时代的Shader Reference官方文档也进一步地变得丰满。  主要需要了解到的是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 61,386
精华内容 24,554
关键字:

中新能