Core Animation

  1. 接下来详细介绍下动画的各个属性及作用
  • fromValue: 动画的开始值(Any类型, 根据动画不同可以是CGPoint、NSNumber等)
  • toValue: 动画的结束值, 和fromValue类似
  • beginTime: 动画的开始时间
  • duration : 动画的持续时间
  • repeatCount : 动画的重复次数
  • fillMode: 动画的运行场景
  • isRemovedOnCompletion: 完成后是否删除动画
  • autoreverses: 执行的动画按照原动画返回执行
  • path:关键帧动画中的执行路径
  • values: 关键帧动画中的关键点数组
  • animations: 组动画中的动画数组
  • delegate : 动画代理, 封装了动画的执行和结束方法
  • timingFunction: 控制动画的显示节奏, 系统提供五种值选择,分别是:

1.kCAMediaTimingFunctionDefault( 默认,中间快)

2.kCAMediaTimingFunctionLinear(线性动画)

3.kCAMediaTimingFunctionEaseIn(先慢后快 慢进快出)

4.kCAMediaTimingFunctionEaseOut(先块后慢快进慢出)

5.kCAMediaTimingFunctionEaseInEaseOut(先慢后快再慢)

  • type: 过渡动画的动画类型,系统提供了多种过渡动画, 分别是:

1: fade(淡出 默认)

2: moveIn(覆盖原图)

3: push(推出)

4: fade(淡出 默认)

5: reveal(底部显示出来)

6: cube(立方旋转)

7: suck(吸走)

8: oglFlip(水平翻转 沿y轴)

9: ripple(滴水效果)

10: curl(卷曲翻页 向上翻页)

11: unCurl(卷曲翻页返回 向下翻页)

12: caOpen(相机开启)

13: caClose(相机关闭)

  • subtype : 过渡动画的动画方向, 系统提供了四种,分别是:

1.fromLeft( 从左侧)

2.fromRight(从右侧)

3.fromTop(有上面)

