热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android通过Path实现搜索按钮和时钟复杂效果

这篇文章主要为大家详细介绍了Android通过Path实现搜索按钮和时钟复杂效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

在Android中复杂的图形的绘制绝大多数是通过path来实现,比如绘制一条曲线,然后让一个物体随着这个曲线运动,比如搜索按钮,比如一个简单时钟的实现:

那么什么是path呢! 

定义:path  就是路径,就是图形的路径的集合,它里边包含了路径里边的坐标点,等等的属性。我们可以获取到任意点的坐标,正切值。 

那么要获取Path上边所有点的坐标还需要用到一个类,PathMeasure; 

PathMesure:

PathMeasure是一个用来测量Path的类,主要有以下方法: 

构造方法

 

公共方法

 

可以看到,这个就等于是一个Path的一个工具类,方法很简单,那么就开始我们所要做的按钮跟时钟的开发吧 

(1)搜索按钮,首先上图:

 

要实现这个功能首先要把他分解开来做;
创建搜索按钮的path路径,然后创建外圈旋转的path, 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void initPath(){
   mPath_search = new Path();
   mPath_circle = new Path();
 
   mMeasure = new PathMeasure();
 
   // 注意,不要到360度,否则内部会自动优化,测量不能取到需要的数值
   RectF oval1 = new RectF(-50, -50, 50, 50);     // 放大镜圆环
   mPath_search.addArc(oval1, 45, 359.9f);
 
   RectF oval2 = new RectF(-100, -100, 100, 100);   // 外部圆环
   mPath_circle.addArc(oval2, 45, -359.9f);
 
   float[] pos = new float[2];
 
   mMeasure.setPath(mPath_circle, false);        // 放大镜把手的位置
   mMeasure.getPosTan(0, pos, null);
 
   mPath_search.lineTo(pos[0], pos[1]);         // 放大镜把手
 
   Log.i("TAG", "pos=" + pos[0] + ":" + pos[1]);
 
 }

我们要的效果就是点击搜索按钮的时候开始从按钮变为旋转,然后搜索结束以后变为搜索按钮。 

所以我们可以确定有四种状态: 

1
2
3
public  enum Seach_State{
  START,END,NONE,SEARCHING
}

  然后根据状态来进行动态绘制path,动态绘制path就要使用到PathMeasure测量当前path的坐标,然后进行绘制。 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private void drawPath(Canvas c) {
  c.translate(mWidth / 2, mHeight / 2);
  switch (mState){
 
    case NONE:
      c.drawPath(mPath_search,mPaint);
      break;
 
    case START:
      mMeasure.setPath(mPath_search,true);
      Path path = new Path();
      mMeasure.getSegment(mMeasure.getLength() * curretnAnimationValue,mMeasure.getLength(),path, true);
      c.drawPath(path,mPaint);
      break;
 
    case SEARCHING:
      mMeasure.setPath(mPath_circle,true);
      Path path_search = new Path();
      mMeasure.getSegment(mMeasure.getLength()*curretnAnimationValue -30,mMeasure.getLength()*curretnAnimationValue,path_search,true);
      c.drawPath(path_search,mPaint);
      break;
 
    case END:
      mMeasure.setPath(mPath_search,true);
      Path path_view = new Path();
 
      mMeasure.getSegment(0,mMeasure.getLength()*curretnAnimationValue,path_view,true);
      c.drawPath(path_view,mPaint);
      break;
  }
 
}

然后就是需要通过使用属性动画来返回当前该绘制的百分百,通过这个值来进行计算要绘制的path。
下边是整个代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
package com.duoku.platform.demo.canvaslibrary.attract.view;
 
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
 
/**
 * Created by chenpengfei_d on 2016/9/7.
 */
public class SearchView extends View {
  private Paint mPaint;
  private Context mContext;
  private Path mPath_circle;
  private Path mPath_search;
  private PathMeasure mMeasure;
  private ValueAnimator mValueAnimator_search;
  private long defaultduration=3000;
  private float curretnAnimationValue;
  private Seach_State mState = Seach_State.SEARCHING;
  public SearchView(Context context) {
    super(context);
    init(context);
  }
 
