开启Fluter基础之旅四-------表格、动画、手势
表格:
在上一次https://www.cnblogs.com/webor2006/p/12578218.html学习了Flutter布局,这里再来学习一个表格布局,这个在早期HTML的布局中是经常被用的,先来了解一下它:
下面来使用一下:
其中Table看一下它的命名构造的定义:
所以咱们来定义一下:
class HomePage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(body: Center(child: Table(columnWidths: const <int, TableColumnWidth>{//定义4列,每列宽500: FixedColumnWidth(50.0),1: FixedColumnWidth(50.0),2: FixedColumnWidth(50.0),3: FixedColumnWidth(50.0),},border: TableBorder.all(//定义表格边框是红色,宽度为5color: Colors.red,width: 2.0,),children: const <TableRow>[//定义每行的具体内容TableRow(//第一行,定义表头children: [Text("头像"),Text("姓名"),Text("年龄"),Text("身高"),]),TableRow(//第二行children: [Icon(Icons.account_circle),Text("张三"),Text("21"),Text("160"),]),TableRow(//第三行children: [Icon(Icons.account_circle),Text("李四"),Text("24"),Text("170"),]),TableRow(//第四行children: [Icon(Icons.account_circle),Text("王五"),Text("40"),Text("178"),]),],),),);}
}
运行:
这里简单的使用一下既可,具体在实际项目中进行操练。
手势:
概述:
Flutter中的手势分为两层:
第一层是触摸原始指针(Pointer)事件,描述了屏幕上指针(如触摸、鼠标和触控笔)的位置和移动。
指针(Pointer)代表用户与屏幕交互的原始数据,有四种事件类型:
- PointerDownEvent: 指针接触到屏幕
- PointerMoveEvent: 指针从屏幕上的一个位置移动到另一个位置
- PointerUpEvent: 指针停止接触屏幕
- PointerCancelEvent: 指针的输入事件不再针对此应用(事件取消)
第二层就是我们可以检测到的手势,主要分为三大类:轻击、拖动和缩放。
GestureDetector:
GestureDetector可以进行手势检测,如单击,双击,长按,垂直、水平拖动等。它的事件描述如下:
下面来使用一下:
import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: HomePage(),);}
}class HomePage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(body: Center(child: MyButton(),),);}
}class MyButton extends StatelessWidget {@overrideWidget build(BuildContext context) {return GestureDetector();}
}
其中简单看一下GestureDetector的命名构造:
咱们定义一个child:
接下来则给咱们的这个Container添加一个事件:
运行:
还有更多的事件就待实际项目中再来熟悉。
Dissmissible:
像典型列表中的左测删除效果就可以用它来实现,先看一下它的属性:
下面具体来看一下,先来显示一个列表:
其中代码上有一个语法再回忆一下:
对应语法如:
接下来则需要构建Dissmissible这个View了,先看一下它的命名构造:
那咱们来定义一下,先来定义这个Key,先看一下它的定义:
那还记得这个factory工厂构建的语法么?可以参考:https://www.cnblogs.com/webor2006/p/11981709.html,回忆一下:
那咱们来定义一下:
运行看一下效果:
可以看到滑动就删除了,但是呢其列表的数据是没有删除的,所以这里可以做一个监听,另外增加一下背景效果看得明显一点:
其实这个列表的左滑删除跟平常我们通常所的那个效果还是有区别的,要实现那种效果就需要自定义了,这种效果在之后再来学习。
动画:
概述:
Flutter中的动画分为补间(Tween)动画和基于物理(Physics-based)的动画。
- 在补间动画中,定义了开始点和结束点、时间线以及定义转换时间和速度的曲线,然后由框架自动计算如何从开始点过渡到结束点。
- 在基于物理的动画(遵循物理学定律)中,运动被模拟为与真实世界的行为相似,可以模拟弹簧、阻尼、重力等物理效果。例如,当你掷球时,它在何处落地,取决于抛球速度有多快、球有多重、距离地面有多远。类似地,将连接在弹簧上的球落下(并弹起)与连接到绳子上的球放下的方式也是不同。
Animation:
Animation类是Flutter动画中核心的抽象类,它包含动画的当前值和状态两个属性。定义了动画的一系列监听回调,如:
- 值监听:addListener、removeListener
- 状态监听:addStatusListener、removeStatusListener
另外它有四种状态:
- dismissed:动画初始状态
- forward:动画从头到尾播放状态
- reverse:动画从尾到头播放状态
- completed:动画完成状态
其中Animation是一个抽象类:
AnimationController:
它是用来进行动画控制的,它有如下特点:
- AnimationController是一个特殊的Animation对象,在屏幕刷新的每一帧,就会生成一个新的值,默认情况下,AnimationController在给定的时间段内会线性的生成从0.0到1.0的数字。
- 属于Animation类型。
- 具有控制动画的方法,例如,.forward()方法可以启动动画。
- 当创建一个AnimationController时,需要传递一个vsync参数,存在vsync时会防止屏幕外动画(动画的UI不在当前屏幕时)消耗不必要的资源。
- 通过将SingleTickerProviderStateMixin添加到类定义中,可以将stateful对象作为vsync的值。如果要使用自定义的State对象作为vsync时,请包含TickerProviderStateMixin。
- 特别注意:在不使用时需要调用dispose方法,否则会造成资源泄露!!!!
下面来简单用一下补间(Tween)动画,要实现的效果很简单,如下:
也就是从0~1之间在指定时间进行增加,先来搭个骨架:
import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: HomePage(),);}
}class HomePage extends StatefulWidget {@override_HomePageState createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {Animation<double> _doubleAnim;@overrideWidget build(BuildContext context) {return Scaffold(body: Center(child: Text(''),),floatingActionButton: FloatingActionButton(onPressed: () {//TODO},),);}
}
其中要控制动画就需要用到AnimationController了:
接下来就需要对它进行初始化了,而在使用化AnimationController时,如之前的理论:
具体传参为:
其中with这是啥语法呢?可以参考:https://www.cnblogs.com/webor2006/p/11981709.html,回忆一下:
而对于vsync这个参数,这里直接传当前的Stateful对象,其理论规则是:
接下来再来初始化Animation,由于它是抽象的,需要用到子类,这里会用到Tween,先来对它有个了解:
Tween:
线性估值器。Flutter还封装定义了不同类型的估值器:
- ReverseTween
- ColorTween
- SizeTween
- RectTween
- IntTween
- StepTween
- ConstantTween
下面则来初始化一下Animation:
然后此时咱们还可以给动画做个监听:
这里又用到了一个知识点,标了下划线的,回忆一下:
好,接下来动画的启动则放在按钮事件里,如下:
那下面运行看一下日志输出:
2020-04-21 16:30:06.056 7116-7195/com.example.flutter_app I/flutter: 0.0
2020-04-21 16:30:06.057 7116-7195/com.example.flutter_app I/flutter: AnimationStatus.dismissed
2020-04-21 16:30:06.057 7116-7195/com.example.flutter_app I/flutter: AnimationStatus.forward
2020-04-21 16:30:06.060 7116-7195/com.example.flutter_app I/flutter: 0.0
2020-04-21 16:30:06.085 7116-7195/com.example.flutter_app I/flutter: 0.03334
2020-04-21 16:30:06.102 7116-7195/com.example.flutter_app I/flutter: 0.050009
2020-04-21 16:30:06.117 7116-7195/com.example.flutter_app I/flutter: 0.066682
2020-04-21 16:30:06.135 7116-7195/com.example.flutter_app I/flutter: 0.083354
2020-04-21 16:30:06.151 7116-7195/com.example.flutter_app I/flutter: 0.100027
2020-04-21 16:30:06.167 7116-7195/com.example.flutter_app I/flutter: 0.1167
2020-04-21 16:30:06.184 7116-7195/com.example.flutter_app I/flutter: 0.133373
2020-04-21 16:30:06.200 7116-7195/com.example.flutter_app I/flutter: 0.150046
2020-04-21 16:30:06.217 7116-7195/com.example.flutter_app I/flutter: 0.166718
2020-04-21 16:30:06.234 7116-7195/com.example.flutter_app I/flutter: 0.183391
2020-04-21 16:30:06.252 7116-7195/com.example.flutter_app I/flutter: 0.200063
2020-04-21 16:30:06.268 7116-7195/com.example.flutter_app I/flutter: 0.216734
2020-04-21 16:30:06.284 7116-7195/com.example.flutter_app I/flutter: 0.233406
2020-04-21 16:30:06.301 7116-7195/com.example.flutter_app I/flutter: 0.250079
2020-04-21 16:30:06.318 7116-7195/com.example.flutter_app I/flutter: 0.266751
2020-04-21 16:30:06.335 7116-7195/com.example.flutter_app I/flutter: 0.283423
2020-04-21 16:30:06.352 7116-7195/com.example.flutter_app I/flutter: 0.300096
2020-04-21 16:30:06.368 7116-7195/com.example.flutter_app I/flutter: 0.316769
2020-04-21 16:30:06.385 7116-7195/com.example.flutter_app I/flutter: 0.333441
2020-04-21 16:30:06.401 7116-7195/com.example.flutter_app I/flutter: 0.350113
2020-04-21 16:30:06.418 7116-7195/com.example.flutter_app I/flutter: 0.366785
2020-04-21 16:30:06.436 7116-7195/com.example.flutter_app I/flutter: 0.383458
2020-04-21 16:30:06.451 7116-7195/com.example.flutter_app I/flutter: 0.400129
2020-04-21 16:30:06.468 7116-7195/com.example.flutter_app I/flutter: 0.416798
2020-04-21 16:30:06.484 7116-7195/com.example.flutter_app I/flutter: 0.433468
2020-04-21 16:30:06.501 7116-7195/com.example.flutter_app I/flutter: 0.450138
2020-04-21 16:30:06.518 7116-7195/com.example.flutter_app I/flutter: 0.466808
2020-04-21 16:30:06.534 7116-7195/com.example.flutter_app I/flutter: 0.48348
2020-04-21 16:30:06.552 7116-7195/com.example.flutter_app I/flutter: 0.500149
2020-04-21 16:30:06.585 7116-7195/com.example.flutter_app I/flutter: 0.533494
2020-04-21 16:30:06.601 7116-7195/com.example.flutter_app I/flutter: 0.550165
2020-04-21 16:30:06.619 7116-7195/com.example.flutter_app I/flutter: 0.566836
2020-04-21 16:30:06.635 7116-7195/com.example.flutter_app I/flutter: 0.583508
2020-04-21 16:30:06.652 7116-7195/com.example.flutter_app I/flutter: 0.600177
2020-04-21 16:30:06.667 7116-7195/com.example.flutter_app I/flutter: 0.616846
2020-04-21 16:30:06.684 7116-7195/com.example.flutter_app I/flutter: 0.633516
2020-04-21 16:30:06.701 7116-7195/com.example.flutter_app I/flutter: 0.650186
2020-04-21 16:30:06.718 7116-7195/com.example.flutter_app I/flutter: 0.666856
2020-04-21 16:30:06.734 7116-7195/com.example.flutter_app I/flutter: 0.683528
2020-04-21 16:30:06.751 7116-7195/com.example.flutter_app I/flutter: 0.700201
2020-04-21 16:30:06.767 7116-7195/com.example.flutter_app I/flutter: 0.716874
2020-04-21 16:30:06.784 7116-7195/com.example.flutter_app I/flutter: 0.733546
2020-04-21 16:30:06.801 7116-7195/com.example.flutter_app I/flutter: 0.750219
2020-04-21 16:30:06.818 7116-7195/com.example.flutter_app I/flutter: 0.766891
2020-04-21 16:30:06.834 7116-7195/com.example.flutter_app I/flutter: 0.783564
2020-04-21 16:30:06.853 7116-7195/com.example.flutter_app I/flutter: 0.800236
2020-04-21 16:30:06.870 7116-7195/com.example.flutter_app I/flutter: 0.816908
2020-04-21 16:30:06.884 7116-7195/com.example.flutter_app I/flutter: 0.83358
2020-04-21 16:30:06.901 7116-7195/com.example.flutter_app I/flutter: 0.850252
2020-04-21 16:30:06.918 7116-7195/com.example.flutter_app I/flutter: 0.866924
2020-04-21 16:30:06.935 7116-7195/com.example.flutter_app I/flutter: 0.883597
2020-04-21 16:30:06.952 7116-7195/com.example.flutter_app I/flutter: 0.900269
2020-04-21 16:30:06.969 7116-7195/com.example.flutter_app I/flutter: 0.916941
2020-04-21 16:30:06.984 7116-7195/com.example.flutter_app I/flutter: 0.933614
2020-04-21 16:30:07.002 7116-7195/com.example.flutter_app I/flutter: 0.950286
2020-04-21 16:30:07.018 7116-7195/com.example.flutter_app I/flutter: 0.966958
2020-04-21 16:30:07.034 7116-7195/com.example.flutter_app I/flutter: 0.983631
2020-04-21 16:30:07.051 7116-7195/com.example.flutter_app I/flutter: 1.0
2020-04-21 16:30:07.052 7116-7195/com.example.flutter_app I/flutter: AnimationStatus.completed
其中可以看到动画值从0.0到1.0在1秒内的变化过程,另外还能看到动画经过了三个状态,如之前理论所说:
但是!!这个程序还是有个写法上的问题:
所以,咱们在State中生命回调中需要进行调用一下:
关于State生命周期可以参考:https://www.cnblogs.com/webor2006/p/11996535.html,这里复习一下:
接下来则让这个变化显示在文件上:
运行:
案例二:扩大View动画效果:
接下来还是利用Tween来对Widget进行宽高进行改变达成动画的效果,具体如下:
import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: HomePage(),);}
}class HomePage extends StatefulWidget {@override_HomePageState createState() => _HomePageState();
}class _HomePageState extends State<HomePage>with SingleTickerProviderStateMixin {Animation<double> _doubleAnim;AnimationController _animationController;@overridevoid initState() {super.initState();_animationController =AnimationController(vsync: this, duration: const Duration(seconds: 1))..forward();_doubleAnim = Tween(begin: 20.0, end: 100.0).animate(_animationController)..addListener(() {setState(() {});//让其widget进行更新})..addStatusListener((status) {//监听动画状态让其动画循环播放if (status == AnimationStatus.completed) {_animationController.reverse();} else if (status == AnimationStatus.dismissed) {_animationController.forward();}});}@overridevoid dispose() {super.dispose();_animationController.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(body: Center(child: Container(width: _doubleAnim.value,height: _doubleAnim.value,color: Colors.red,),),);}
}
程序比较简单,运行一下:
案例三:通过ColorTween实现颜色渐变效果
基本上写法依葫芦画瓢就成了:
import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: HomePage(),);}
}class HomePage extends StatefulWidget {@override_HomePageState createState() => _HomePageState();
}class _HomePageState extends State<HomePage>with SingleTickerProviderStateMixin {Animation<Color> _colorAnim;AnimationController _animationController;@overridevoid initState() {super.initState();_animationController =AnimationController(vsync: this, duration: const Duration(seconds: 1))..forward();_colorAnim = ColorTween(begin: Colors.red, end: Colors.white).animate(_animationController)..addListener(() {setState(() {});})..addStatusListener((status) {//监听动画状态让其动画循环播放if (status == AnimationStatus.completed) {_animationController.reverse();} else if (status == AnimationStatus.dismissed) {_animationController.forward();}});}@overridevoid dispose() {super.dispose();_animationController.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(body: Center(child: Container(width: 200.0,height: 200.0,color: _colorAnim.value,),),);}
}
案例四:AnimatedWidget
先来看一下效果:
这个录屏有点卡,其实是旋转+放大动画的组合,下面来看一下如何实现的:
import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: HomePage(),);}
}class HomePage extends StatefulWidget {@override_HomePageState createState() => _HomePageState();
}class _HomePageState extends State<HomePage>with SingleTickerProviderStateMixin {Animation<double> _doubleAnim;AnimationController _animationController;@overridevoid initState() {super.initState();_animationController =AnimationController(vsync: this, duration: const Duration(seconds: 1))..forward();_doubleAnim = Tween(begin: 0.0, end: 1.0).animate(_animationController)..addListener(() {setState(() {});})..addStatusListener((status) {if (status == AnimationStatus.completed) {_animationController.reverse();} else if (status == AnimationStatus.dismissed) {_animationController.forward();}});}@overridevoid dispose() {super.dispose();_animationController.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(body: Center(child: MyLogo(animation: _doubleAnim,),),);}
}class MyLogo extends AnimatedWidget {MyLogo({Key key, @required Animation animation}): super(key: key, listenable: animation);@overrideWidget build(BuildContext context) {return Container(width: 200.0,height: 200.0,child: FlutterLogo(),);}
}
目前还木有动画,运行就是一个Flutter静态的LOGO:
接下为则给这张LOGO增加两个混合的动画效果,如下:
最终的效果就如之前所看到的那样了。
Curve:
定义了时间和数值的抽象类。Flutter封装定义了一系列的插值器,如linear、decelerate、ease、bounce、cubic等。当然Flutter提供的不满足需求的话,也可以自定义插值器。 在Android中动画也有插值器的概念,看一下Curve类中定义的各种插值器:
下面来使用一下,先看一下最终效果:
具体来看一下如何来实现:
import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: HomePage(),);}
}class HomePage extends StatefulWidget {@override_HomePageState createState() => _HomePageState();
}class _HomePageState extends State<HomePage>with SingleTickerProviderStateMixin {Animation<double> _doubleAnim;AnimationController _animationController;@overridevoid initState() {super.initState();_animationController =AnimationController(vsync: this, duration: const Duration(seconds: 1))..forward();_doubleAnim = Tween(begin: 0.0, end: 1.0).animate(_animationController)..addListener(() {setState(() {});})..addStatusListener((status) {if (status == AnimationStatus.completed) {_animationController.reverse();} else if (status == AnimationStatus.dismissed) {_animationController.forward();}});}@overridevoid dispose() {super.dispose();_animationController.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(body: AnimatedBuilder(animation: _animationController,),);}
}
而AnimatedBuilder还有一个参数需要设置:
而该builder是一个函数,形态为:
所以咱们传递一下:
然后这里对于动画的初始化需要修改一下,因为是从左到右进行平移,然后此次增加了一个慢进快出的插值器,具体如下:
class _HomePageState extends State<HomePage>with SingleTickerProviderStateMixin {Animation<double> _doubleAnim;AnimationController _animationController;void myListener(status) {if (status == AnimationStatus.completed) {//当动画执行完毕之后,将监听去掉,并将其恢复,不然还是会重复执行这个完成的回调监听的_animationController.removeStatusListener(myListener);_animationController.reset();_doubleAnim = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(parent: _animationController, curve: Curves.fastOutSlowIn));_animationController.forward();}}@overridevoid initState() {super.initState();_animationController =AnimationController(vsync: this, duration: const Duration(seconds: 2));_doubleAnim = Tween(begin: -1.0, end: 0.0).animate(CurvedAnimation(parent: _animationController, curve: Curves.fastOutSlowIn))..addStatusListener(myListener);_animationController.forward();}@overridevoid dispose() {super.dispose();_animationController.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(body: AnimatedBuilder(animation: _animationController,builder: (BuildContext context, Widget child) {var _screenWidth = MediaQuery.of(context).size.width; //获得屏幕宽度return Transform(transform: Matrix4.translationValues(//进行x平移操作_doubleAnim.value * _screenWidth,0.0,0.0),child: Center(child: Container(width: 200.0,height: 200.0,child: FlutterLogo(),),),);}),);}
}
关于动画这块要想熟练掌握只能是在实际项目中不断进行操练才行,这里先对其入个门,知道动画的一个基本实现步骤既可。
开启Fluter基础之旅四-------表格、动画、手势相关推荐
- 开启Fluter基础之旅五-------ListView 3D滚动、Flipper效果、ListView下拉刷新上拉加载、ListView重排序...
继续来来操练Flutter的基础,对于Flutter的学习也有一段时间了,实操项目还木有做过,所以待这次基础学完之后就打算用一个项目对之前所学的进行一下巩固,不然光学这些零散的知识点最终还是不会Flu ...
- 开启Fluter基础之旅二-------Future再论、常用组件、Material Design风格组件学习
Future再论: 这里在继续往下学习之前,先来看一下Dart语言关于Event-Queue和Microtask Queue需要注意的一个小点,这个在之前https://www.cnblogs.com ...
- 开启Fluter基础之旅三-------Material Design风格组件、Cupertino风格组件、Flutter页面布局篇...
Material Design风格组件: 继续接着上一次https://www.cnblogs.com/webor2006/p/12545701.html的Material Design进行学习. A ...
- 容器开启数据服务之旅系列(四):Kubernetes QoS 助力在线运用与大数据离线运用的带宽控制和磁盘控制...
容器开启数据服务之旅系列(四) Kubernetes QoS 助力在线运用与大数据离线运用的带宽控制和磁盘控制 概述 本文是2018年大数据峰会上的一些分享,关于在线业务,离线业务在ACK(阿里云容器 ...
- 计算机表格数据处理,《计算机操作基础》第四章 Excel表格数据处理课后练习.docx...
PAGE1 / NUMPAGES1 <计算机操作基础> 第四章 Excel表格数据处理课后练习 一. 单项选择题 共 17 题 1. 在Excel2003工作表的A1单元格中输入" ...
- NGUI从入门到实战第1章开启NGUI学习之旅
NGUI从入门到实战第1章开启NGUI学习之旅 NGUI是Unity最重要的插件之一.使用NGUI可以高效地为游戏添加界面.本书将带领大家学习NGUI.作为NGUI学习之旅的第一站,本章会在整体上介绍 ...
- 一文开启自然语言处理之旅
1. 自然语言处理简介 自然语言处理 (Natural Language Processing, NLP) 是人工智能领域最火热的研究方向之一,NLP 为计算机真正理解人类语言提供了基础.NLP 已成 ...
- 给科技插上翅膀,中兴以5G技术开启万物互联之旅
随着5G行业应用场景的不断扩展及人工智能的发展,5G即将迎来新一轮技术革新.从生活到生产,5G技术无所不在,从人与人连接,到人与物.物与物连接,中兴通讯正以5G技术开启万物互联之旅:如在产品领域推出5 ...
- 使用SpringBoot开启微服务之旅
\ 本文要点 \\ 微服务可以使你的代码解耦\\t 微服务可以使不同的团队专注于更小范围的工作职责.使用独立的技术.更安全更频繁地部署\\t SpringBoot支持各种REST API的实现方式\\ ...
最新文章
- c语言和python哪个自学好-自学编程应该从c语言还是python入手?
- nfs上安装oracle,使用NFS安装oracle软件
- JavaScript Tutorial
- Android怎么自定义listview布局,Android ListView自定义布局
- 2021中国新消费品牌社媒营销研究报告
- TinyML与Tensor Flow Lite的关系
- java 算法基础之一寻找最大公约数
- 面向对象之多态、多态性
- 梦断代码最后4章读后感
- 如何循序渐进向DotNet架构师发展(转)
- Web APIs day6 | 正则阶段案例
- 根据UI设计图得到android所需要的dp
- 北航计算机组成实验project4,北航fpga实验的报告.docx
- 移动软件开发-高校新闻网
- python web server 知乎_“知乎网”技术方案初探
- Python_25_XML解析
- 什么是EISA分区,如何删除
- 印尼央行批准外国游客使用移动支付
- 整数序列中的众数和中位数
- oracle用户sysman过期,sysman密码过期导致oem无法使用