4.fromBottom(从下面

1. 基础动画( CABasicAnimation )

基础动画主要提供了对于CALayer对象中的可变属性进行简单动画的操作。比如:位移、旋转、缩放、透明度、背景色等。

基础动画根据 keyPath来区分不同的动画,,

系统提供了多个类型,如: transform.scale(比例转换)、transform.scale.x、transform.scale.y、 transform.rotation(旋转) 、transform.rotation.x(绕x轴旋转)、transform.rotation.y(绕y轴旋转)、transform.rotation.z(绕z轴旋转)、opacity(透明度)、margin、backgroundColor(背景色)、cornerRadius(圆角)、borderWidth(边框宽)、bounds、contents、contentsRect、cornerRadius、frame、hidden、mask、masksToBounds、shadowColor(阴影色)、shadowOffset、shadowOpacity、shadowOpacity, 在使用时候, 需要根据具体的需求选择合适的.

?一个简单的动画效果

ABasicAnimation *moveAnimation = [CABasicAnimation animationWithKeyPath:@"position.y"]; moveAnimation.duration = 0.8;//动画时间 //动画起始值和终止值的设置 moveAnimation.fromValue = @(self.imageView.center.x); moveAnimation.toValue = @(self.imageView.center.x-30); //一个时间函数,表示它以怎么样的时间运行 moveAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; moveAnimation.repeatCount = HUGE_VALF; moveAnimation.repeatDuration = 2; moveAnimation.removedOnCompletion = NO; moveAnimation.fillMode = kCAFillModeForwards; //添加动画,后面有可以拿到这个动画的标识 [self.imageView.layer addAnimation:moveAnimation forKey:@"可以拿到这个动画的Key值"];

在addAnimation:forKey:方法中,也可以给这个动画设置一个键,可以在其他地方将其取出来,进行一些操作,比如删除。这也充分体现了kvc的灵活。

用到CALayer的 removeAnimationForKey:方法。

相关属性

keyPath:要改变的属性名称(传字符串)

fromValue:keyPath相应属性的初始值

toValue:keyPath相应属性的结束值

timingFunction:动画随时间运行的关系

2. 关键帧动画( CAKeyframeAnimation )

CAKeyframeAnimation和 CABasicAnimation都属于CAPropertyAnimatin的子类。不同的是 CABasicAnimation只能从一个数值(fromValue)变换成另一个数值(toValue),而 CAKeyframeAnimation则会使用一个数组(values) 保存一组关键帧, 也可以给定一个路径(path)制作动画。

  • values:存放关键帧(keyframe)的数组,动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧 .
  • path:可以设置一个 CGPathRef或 CGMutablePathRef,让层跟着路径移动. path只对 CALayer的 anchorPoint和 position起作用, 如果设置了path,那么values将被忽略.
  • keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0, keyTimes中的每一个时间值都对应 values中的每一帧.当 keyTimes没有设置的时候,各个关键帧的时间是根据 duration平分的。

?一个关键帧动画代码

CAKeyframeAnimation *animaiton = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"]; NSArray *rotationVelues = @[@(M_PI_4), @(-M_PI_4), @(M_PI_4)]; animaiton.values = rotationVelues; animation.rotationMode = kCAAnimationRotateAuto; //方向 animaiton.duration = 3.0f; animation.keyTimes = @[@0.2 ,@0.8 ,@1]; animation.path = bezierPath.CGPath; animaiton.repeatCount = HUGE_VALF; // #define HUGE_VALF 1e50f [self.imageView.layer addAnimation:animaiton forKey:nil];

属性说明

values:上述的NSArray对象。里面的元素称为“关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧

path:可以设置一个CGPathRef、CGMutablePathRef,让图层按照路径轨迹移动。path只对CALayer的anchorPoint和position起作用。如果设置了path,那么values将被忽略

keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧。如果没有设置keyTimes,各个关键帧的时间是平分的

bezierPath:贝赛尔曲线路径,为动画提供一个动画移动的路线。

UIBezierPath - 贝赛尔曲线

//创建路径 UIBezierPath *bezierPath = [[UIBezierPath alloc] init]; [bezierPath moveToPoint:CGPointMake(0, 450)]; [bezierPath addCurveToPoint:CGPointMake(370, 500) controlPoint1:CGPointMake(350, 200) controlPoint2:CGPointMake(300, 600)]; //一个曲线 //路径样式 CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.path = bezierPath.CGPath; shapeLayer.fillColor = [UIColor clearColor].CGColor; //填充色<默认黑色> shapeLayer.strokeColor = [UIColor blueColor].CGColor; //线色 shapeLayer.lineWidth = 2; [self.view.layer addSublayer:shapeLayer];

UIBezierPath创建一个路径,画出一条曲线。栗子中 起点(0,450)、终点(370,500) 和 (350,200)、(370,500)来个点来确定线的路径。

CAShapeLayer对上面的线进行属性上的设置。最后添加到一个layer上。

++CABasicAnimation可看做是只有2个关键帧的CAKeyframeAnimation++

3. 组动画( CAAnimationGroup )

CAAnimationGroup 是 CAAnimation 的子类,可以保存一组动画对象,可以保存基础动画、关键帧动画等,数组中所有动画对象可以同时并发运行, 也可以通过实践设置为串行连续动画.

属性说明

animations:用来保存一组动画对象的NSArray

CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; animationGroup.animations = @[animation,basicAnimation]; animationGroup.duration = 4; animation.repeatCount = 9; [_imageLayer addAnimation:animationGroup forKey:@"changeColor"];

4. 过渡动画( CATransition)

CATransition是 CAAnimation的子类,用于做过渡动画或者 转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。

过渡动画通过 type设置不同的动画效果, CATransition有多种过渡效果, 但其实 Apple官方的SDK只提供了四种:

  • fade 淡出 默认
  • moveIn 覆盖原图
  • push 推出
  • reveal 底部显示出来

但私有API提供了其他很多非常炫的过渡动画,如 cube(立方旋转)、suckEffect(吸走)、oglFlip(水平翻转 沿y轴)、 rippleEffect(滴水效果)、pageCurl(卷曲翻页 向上翻页)、pageUnCurl(卷曲翻页 向下翻页)、cameraIrisHollowOpen(相机开启)、cameraIrisHollowClose(相机关闭)等。

注: 因 Apple不提供维护,并且有可能造成你的app审核不通过, 所以不建议开发者们使用这些私有API.

CATransition *caTransition = [CATransition animation]; caTransition.duration = 0.5; caTransition.delegate = self; caTransition.timingFunction = [CAMediaTimingFunction functionWithName:@"easeInEaseOut"];//切换时间函数 caTransition.type = kCATransitionReveal;//动画切换风格 caTransition.subtype = kCATransitionFromLeft;//动画切换方向 // 子视图交换位置 //[self.parentView exchangeSubviewAtIndex:0 withSubviewAtIndex:1]; //动画在父视图 [self.parentView.layer addAnimation:caTransition forKey:@"Key"];

动画属性

type:动画过渡类型

subtype:动画过渡方向

startProgress:动画起点(在整体动画的百分比)

endProgress:动画终点(在整体动画的百分比)

/ 导航栏切换 UIViewController *viewCtr = [[UIViewController alloc] init]; viewCtr.view.backgroundColor = [UIColor redColor]; [self.navigationController pushViewController:viewCtr animated:NO];// 动画设置 NO 效果比较好 [self.navigationController.view.layer addAnimation:caTransition forKey:@"animation"];

CALayer

它有一些方法和属性来做动画和变换,和UIView最大的不同是CALayer不处理用户的交互事件。

UIView和CALayer的关系 - 平行关系

  • 每个UIView都有一个CALayer实例的图层属性。
  • 视图的职责就是创建并管理这个图层。
  • UIView和CALyer有着平行的层级关系,职责分离。

解释:为什么动画结束后返回原状态?

首先我们需要搞明白一点的是,layer动画运行的过程是怎样的?其实在我们给一个视图添加layer动画时,真正移动并不是我们的视图本身,而是 presentation layer 的一个缓存。动画开始时 presentation layer开始移动,原始layer隐藏,动画结束时,presentation layer从屏幕上移除,原始layer显示。这就解释了为什么我们的视图在动画结束后又回到了原来的状态,因为它根本就没动过。

这个同样也可以解释为什么在动画移动过程中,我们为何不能对其进行任何操作。

所以在我们完成layer动画之后,最好将我们的layer属性设置为我们最终状态的属性,然后将presentation layer 移除掉。

moveAnimation.removedOnCompletion = NO;

UIView中目前最常用的动画方法应该就是这个方法了

+(void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ nullable)(BOOL finished))completion ;