  public SearchView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
  }
 
  public SearchView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context);
  }
 
  public void init(Context context){
    this.mCOntext= context;
    initPaint();
    initPath();
    initAnimation();
 
  }
  public void initPaint(){
    mPaint = new Paint();
    mPaint.setDither(true);
    mPaint.setStrokeCap(Paint.Cap.ROUND);//设置笔头效果
    mPaint.setAntiAlias(true);
    mPaint.setColor(Color.RED);
    mPaint.setStrokeWidth(3);
    mPaint.setStyle(Paint.Style.STROKE);
  }
 
  public void initPath(){
    mPath_search = new Path();
    mPath_circle = new Path();
 
    mMeasure = new PathMeasure();
 
    // 注意,不要到360度,否则内部会自动优化,测量不能取到需要的数值
    RectF oval1 = new RectF(-50, -50, 50, 50);     // 放大镜圆环
    mPath_search.addArc(oval1, 45, 359.9f);
 
    RectF oval2 = new RectF(-100, -100, 100, 100);   // 外部圆环
    mPath_circle.addArc(oval2, 45, -359.9f);
 
    float[] pos = new float[2];
 
    mMeasure.setPath(mPath_circle, false);        // 放大镜把手的位置
    mMeasure.getPosTan(0, pos, null);
 
    mPath_search.lineTo(pos[0], pos[1]);         // 放大镜把手
 
    Log.i("TAG", "pos=" + pos[0] + ":" + pos[1]);
 
  }
 
  public void initAnimation(){
    mValueAnimator_search = ValueAnimator.ofFloat(0f,1.0f).setDuration(defaultduration);
 
    mValueAnimator_search.addUpdateListener(updateListener);
 
    mValueAnimator_search.addListener(animationListener);
  }
  private ValueAnimator.AnimatorUpdateListener updateListener = new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
      curretnAnimatiOnValue= (float) animation.getAnimatedValue();
      invalidate();
    }
  };
 
  private Animator.AnimatorListener animatiOnListener= new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animation) {
 
    }
 
    @Override
    public void onAnimationEnd(Animator animation) {
        if(mState ==Seach_State.START){
          setState(Seach_State.SEARCHING);
        }
    }
 
    @Override
    public void onAnimationCancel(Animator animation) {
 
    }
 
    @Override
    public void onAnimationRepeat(Animator animation) {
 
    }
  };
  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
 
    drawPath(canvas);
  }
  private int mWidth,mHeight;
  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mWidth = w;
    mHeight = h;
 
  }
 
  private void drawPath(Canvas c) {
    c.translate(mWidth / 2, mHeight / 2);
    switch (mState){
 
      case NONE:
        c.drawPath(mPath_search,mPaint);
        break;
 
      case START:
        mMeasure.setPath(mPath_search,true);
        Path path = new Path();
        mMeasure.getSegment(mMeasure.getLength() * curretnAnimationValue,mMeasure.getLength(),path, true);
        c.drawPath(path,mPaint);
        break;
 
      case SEARCHING:
        mMeasure.setPath(mPath_circle,true);
        Path path_search = new Path();
        mMeasure.getSegment(mMeasure.getLength()*curretnAnimationValue -30,mMeasure.getLength()*curretnAnimationValue,path_search,true);
        c.drawPath(path_search,mPaint);
        break;
 
      case END:
        mMeasure.setPath(mPath_search,true);
        Path path_view = new Path();
 
        mMeasure.getSegment(0,mMeasure.getLength()*curretnAnimationValue,path_view,true);
        c.drawPath(path_view,mPaint);
        break;
    }
 
  }
 
 
  public void setState(Seach_State state){
    this.mState = state;
    startSearch();
  }
 
  public void startSearch(){
    switch (mState){
      case START:
        mValueAnimator_search.setRepeatCount(0);
        break;
 
      case SEARCHING:
        mValueAnimator_search.setRepeatCount(ValueAnimator.INFINITE);
        mValueAnimator_search.setRepeatMode(ValueAnimator.REVERSE);
        break;
 
      case END:
        mValueAnimator_search.setRepeatCount(0);
        break;
    }
    mValueAnimator_search.start();
  }
  public  enum Seach_State{
    START,END,NONE,SEARCHING
  }
}

 (学习的点:path可以组合,可以把不同的path放置到一个path里边,然后进行统一的绘制) 