稍微复杂点的方法还是使用CALayer调用CAAnimation的API更为方便。

CAShapeLayer

CAShapeLayer是一个通过矢量图形而不是bitmap(位图)来挥之的图层子类。

你指定诸如颜色和线宽等属性,用CAPath来定义想绘制的图形,最后CAShapeLayer就自动渲染出来了。

优点

1.渲染快速。CAShapeLayer使用了硬件加速,绘制同一个图形会比用Core Graphics快很多。

2.高效使用内存。一个CAShapeLayer不需要像CALayer一样创建一个寄宿图,所以无论有多大,都不会占用大多的内存。

3.不会被图层边界裁剪掉。

4.不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图普通图层一样变得像素化。

创建一个CGPath

CAShapeLayer可以用来绘制所有能够通过CGPath来表示的形状。这个形状不一定要闭合,图层路径也不一定要不可破的,事实上你可以在一个图层上绘制好几个不同的形状。

你可以控制一些属性比如

lineWith(线宽,用点表示单位)、lineCap(线条结尾的样子)、和lineJoin(线条之间的结合点的样子)。

CAShapeLayer属性时CGPathRef类型,当时我们用 UIBezierPath 帮助类创建了图层路径,这样我们就不用考虑释放CGPath了。

贝赛尔曲线+CAShapeLayer

//创建圆角

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.imageView.frame.size.width, self.imageView.frame.size.height) cornerRadius:10];

CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];

maskLayer.path = maskPath.CGPath;

self.imageView.layer.mask=maskLayer;

/画图

- (void)viewDidLoad { [super viewDidLoad]; //create path UIBezierPath *path = [[UIBezierPath alloc] init]; [path moveToPoint:CGPointMake(175, 100)]; [path addArcWithCenter:CGPointMake(150, 100) radius:25 startAngle:0 endAngle: 2*M_PI clockwise:YES]; [path moveToPoint:CGPointMake(150, 125)]; [path addLineToPoint:CGPointMake(150, 175)]; [path addLineToPoint:CGPointMake(125, 225)]; [path moveToPoint:CGPointMake(150, 175)]; [path addLineToPoint:CGPointMake(175, 225)]; [path moveToPoint:CGPointMake(100, 150)]; [path addLineToPoint:CGPointMake(200, 150)]; //create shape layer CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.strokeColor = [UIColor redColor].CGColor; shapeLayer.fillColor = [UIColor clearColor].CGColor; shapeLayer.lineWidth = 5; shapeLayer.lineJoin = kCALineJoinRound; shapeLayer.lineCap = kCALineCapRound; shapeLayer.path = path.CGPath; //add it to our view [self.containerView.layer addSublayer:shapeLayer]; }

隐式动画

Core Animation基于一个假设,说屏幕上的任何东西都是可以(或可能) 做动画。

动画并不需要你在Core Animation中手动打开,相反需要明确的关闭,否者它一只存在。

当你改变CALayer的一个可以做动画的属性,它并不能在屏幕上体现出来。相反,它是从先前的值平滑过渡到新的值。这一切都是默认的行为,你不需要做额外的操作。

? 一个View上放一个CALayer,点击按钮改变layer的颜色<颜色是渐变过去的>

这就是隐式动画,之所以叫隐式动画是因为我们并没有指定任何的动画类型。我们仅仅改变了一个属性,然后Core Animation来决定如何并且何时去动画。

CATransaction 管理事务的类

+begin 入栈

+commit 出栈

+setAnimationDuration: 设置当前动画的时间

+AnimationDuration 获取动画时间

Core Animation在每个RUNLOOP周期中自动开始一次新的事物。

RUNROOP:iOS中负责收集用户输入,处理定时器或者网络事件并且重新绘制屏幕的东西。理解:涉及到任何主动或被动的操作改变都会唤醒run loop。

即使不显式的用[CATransaction begin]开始一次事务,任何在一次run loop循环中属性的改变都会被集中起来,然后做一次0.25秒的动画。

UIView中的layer属性 并不存在 隐形动画

总结

1.UIView关联的图层禁用了隐形动画,对这种图层做动画的唯一方法就是使用IUView的动画函数(而不是依赖CATransaction),或者继承UIView,并覆盖 -actionForLayer:forKey: 方法,或者直接创建一个显性动画。

2.对于独立的图层,我们可以通过实现图层的 -actionForLayer:forKey:委托方法,或者提供一个actions字典来控制隐形动画

显式动画

animationDidStop: finished:

代码demo

属性动画

基础动画

CABasicAnimation *animation = [CABasicAnimation animation]; animation.keyPath = @"backgroundColor";animation.toValue = (__bridge id)color.CGColor;animation.delegate = self;//apply animation to layer[self.colorLayer addAnimation:animation forKey:nil];

关键帧路径动画

CAKeyframeAnimation *animation = [CAKeyframeAnimation animation]; animation.keyPath = @"position"; animation.duration = 4.0; animation.path = bezierPath.CGPath; animation.rotationMode = kCAAnimationRotateAuto; [shipLayer addAnimation:animation forKey:nil];

动画组

CABasicAnimation和CAKeyframeAnimation仅仅是作用于单独的属性。

CAAnimationGroup可以把这些动画组合在一起 <CAAnimation 子类>

过渡

属性动画只对图层的可动画属性属性起作用

所以如果要改变一个不能动画的属性(比如图片),或者从层级关系中添加或者移除图层,属性动画将不起作用。

过渡:过渡并不像属性动画那样平滑的在两个值之间做动画,而是影响到整个图层的变化

。过渡动画首先展示之前的图层外观,然后通过一个交换过渡到新的外观。

CATransition:CAAnimation子类

CATransition有个type和subtype来识别变换效果,类型 NSString

type kCATransitionFade //默认 淡入淡出效果 kCATransitionMoveIn //新图片滑动进入,直接覆盖旧的图片 kCATransitionPush //边缘的一侧进来,把旧的图层从另一侧推出去 kCATransitionReveal //旧图片滑出去,来显示新图片 subtype kCATransitionFromRight kCATransitionFromLeft kCATransitionFromTop kCATransitionFromBottom

在想:控制器之间的过场动画是不是能用这个实现。

仿射变换

UIView可以通过transform属性做变换,transform是一个CGAffineTransform类型

CGAffineTransform 是用矩阵相乘的方法实现仿射变换

CGAffineTransformMakeRotation(CGFloat angle) 旋转

CGAffineTransformMakeScale(CGFloat sx, CGFloat sy) 缩放

CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty) 位移