(2)时钟效果:

 

说一下时钟的思路啊,网上很多时钟都是通过Canvas绘制基本图形实现的,没有通过path来实现的,使用path实现是为了以后更加灵活的控制时钟的绘制效果,比如我们要让最外边的圆圈逆时针旋转,还比如在上边添加些小星星啥的,用path的话会更加灵活。 

时钟的实现分部分: 

1、创建外圈path路径 

2、创建刻度path路径,要区分整点,绘制时间点 

3、绘制指针,(这个使用的是canvas绘制的线段,也可以使用Path,可以自己测试) 

需要计算当前时针,分针,秒针的角度,然后进行绘制 

整体代码: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
package com.duoku.platform.demo.canvaslibrary.attract.view;
 
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
 
import java.util.Calendar;
 
/**
 * Created by chenpengfei_d on 2016/9/8.
 */
public class TimeView extends View {
  private Paint mPaint,mPaint_time;
  private Paint mPaint_h,mPaint_m,mPaint_s;
  private Path mPath_Circle;
  private Path mPath_Circle_h;
  private Path mPath_Circle_m;
  private Path mPath_h,mPath_m,mPath_s;
  private Path mPath_duration;
 
  private PathMeasure mMeasure;
  private PathMeasure mMeasure_h;
  private PathMeasure mMeasure_m;
  private Handler mHandler = new Handler();
  private Runnable clockRunnable;
  private boolean isRunning;
  public TimeView(Context context) {
    super(context);
    init();
  }
 