CALayer 同样又个transform属性,transform的属性是CATransform3D

CALayer对应UIView的transform属性叫做affineTransform

混合变换

当操纵一个变换的时候,初始生成一个什么都不做的变换很重要-也就是创建一个CGAffineTransform类型的空值。<CGAffineTransformIdentity>

CGAffineTransformRotate(CGAffineTransform t, CGFloat angle) CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy) CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)

最后,如果需要混合两个已经存在的矩阵,就可以使用下面方法,在两个基础上新建一个变换

CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);

先缩小50%-再旋转30度-最后向右移动200像素

- (void)viewDidLoad { [super viewDidLoad]; //create a new transform CGAffineTransform transform = CGAffineTransformIdentity; //scale by 50%transform = CGAffineTransformScale(transform, 0.5, 0.5); //rotate by 30 degreestransform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30.0); //translate by 200 points transform = CGAffineTransformTranslate(transform, 200, 0);//apply transform to layer self.layerView.layer.affineTransform = transform; }

结果并不是预期的结果:这意味着变换的顺序会影响最终的结果,也就是说旋转之后的平移和平移之后的的旋转结果可能不同

CG - Core Graphics 框架 :严格意义上说是2D绘图API

CGAffineTransform 仅仅对2D变换有效

3D变换

CALayer的transform属性是CATransform3D类型,就能实现3D空间内移动或者旋转

CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)

CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz) CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)

想象x、y、z向旋转,缩放、位移的效果

绕Y轴做45度角的旋转

- (void)viewDidLoad { [super viewDidLoad]; //rotate the layer 45 degrees along the Y axis CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0); self.layerView.layer.transform = transform; }

sublayerTransform:CALayer的属性,可以对所有子视图操作

动画总结 Core Animation 贝赛尔曲线 显式动画 隐式动画相关推荐

  1. 核心动画(Core Animation)简介及常规用法

    Core Animation是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍,使用它需要先添加QuartzCore.framework和引入对应的框架<Quar ...

  2. boost::units模块实现测试显式和隐式单位转换

    boost::units模块实现测试显式和隐式单位转换 实现功能 C++实现代码 实现功能 boost::units模块实现测试显式和隐式单位转换 C++实现代码 #include <iostr ...

  3. 一阶常微分方程的数值解法(二阶显式、隐式 Adams 公式及 Milne 方法)

    一阶常微分方程的数值解法 这里我们介绍二阶显式.隐式 Adams 公式及 Milne 方法求解方程. 题目: 对初值问题 u ′ = u − t 2 , 0 ≤ t ≤ 1 , u ( 0 ) = 0 ...

  4. 隐式链接隐式链接_在木材上隐式标签选择

    隐式链接隐式链接 I assume every Android developer knows and uses the logging facilities provided by the Andr ...

  5. 游标(概念、优点、分类、静态游标的使用(显示游标(显示游标的属性、遍历显示游标、显示游标的FOR循环)、隐式游标(隐式游标的属性))、动态游标的使用、静态游标和动态游标的区别、更新或删除当前游标数据

    文章目录 游标 游标概念 游标优点 游标分类 静态游标的使用 显示游标 显示游标的属性 遍历显示游标 显示游标的FOR循环 接收显式游标数据的数据类型(普通变量.记录变量.集合变量) 通过游标更新.删 ...

  6. Core Animation 文档翻译 (第二篇)—核心动画基础要素

    前言 核心动画为我们APP内Views动画和其他可视化元素动画提供了综合性的实现体系.核心动画不是我们APP内Views的替代品,相反,它是一种结合Views来提供更好性能和支持Content动画的技 ...

  7. IOS动画(Core Animation)总结 (参考多方文章)

    一.简介 iOS 动画主要是指Core Animation框架.官方使用文档地址为:Core Animation Guide. Core Animation是IOS和OS X平台上负责图形渲染与动画的 ...

  8. iOS动画集锦(Core Animation)

    iOS 动画主要是指 Core Animation 框架, Core Animation是 iOS 和 OS X 平台上负责图形渲染与动画的基础框架.Core Animation 可以作用于动画视图或 ...

  9. Activity(活动)之Intent(意图)(显式与隐式)的使用

    Intent(意图)是Android中各个组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据.Intent一般可以用于启动活动.启动服务以及发送广播,发 ...

最新文章

  1. LeetCode: 104. Maximum Depth of Binary Tree
  2. Tungsten Fabric SDN — 社区贡献
  3. 如何添加sersync进程监控脚本
  4. Scala集合:reduce(化简)方法使用示例
  5. 【渝粤教育】国家开放大学2018年春季 0676-22T物流成本管理 参考试题
  6. app vue 真机运行_使用 HBuilder 将 Vue 项目打包成手机 App
  7. 如何在Angular 2项目中使用Bootstrap css库
  8. 解决docker push镜像到docker hub报没有权限
  9. android 分包粘包_Android Socket 发送与接收数据问题处理: 发送后的数据接收到总是粘包...
  10. APP 代码提交GitHub: 提交、合并与冲突解决 (终端操作语法)
  11. 在肉鸡上构建一个完美的虚拟主机
  12. 交叉编译Ghostscript
  13. 站群软件-免费站群软件
  14. 读季琦《创始人·手记》
  15. Android WebView播放视频并支持全屏
  16. Linux学习-文件操作和属性
  17. Gaussdb(DWS) 迁移工具GDS介绍及搭建使用
  18. Visual Studio滚动条设置
  19. baidu卫兵世界杯智能提速 打破运营商OTT端阻力
  20. 征服Java面试官!为什么@Value可以获取配置中心的值?感悟分享

热门文章

  1. 界外篇:返回前端订单列表中的订单详情为null,如何去除,如何为空
  2. 【转载】64位Linux下源码安装apache2
  3. 关于 passive
  4. 放苹果———动态规划中的划分数问题
  5. linux佳能打印机服务,技术|为 Linux 选择打印机
  6. java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed
  7. 金华计算机应用能力考试,金华2015年全国计算机应用能力考试安排
  8. 首席新媒体运营商学院黎想:社群运营的落地及方法!
  9. wan端口未连接怎么弄_WAN口未插网线怎么办? | 192路由网
  10. java web连接数据库