  public TimeView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
  }
 
  public TimeView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
  }
  int t = 3;
  public void init(){
    //初始化画笔
    mPaint = new Paint();
    mPaint.setDither(true);
    mPaint.setAntiAlias(true);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(2);
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setColor(Color.RED);
    mPaint_time = new Paint();
    mPaint_time.setDither(true);
    mPaint_time.setAntiAlias(true);
    mPaint_time.setStyle(Paint.Style.STROKE);
    mPaint_time.setStrokeWidth(2);
    mPaint_time.setTextSize(15);
    mPaint_time.setStrokeCap(Paint.Cap.ROUND);
    mPaint_time.setStrokeJoin(Paint.Join.ROUND);
    mPaint_time.setColor(Color.RED);
 
    mPaint_h = new Paint();
    mPaint_h.setDither(true);
    mPaint_h.setAntiAlias(true);
    mPaint_h.setStyle(Paint.Style.STROKE);
    mPaint_h.setStrokeWidth(6);
    mPaint_h.setTextSize(15);
    mPaint_h.setStrokeCap(Paint.Cap.ROUND);
    mPaint_h.setStrokeJoin(Paint.Join.ROUND);
    mPaint_h.setColor(Color.RED);
 
    mPaint_m = new Paint();
    mPaint_m.setDither(true);
    mPaint_m.setAntiAlias(true);
    mPaint_m.setStyle(Paint.Style.STROKE);
    mPaint_m.setStrokeWidth(4);
    mPaint_m.setTextSize(15);
    mPaint_m.setStrokeCap(Paint.Cap.ROUND);
    mPaint_m.setStrokeJoin(Paint.Join.ROUND);
    mPaint_m.setColor(Color.RED);
 
    mPaint_s = new Paint();
    mPaint_s.setDither(true);
    mPaint_s.setAntiAlias(true);
    mPaint_s.setStyle(Paint.Style.STROKE);
    mPaint_s.setStrokeWidth(2);
    mPaint_s.setTextSize(15);
    mPaint_s.setStrokeCap(Paint.Cap.ROUND);
    mPaint_s.setStrokeJoin(Paint.Join.ROUND);
    mPaint_s.setColor(Color.RED);
    //初始化刻度
    mPath_Circle = new Path();
    mPath_Circle.addCircle(0,0,250, Path.Direction.CCW);
    mPath_Circle_h = new Path();
    mPath_Circle_h.addCircle(0,0,220, Path.Direction.CCW);
    mPath_Circle_m = new Path();
    mPath_Circle_m.addCircle(0,0,235, Path.Direction.CCW);
    //初始化PathMeasure测量path坐标,
    mMeasure = new PathMeasure();
    mMeasure.setPath(mPath_Circle,true);
    mMeasure_h = new PathMeasure();
    mMeasure_h.setPath(mPath_Circle_h,true);
    mMeasure_m = new PathMeasure();
    mMeasure_m.setPath(mPath_Circle_m,true);
    //获取刻度path
    mPath_duration = new Path();
    for (int i = 60; i>0 ;i --){
      Path path = new Path();
      float pos [] = new float[2];
      float tan [] = new float[2];
      float pos2 [] = new float[2];
      float tan2 [] = new float[2];
      float pos3 [] = new float[2];
      float tan3 [] = new float[2];
      mMeasure.getPosTan(mMeasure.getLength()*i/60,pos,tan);
      mMeasure_h.getPosTan(mMeasure_h.getLength()*i/60,pos2,tan2);
      mMeasure_m.getPosTan(mMeasure_m.getLength()*i/60,pos3,tan3);
 
      float x = pos[0];
      float y = pos[1];
      float x2 = pos2[0];
      float y2 = pos2[1];
      float x3 = pos3[0];
      float y3 = pos3[1];
      path.moveTo(x , y);
 
      if(i% 5 ==0){
        path.lineTo(x2,y2);
        if(t>12){
          t = t-12;
        }
        String time = t++ +"";
        Path path_time = new Path();
        mMeasure_h.getPosTan(mMeasure_h.getLength()*(i-1)/60,pos2,tan2);
        mPaint.getTextPath(time,0,time.length(),(x2- (x2/15)),y2-(y2/15),path_time);
        path.close();
        path.addPath(path_time);
      }else{
        path.lineTo(x3,y3);
      }
 
 
      mPath_duration.addPath(path);
      clockRunnable = new Runnable() {//里面做的事情就是每隔一秒,刷新一次界面
        @Override
        public void run() {
          //线程中刷新界面
          postInvalidate();
          mHandler.postDelayed(this, 1000);
        }
      };
    }
 
    mPath_h = new Path();
    mPath_h.rLineTo(50,30);
 
    mPath_m = new Path();
    mPath_m.rLineTo(80,80);
 
    mPath_s = new Path();
    mPath_s.rLineTo(130,50);
  }
  private int mWidth,mHeight;
  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mWidth = w;
    mHeight = h;
  }
 
  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if(!isRunning){
      isRunning = true;
      mHandler.postDelayed(clockRunnable,1000);
    }else{
      canvas.translate(mWidth/2,mHeight/2);
 
      canvas.drawPath(mPath_Circle,mPaint);
      canvas.save();
      canvas.drawPath(mPath_duration,mPaint_time);
 
      canvas.drawPoint(0,0,mPaint_time);
 
      drawClockPoint(canvas);
    }
 
 
 
  }
  private Calendar cal;
  private int hour;
  private int min;
  private int second;
  private float hourAngle,minAngle,secAngle;
  /**
   * 绘制三个指针
   * @param canvas
   */
  private void drawClockPoint(Canvas canvas) {
    cal = Calendar.getInstance();
    hour = cal.get(Calendar.HOUR);//Calendar.HOUR获取的是12小时制,Calendar.HOUR_OF_DAY获取的是24小时制
    min = cal.get(Calendar.MINUTE);
    secOnd= cal.get(Calendar.SECOND);
    //计算时分秒指针各自需要偏移的角度
    hourAngle = (float)hour / 12 * 360 + (float)min / 60 * (360 / 12);//360/12是指每个数字之间的角度
    minAngle = (float)min / 60 * 360;
    secAngle = (float)second / 60 * 360;
    //下面将时、分、秒指针按照各自的偏移角度进行旋转,每次旋转前要先保存canvas的原始状态
    canvas.save();
    canvas.rotate(hourAngle,0, 0);
    canvas.drawLine(0, 0, mWidth/6, getHeight() / 6 - 65, mPaint_h);//时针长度设置为65
 
    canvas.restore();
    canvas.save();
    canvas.rotate(minAngle,0, 0);
    canvas.drawLine(0, 0, mWidth/6, getHeight() / 6 - 90 , mPaint_m);//分针长度设置为90
 
    canvas.restore();
    canvas.save();
    canvas.rotate(secAngle,0, 0);
    canvas.drawLine(0, 0, mWidth/6, getHeight() / 6 - 110 , mPaint_s);//秒针长度设置为110
 
    canvas.restore();
  }
}

这其实还不算特别复杂的动画,也许你有啥好的想法,可以自己通过Path + 属性动画来实现更好看的效果; 

比如星空的效果,比如动态绘制文字 + 路径实现类似ppt中播放的一些特效,比如电子书的自动翻页。 

(3)下边再介绍一个知识,就是svg: 

svg是什么东西呢? 

他的学名叫做可缩放矢量图形,是基于可扩展标记语言(标准通用标记语言的子集),用于描述二维矢量图形的一种图形格式。 

这种格式的图形式可以加载到Android的Path里边。 

既然可以加载到Path里边,那么是不是就可以实现更复杂的效果呢,下边看图:(明天再写了)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • Android 九宫格布局详解及实现:人人网应用示例
    本文深入探讨了人人网Android应用中独特的九宫格布局设计,解析其背后的GridView实现原理,并提供详细的代码示例。这种布局方式不仅美观大方,而且在现代Android应用中较为少见,值得开发者借鉴。 ... [详细]
  • 深入解析Android自定义View面试题
    本文探讨了Android Launcher开发中自定义View的重要性,并通过一道经典的面试题,帮助开发者更好地理解自定义View的实现细节。文章不仅涵盖了基础知识,还提供了实际操作建议。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文将详细介绍如何使用剪映应用中的镜像功能,帮助用户轻松实现视频的镜像效果。通过简单的步骤,您可以快速掌握这一实用技巧。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • 如何在WPS Office for Mac中调整Word文档的文字排列方向
    本文将详细介绍如何使用最新版WPS Office for Mac调整Word文档中的文字排列方向。通过这些步骤,用户可以轻松更改文本的水平或垂直排列方式,以满足不同的排版需求。 ... [详细]
  • 本文总结了在使用Ionic 5进行Android平台APK打包时遇到的问题,特别是针对QRScanner插件的改造。通过详细分析和提供具体的解决方法,帮助开发者顺利打包并优化应用性能。 ... [详细]
  • 理解存储器的层次结构有助于程序员优化程序性能,通过合理安排数据在不同层级的存储位置,提升CPU的数据访问速度。本文详细探讨了静态随机访问存储器(SRAM)和动态随机访问存储器(DRAM)的工作原理及其应用场景,并介绍了存储器模块中的数据存取过程及局部性原理。 ... [详细]
  • 360SRC安全应急响应:从漏洞提交到修复的全过程
    本文详细介绍了360SRC平台处理一起关键安全事件的过程,涵盖从漏洞提交、验证、排查到最终修复的各个环节。通过这一案例,展示了360在安全应急响应方面的专业能力和严谨态度。 ... [详细]
  • 几何画板展示电场线与等势面的交互关系
    几何画板是一款功能强大的物理教学软件,具备丰富的绘图和度量工具。它不仅能够模拟物理实验过程,还能通过定量分析揭示物理现象背后的规律,尤其适用于难以在实际实验中展示的内容。本文将介绍如何使用几何画板演示电场线与等势面之间的关系。 ... [详细]
author-avatar
-依小冷_217
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有