网络监测 断网 网速 ping

监测网络的Activity

public class NetActivity extends Activity {
	
	private static final String URL_DOWNLOAD = "http://f2.market.xiaomi.com/download/AppStore/08caf4c947ec5ded753141a9ca98e9691ad43e32d/com.tencent.wework.apk";
	
	public static final int MESSAGE_WHAT_REFUSH_CURRENT_SPEED = 1;//刷新当前网速
	public static final int MESSAGE_WHAT_REFUSH_AVE_SPEED = 2;//刷新平均网速
	public static final int MESSAGE_WHAT_REFUSH_RESET = 3;//重置
	
	private static final int DURATION_REFUSH_CURRENT_SPEED = 150;//过多久时间后刷新一次实时网速
	private static final int DURATION_REFUSH_AVE_SPEED = 300;//过多久时间后刷新一次平均网速
	private static final int DURATION_MAXCHECK = 5 * 1000;//整个测速过程允许的最大时间
	
	private TextView tv_type, tv_now_speed, tv_ave_speed;
	private DashboardView mDashboardView;
	private Button btn;
	private boolean flag = false;
	private Handler handler = new StaticUiHandler(this);
	private Disposable pingDisposable;
	
	private long lastTotalRxBytes = 0;
	private long lastTimeStamp = 0;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_net);
		
		tv_type = findViewById(R.id.connection_type);
		tv_now_speed = findViewById(R.id.now_speed);
		tv_ave_speed = findViewById(R.id.ave_speed);
		btn = findViewById(R.id.start_btn);
		mDashboardView = findViewById(R.id.dashboard_view);
		
		btn.setOnClickListener(arg0 -> checkNetSpeed());
		checkNetSpeed();
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		flag = false;
		handler.removeCallbacksAndMessages(null);
		if (pingDisposable != null) {
			pingDisposable.dispose();
		}
	}
	
	/**
	 * 监测网速
	 */
	private void checkNetSpeed() {
		flag = true;
		handler.postDelayed(this::reset, DURATION_MAXCHECK);
		checkNetType();
		
		btn.setText("正在ping百度ip...");
		btn.setEnabled(false);
	}
	
	/**
	 * 监测网络类型
	 */
	private void checkNetType() {
		//先ping一下百度看能不能通
		pingDisposable = Observable.create((ObservableOnSubscribe<Boolean>) emitter -> {
			boolean isSuccess = PingUtils.pingIpAddress(PingUtils.BAIDU_IP, 2);
			emitter.onNext(isSuccess);
		})
				.subscribeOn(Schedulers.io())
				.observeOn(AndroidSchedulers.mainThread())
				.subscribe(aBoolean -> {
					//通的话再检查是什么网,或者网络不可用
					btn.setText("正在检查网络...");
					if (aBoolean) {
						ConnectivityManager manager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
						if (manager != null) {
							NetworkInfo networkInfo = manager.getActiveNetworkInfo();
							if (networkInfo != null) {
								tv_type.setText(networkInfo.getTypeName());//网络类型
								btn.postDelayed(() -> btn.setText("正在测试网速..."), 500);
								new DownloadThread().start();
							} else {
								tv_type.setText("网络不可用");
								Toast.makeText(NetActivity.this, "网络不可用", Toast.LENGTH_SHORT).show();
								reset();
							}
						} else {
							tv_type.setText("网络不可用");
							Toast.makeText(NetActivity.this, "网络不可用", Toast.LENGTH_SHORT).show();
							reset();
						}
						//不同的话说明没网
					} else {
						tv_type.setText("无网络");
						Toast.makeText(NetActivity.this, "无网络", Toast.LENGTH_SHORT).show();
						reset();
					}
				});
	}
	
	/**
	 * 复原到初始状态
	 */
	private void reset() {
		if (flag) {
			flag = false;
			btn.setText("重新测试");
			btn.setEnabled(true);
			handler.removeCallbacksAndMessages(null);
		}
	}
	
	private void showCurrentNetSpeed() {
		long nowTimeStamp = System.currentTimeMillis();
		long totalRxBytes = TrafficStats.getTotalRxBytes();
		if (TrafficStats.getUidRxBytes(getApplicationInfo().uid) != TrafficStats.UNSUPPORTED
				&& totalRxBytes > lastTotalRxBytes && nowTimeStamp - lastTimeStamp > 0) {
			int speed = (int) ((totalRxBytes - lastTotalRxBytes) * 1000 / (nowTimeStamp - lastTimeStamp));//毫秒转换成秒
			lastTimeStamp = nowTimeStamp;
			lastTotalRxBytes = TrafficStats.getTotalRxBytes();
			
			tv_now_speed.setText(PingUtils.formatData(speed) + "/S");
			Log.i("bqt", "当前网速:" + PingUtils.formatData(speed) + "/S");
			
			mDashboardView.setRealTimeValue(speed * 1.0f / 1024 / 1024);
		}
	}
	
	//region  Handler
	
	static class StaticUiHandler extends Handler {
		private SoftReference<NetActivity> mSoftReference;
		
		StaticUiHandler(NetActivity activity) {
			mSoftReference = new SoftReference<>(activity);
		}
		
		@SuppressLint("SetTextI18n")
		@Override
		public void handleMessage(Message msg) {
			NetActivity activity = mSoftReference.get();
			if (activity != null && msg != null) {
				switch (msg.what) {
					case MESSAGE_WHAT_REFUSH_CURRENT_SPEED:
						activity.showCurrentNetSpeed();
						break;
					case MESSAGE_WHAT_REFUSH_AVE_SPEED:
						activity.tv_ave_speed.setText(PingUtils.formatData((int) msg.obj) + "/S");
						break;
					case MESSAGE_WHAT_REFUSH_RESET:
						activity.reset();
						break;
				}
			}
		}
	}
	//endregion
	
	//region  子线程
	
	/**
	 * 下载资源,下载过程中,根据已下载长度、总长度、时间计算实时网速
	 */
	class DownloadThread extends Thread {
		@Override
		public void run() {
			try {
				URLConnection connection = new URL(URL_DOWNLOAD).openConnection();
				InputStream inputStream = connection.getInputStream();
				Log.i("bqt", "总长度:" + PingUtils.formatData(connection.getContentLength()));
				
				long startTime = System.currentTimeMillis();//开始时间
				long usedTime;//已经使用的时长
				long tempTime1 = 0, tempTime2 = 0;
				
				int aveSpeed;//当前网速和平均网速
				int temLen, downloadLen = 0;//已下载的长度
				byte[] buf = new byte[1024];
				while ((temLen = inputStream.read(buf)) != -1 && flag) {
					usedTime = System.currentTimeMillis() - startTime;//毫秒
					downloadLen += temLen;
					
					//刷新当前网速
					if (System.currentTimeMillis() - tempTime1 > DURATION_REFUSH_CURRENT_SPEED) {
						tempTime1 = System.currentTimeMillis();
						handler.sendMessage(Message.obtain(handler, MESSAGE_WHAT_REFUSH_CURRENT_SPEED, 0));
					}
					
					//刷新平均网速
					if (System.currentTimeMillis() - tempTime2 > DURATION_REFUSH_AVE_SPEED) {
						if (usedTime > 0) {//防止分母为零时报ArithmeticException
							tempTime2 = System.currentTimeMillis();
							aveSpeed = (int) (downloadLen / usedTime) * 1000;//平均网速,单位秒
							handler.sendMessage(Message.obtain(handler, MESSAGE_WHAT_REFUSH_AVE_SPEED, aveSpeed));
							Log.i("bqt", "平均网速:" + PingUtils.formatData(aveSpeed) + "/S   已下载长度:" + PingUtils.formatData(downloadLen));
						}
					}
				}
				
				//重置
				handler.sendMessage(Message.obtain(handler, MESSAGE_WHAT_REFUSH_RESET));
				inputStream.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	//endregion
}
x
1
209
 
1
public class NetActivity extends Activity {
2
    
3
    private static final String URL_DOWNLOAD = "http://f2.market.xiaomi.com/download/AppStore/08caf4c947ec5ded753141a9ca98e9691ad43e32d/com.tencent.wework.apk";
4
    
5
    public static final int MESSAGE_WHAT_REFUSH_CURRENT_SPEED = 1;//刷新当前网速
6
    public static final int MESSAGE_WHAT_REFUSH_AVE_SPEED = 2;//刷新平均网速
7
    public static final int MESSAGE_WHAT_REFUSH_RESET = 3;//重置
8
    
9
    private static final int DURATION_REFUSH_CURRENT_SPEED = 150;//过多久时间后刷新一次实时网速
10
    private static final int DURATION_REFUSH_AVE_SPEED = 300;//过多久时间后刷新一次平均网速
11
    private static final int DURATION_MAXCHECK = 5 * 1000;//整个测速过程允许的最大时间
12
    
13
    private TextView tv_type, tv_now_speed, tv_ave_speed;
14
    private DashboardView mDashboardView;
15
    private Button btn;
16
    private boolean flag = false;
17
    private Handler handler = new StaticUiHandler(this);
18
    private Disposable pingDisposable;
19
    
20
    private long lastTotalRxBytes = 0;
21
    private long lastTimeStamp = 0;
22
    
23
    @Override
24
    protected void onCreate(Bundle savedInstanceState) {
25
        super.onCreate(savedInstanceState);
26
        setContentView(R.layout.activity_net);
27
        
28
        tv_type = findViewById(R.id.connection_type);
29
        tv_now_speed = findViewById(R.id.now_speed);
30
        tv_ave_speed = findViewById(R.id.ave_speed);
31
        btn = findViewById(R.id.start_btn);
32
        mDashboardView = findViewById(R.id.dashboard_view);
33
        
34
        btn.setOnClickListener(arg0 -> checkNetSpeed());
35
        checkNetSpeed();
36
    }
37
    
38
    @Override
39
    protected void onDestroy() {
40
        super.onDestroy();
41
        flag = false;
42
        handler.removeCallbacksAndMessages(null);
43
        if (pingDisposable != null) {
44
            pingDisposable.dispose();
45
        }
46
    }
47
    
48
    /**
49
     * 监测网速
50
     */
51
    private void checkNetSpeed() {
52
        flag = true;
53
        handler.postDelayed(this::reset, DURATION_MAXCHECK);
54
        checkNetType();
55
        
56
        btn.setText("正在ping百度ip...");
57
        btn.setEnabled(false);
58
    }
59
    
60
    /**
61
     * 监测网络类型
62
     */
63
    private void checkNetType() {
64
        //先ping一下百度看能不能通
65
        pingDisposable = Observable.create((ObservableOnSubscribe<Boolean>) emitter -> {
66
            boolean isSuccess = PingUtils.pingIpAddress(PingUtils.BAIDU_IP, 2);
67
            emitter.onNext(isSuccess);
68
        })
69
                .subscribeOn(Schedulers.io())
70
                .observeOn(AndroidSchedulers.mainThread())
71
                .subscribe(aBoolean -> {
72
                    //通的话再检查是什么网,或者网络不可用
73
                    btn.setText("正在检查网络...");
74
                    if (aBoolean) {
75
                        ConnectivityManager manager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
76
                        if (manager != null) {
77
                            NetworkInfo networkInfo = manager.getActiveNetworkInfo();
78
                            if (networkInfo != null) {
79
                                tv_type.setText(networkInfo.getTypeName());//网络类型
80
                                btn.postDelayed(() -> btn.setText("正在测试网速..."), 500);
81
                                new DownloadThread().start();
82
                            } else {
83
                                tv_type.setText("网络不可用");
84
                                Toast.makeText(NetActivity.this, "网络不可用", Toast.LENGTH_SHORT).show();
85
                                reset();
86
                            }
87
                        } else {
88
                            tv_type.setText("网络不可用");
89
                            Toast.makeText(NetActivity.this, "网络不可用", Toast.LENGTH_SHORT).show();
90
                            reset();
91
                        }
92
                        //不同的话说明没网
93
                    } else {
94
                        tv_type.setText("无网络");
95
                        Toast.makeText(NetActivity.this, "无网络", Toast.LENGTH_SHORT).show();
96
                        reset();
97
                    }
98
                });
99
    }
100
    
101
    /**
102
     * 复原到初始状态
103
     */
104
    private void reset() {
105
        if (flag) {
106
            flag = false;
107
            btn.setText("重新测试");
108
            btn.setEnabled(true);
109
            handler.removeCallbacksAndMessages(null);
110
        }
111
    }
112
    
113
    private void showCurrentNetSpeed() {
114
        long nowTimeStamp = System.currentTimeMillis();
115
        long totalRxBytes = TrafficStats.getTotalRxBytes();
116
        if (TrafficStats.getUidRxBytes(getApplicationInfo().uid) != TrafficStats.UNSUPPORTED
117
                && totalRxBytes > lastTotalRxBytes && nowTimeStamp - lastTimeStamp > 0) {
118
            int speed = (int) ((totalRxBytes - lastTotalRxBytes) * 1000 / (nowTimeStamp - lastTimeStamp));//毫秒转换成秒
119
            lastTimeStamp = nowTimeStamp;
120
            lastTotalRxBytes = TrafficStats.getTotalRxBytes();
121
            
122
            tv_now_speed.setText(PingUtils.formatData(speed) + "/S");
123
            Log.i("bqt", "当前网速:" + PingUtils.formatData(speed) + "/S");
124
            
125
            mDashboardView.setRealTimeValue(speed * 1.0f / 1024 / 1024);
126
        }
127
    }
128
    
129
    //region  Handler
130
    
131
    static class StaticUiHandler extends Handler {
132
        private SoftReference<NetActivity> mSoftReference;
133
        
134
        StaticUiHandler(NetActivity activity) {
135
            mSoftReference = new SoftReference<>(activity);
136
        }
137
        
138
        @SuppressLint("SetTextI18n")
139
        @Override
140
        public void handleMessage(Message msg) {
141
            NetActivity activity = mSoftReference.get();
142
            if (activity != null && msg != null) {
143
                switch (msg.what) {
144
                    case MESSAGE_WHAT_REFUSH_CURRENT_SPEED:
145
                        activity.showCurrentNetSpeed();
146
                        break;
147
                    case MESSAGE_WHAT_REFUSH_AVE_SPEED:
148
                        activity.tv_ave_speed.setText(PingUtils.formatData((int) msg.obj) + "/S");
149
                        break;
150
                    case MESSAGE_WHAT_REFUSH_RESET:
151
                        activity.reset();
152
                        break;
153
                }
154
            }
155
        }
156
    }
157
    //endregion
158
    
159
    //region  子线程
160
    
161
    /**
162
     * 下载资源,下载过程中,根据已下载长度、总长度、时间计算实时网速
163
     */
164
    class DownloadThread extends Thread {
165
        @Override
166
        public void run() {
167
            try {
168
                URLConnection connection = new URL(URL_DOWNLOAD).openConnection();
169
                InputStream inputStream = connection.getInputStream();
170
                Log.i("bqt", "总长度:" + PingUtils.formatData(connection.getContentLength()));
171
                
172
                long startTime = System.currentTimeMillis();//开始时间
173
                long usedTime;//已经使用的时长
174
                long tempTime1 = 0, tempTime2 = 0;
175
                
176
                int aveSpeed;//当前网速和平均网速
177
                int temLen, downloadLen = 0;//已下载的长度
178
                byte[] buf = new byte[1024];
179
                while ((temLen = inputStream.read(buf)) != -1 && flag) {
180
                    usedTime = System.currentTimeMillis() - startTime;//毫秒
181
                    downloadLen += temLen;
182
                    
183
                    //刷新当前网速
184
                    if (System.currentTimeMillis() - tempTime1 > DURATION_REFUSH_CURRENT_SPEED) {
185
                        tempTime1 = System.currentTimeMillis();
186
                        handler.sendMessage(Message.obtain(handler, MESSAGE_WHAT_REFUSH_CURRENT_SPEED, 0));
187
                    }
188
                    
189
                    //刷新平均网速
190
                    if (System.currentTimeMillis() - tempTime2 > DURATION_REFUSH_AVE_SPEED) {
191
                        if (usedTime > 0) {//防止分母为零时报ArithmeticException
192
                            tempTime2 = System.currentTimeMillis();
193
                            aveSpeed = (int) (downloadLen / usedTime) * 1000;//平均网速,单位秒
194
                            handler.sendMessage(Message.obtain(handler, MESSAGE_WHAT_REFUSH_AVE_SPEED, aveSpeed));
195
                            Log.i("bqt", "平均网速:" + PingUtils.formatData(aveSpeed) + "/S   已下载长度:" + PingUtils.formatData(downloadLen));
196
                        }
197
                    }
198
                }
199
                
200
                //重置
201
                handler.sendMessage(Message.obtain(handler, MESSAGE_WHAT_REFUSH_RESET));
202
                inputStream.close();
203
            } catch (Exception e) {
204
                e.printStackTrace();
205
            }
206
        }
207
    }
208
    //endregion
209
}

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:background="#800F"
              android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#000"
        android:orientation="horizontal"
        android:paddingBottom="10dp"
        android:paddingTop="10dp">

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:singleLine="true"
                android:text="连接方式"
                android:textColor="#ffffff"/>

            <TextView
                android:id="@+id/connection_type"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp"
                android:singleLine="true"
                android:textColor="#fff"
                tools:text="2M/S"/>
        </LinearLayout>

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="#dbdbdb"/>


        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="当前速度"
                android:textColor="#ffffff"/>

            <TextView
                android:id="@+id/now_speed"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp"
                android:textColor="#fff"
                tools:text="2M/S"/>
        </LinearLayout>

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="#dbdbdb"/>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="平均速度"
                android:textColor="#ffffff"/>

            <TextView
                android:id="@+id/ave_speed"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp"
                android:textColor="#fff"
                tools:text="2M/S"/>
        </LinearLayout>
    </LinearLayout>


    <rx.test.bqt.com.rxjavademo.net.DashboardView
        android:id="@+id/dashboard_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"/>

    <Button
        android:id="@+id/start_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="开始测试"/>
</LinearLayout>
112
 
1
<?xml version="1.0" encoding="utf-8"?>
2
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
              xmlns:tools="http://schemas.android.com/tools"
4
              android:layout_width="match_parent"
5
              android:layout_height="match_parent"
6
              android:background="#800F"
7
              android:orientation="vertical">
8
 
          
9
    <LinearLayout
10
        android:layout_width="match_parent"
11
        android:layout_height="wrap_content"
12
        android:background="#000"
13
        android:orientation="horizontal"
14
        android:paddingBottom="10dp"
15
        android:paddingTop="10dp">
16
 
          
17
        <LinearLayout
18
            android:layout_width="0dp"
19
            android:layout_height="wrap_content"
20
            android:layout_weight="1"
21
            android:gravity="center"
22
            android:orientation="vertical">
23
 
          
24
            <TextView
25
                android:layout_width="wrap_content"
26
                android:layout_height="wrap_content"
27
                android:singleLine="true"
28
                android:text="连接方式"
29
                android:textColor="#ffffff"/>
30
 
          
31
            <TextView
32
                android:id="@+id/connection_type"
33
                android:layout_width="wrap_content"
34
                android:layout_height="wrap_content"
35
                android:layout_marginTop="5dp"
36
                android:singleLine="true"
37
                android:textColor="#fff"
38
                tools:text="2M/S"/>
39
        </LinearLayout>
40
 
          
41
        <View
42
            android:layout_width="1dp"
43
            android:layout_height="match_parent"
44
            android:background="#dbdbdb"/>
45
 
          
46
 
          
47
        <LinearLayout
48
            android:layout_width="0dp"
49
            android:layout_height="wrap_content"
50
            android:layout_weight="1"
51
            android:gravity="center"
52
            android:orientation="vertical">
53
 
          
54
            <TextView
55
                android:layout_width="wrap_content"
56
                android:layout_height="wrap_content"
57
                android:text="当前速度"
58
                android:textColor="#ffffff"/>
59
 
          
60
            <TextView
61
                android:id="@+id/now_speed"
62
                android:layout_width="wrap_content"
63
                android:layout_height="wrap_content"
64
                android:layout_marginTop="5dp"
65
                android:textColor="#fff"
66
                tools:text="2M/S"/>
67
        </LinearLayout>
68
 
          
69
        <View
70
            android:layout_width="1dp"
71
            android:layout_height="match_parent"
72
            android:background="#dbdbdb"/>
73
 
          
74
        <LinearLayout
75
            android:layout_width="0dp"
76
            android:layout_height="wrap_content"
77
            android:layout_weight="1"
78
            android:gravity="center"
79
            android:orientation="vertical">
80
 
          
81
            <TextView
82
                android:layout_width="wrap_content"
83
                android:layout_height="wrap_content"
84
                android:text="平均速度"
85
                android:textColor="#ffffff"/>
86
 
          
87
            <TextView
88
                android:id="@+id/ave_speed"
89
                android:layout_width="wrap_content"
90
                android:layout_height="wrap_content"
91
                android:layout_marginTop="5dp"
92
                android:textColor="#fff"
93
                tools:text="2M/S"/>
94
        </LinearLayout>
95
    </LinearLayout>
96
 
          
97
 
          
98
    <rx.test.bqt.com.rxjavademo.net.DashboardView
99
        android:id="@+id/dashboard_view"
100
        android:layout_width="match_parent"
101
        android:layout_height="wrap_content"
102
        android:layout_gravity="center_horizontal"
103
        android:layout_marginTop="20dp"/>
104
 
          
105
    <Button
106
        android:id="@+id/start_btn"
107
        android:layout_width="match_parent"
108
        android:layout_height="wrap_content"
109
        android:layout_gravity="center_horizontal"
110
        android:text="开始测试"/>
111
</LinearLayout>

一些工具方法

public class PingUtils {
	public static final String BAIDU_IP = "119.75.217.109";
	public static final String APPLE_IP = "http://captive.apple.com";
	
	/**
	 * 格式化文件大小
	 *
	 * @param size 文件大小
	 */
	public static String formatData(long size) {
		DecimalFormat formater = new DecimalFormat("####.00");
		if (size < 1024) return size + "B";
		else if (size < Math.pow(1024, 2)) return formater.format(size * Math.pow(1024, -1)) + "KB";
		else if (size < Math.pow(1024, 3)) return formater.format(size * Math.pow(1024, -2)) + "MB";
		else if (size < Math.pow(1024, 4)) return formater.format(size * Math.pow(1024, -3)) + "GB";
		else return "";
	}
	
	/**
	 * ping返回true表示ping通,false表示没有ping通
	 * 所谓没有ping通是指数据包没有返回,也就是客户端没有及时收到ping的返回包因此返回false
	 * 但是要是网络不可用则ping的时候也会返回true,因为ping指定有成功结束,只是ping的返回包是失败的数据包而不是成功的数据包
	 * 所以准确的说返回true表示ping指定返回完成,false表示没收到ping的返回数据
	 * 以上方法是阻塞的,android系统默认等待ping的超时是10s,可以自定义超时时间
	 * 也不用担心方法一直被阻塞,如果ping超时就会自动返回,不必担心方法被阻塞导致无法运行下面的代码
	 * 网上的一些ping的实现说方法会被一直阻塞,实际上是他们ping的命令没写好,以及使用io被阻塞了
	 */
	public static boolean ping(String host, int pingCount) {
		Process process = null;
		BufferedReader successReader = null;
		//String command = "/system/bin/ping -c " + pingCount + " -w 5 " + host;//-c 是指ping的次数,-w是指超时时间,单位为s。
		String command = "ping -c " + pingCount + " " + host;
		boolean isSuccess = false;
		try {
			Log.i("bqt", "【start ping,command:" + command + "】");
			process = Runtime.getRuntime().exec(command);
			if (process == null) {
				Log.i("bqt", "【ping fail:process is null.】");
				return false;
			}
			successReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
			String line;
			while ((line = successReader.readLine()) != null) {
				Log.i("bqt", line);
			}
			int status = process.waitFor();
			if (status == 0) {
				Log.i("bqt", "【exec cmd success】");
				isSuccess = true;
			} else {
				Log.i("bqt", "【exec cmd fail】");
				isSuccess = false;
			}
			Log.i("bqt", "【exec finished】");
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			Log.i("bqt", "【ping exit】");
			if (process != null) {
				process.destroy();
			}
			if (successReader != null) {
				try {
					successReader.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return isSuccess;
	}
	
	/**
	 * 简易版
	 */
	public static boolean pingIpAddress(String host, int pingCount) {
		try {
			String command = "ping -c " + pingCount + " -w 5 " + host;//-c 是指ping的次数,-w是指超时时间,单位为s。
			Log.i("bqt", "【start ping,command:" + command + "】");
			Process process = Runtime.getRuntime().exec(command);
			//其中参数-c 1是指ping的次数为1次,-w是指超时时间单位为s。
			boolean isSuccess = process != null && process.waitFor() == 0;//status 等于0的时候表示网络可用,status等于2时表示当前网络不可用
			Log.i("bqt", "【end ping,isSuccess:" + isSuccess + "】");
			return isSuccess;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return false;
	}
}
94
 
1
public class PingUtils {
2
    public static final String BAIDU_IP = "119.75.217.109";
3
    public static final String APPLE_IP = "http://captive.apple.com";
4
    
5
    /**
6
     * 格式化文件大小
7
     *
8
     * @param size 文件大小
9
     */
10
    public static String formatData(long size) {
11
        DecimalFormat formater = new DecimalFormat("####.00");
12
        if (size < 1024) return size + "B";
13
        else if (size < Math.pow(1024, 2)) return formater.format(size * Math.pow(1024, -1)) + "KB";
14
        else if (size < Math.pow(1024, 3)) return formater.format(size * Math.pow(1024, -2)) + "MB";
15
        else if (size < Math.pow(1024, 4)) return formater.format(size * Math.pow(1024, -3)) + "GB";
16
        else return "";
17
    }
18
    
19
    /**
20
     * ping返回true表示ping通,false表示没有ping通
21
     * 所谓没有ping通是指数据包没有返回,也就是客户端没有及时收到ping的返回包因此返回false
22
     * 但是要是网络不可用则ping的时候也会返回true,因为ping指定有成功结束,只是ping的返回包是失败的数据包而不是成功的数据包
23
     * 所以准确的说返回true表示ping指定返回完成,false表示没收到ping的返回数据
24
     * 以上方法是阻塞的,android系统默认等待ping的超时是10s,可以自定义超时时间
25
     * 也不用担心方法一直被阻塞,如果ping超时就会自动返回,不必担心方法被阻塞导致无法运行下面的代码
26
     * 网上的一些ping的实现说方法会被一直阻塞,实际上是他们ping的命令没写好,以及使用io被阻塞了
27
     */
28
    public static boolean ping(String host, int pingCount) {
29
        Process process = null;
30
        BufferedReader successReader = null;
31
        //String command = "/system/bin/ping -c " + pingCount + " -w 5 " + host;//-c 是指ping的次数,-w是指超时时间,单位为s。
32
        String command = "ping -c " + pingCount + " " + host;
33
        boolean isSuccess = false;
34
        try {
35
            Log.i("bqt", "【start ping,command:" + command + "】");
36
            process = Runtime.getRuntime().exec(command);
37
            if (process == null) {
38
                Log.i("bqt", "【ping fail:process is null.】");
39
                return false;
40
            }
41
            successReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
42
            String line;
43
            while ((line = successReader.readLine()) != null) {
44
                Log.i("bqt", line);
45
            }
46
            int status = process.waitFor();
47
            if (status == 0) {
48
                Log.i("bqt", "【exec cmd success】");
49
                isSuccess = true;
50
            } else {
51
                Log.i("bqt", "【exec cmd fail】");
52
                isSuccess = false;
53
            }
54
            Log.i("bqt", "【exec finished】");
55
        } catch (IOException e) {
56
            e.printStackTrace();
57
        } catch (InterruptedException e) {
58
            e.printStackTrace();
59
        } finally {
60
            Log.i("bqt", "【ping exit】");
61
            if (process != null) {
62
                process.destroy();
63
            }
64
            if (successReader != null) {
65
                try {
66
                    successReader.close();
67
                } catch (IOException e) {
68
                    e.printStackTrace();
69
                }
70
            }
71
        }
72
        return isSuccess;
73
    }
74
    
75
    /**
76
     * 简易版
77
     */
78
    public static boolean pingIpAddress(String host, int pingCount) {
79
        try {
80
            String command = "ping -c " + pingCount + " -w 5 " + host;//-c 是指ping的次数,-w是指超时时间,单位为s。
81
            Log.i("bqt", "【start ping,command:" + command + "】");
82
            Process process = Runtime.getRuntime().exec(command);
83
            //其中参数-c 1是指ping的次数为1次,-w是指超时时间单位为s。
84
            boolean isSuccess = process != null && process.waitFor() == 0;//status 等于0的时候表示网络可用,status等于2时表示当前网络不可用
85
            Log.i("bqt", "【end ping,isSuccess:" + isSuccess + "】");
86
            return isSuccess;
87
        } catch (IOException e) {
88
            e.printStackTrace();
89
        } catch (InterruptedException e) {
90
            e.printStackTrace();
91
        }
92
        return false;
93
    }
94
}

一个自定义View

//https://github.com/woxingxiao/DashboardView/tree/master/app/src/main/java/com/xw/sample/dashboardviewdemo
public class DashboardView extends View {
	
	private static final long ANIMATION_DURATION = 130; //动画时长,注意最好不要在一个动画周期内设置多个动画
	private static final int M_START_ANGLE = 135; // 起始角度
	private static final int M_SWEEP_ANGLE = 270; // 绘制角度
	private static final float mMin = 0; // 最小值
	private static final float mMax = 5; // 最大值,对应5MB/S
	private static final int M_SECTION = 10; // 值域(mMax-mMin)等分份数
	private static final int M_PORTION = 10; // 一个mSection等分份数
	private static final String M_HEADER_TEXT = "MB/S"; // 表头
	private static final int SWEEP_ANGLE_COLOR = 0x880000ff;//圆弧颜色
	private static final int REAL_TIME_VALUE_COLOR = 0xffff0000;//实时读数的颜色
	private static final boolean IS_SHOW_VALUE = true; // 是否显示实时读数
	private int mRadius; // 扇形半径
	private float realTimeValue = mMin; // 实时读数
	private int mStrokeWidth; // 画笔宽度
	private int mLength1; // 长刻度的相对圆弧的长度
	private int mLength2; // 刻度读数顶部的相对圆弧的长度
	private int mPLRadius; // 指针长半径
	private int mPSRadius; // 指针短半径
	
	private int mPadding;
	private float mCenterX, mCenterY; // 圆心坐标
	private Paint mPaint;
	private RectF mRectFArc;
	private Path mPath;
	private RectF mRectFInnerArc;
	private Rect mRectText;
	private String[] mTexts;
	
	public DashboardView(Context context) {
		this(context, null);
	}
	
	public DashboardView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}
	
	public DashboardView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		
		init();
	}
	
	private void init() {
		mStrokeWidth = dp2px(1);
		mLength1 = dp2px(8) + mStrokeWidth;
		mLength2 = mLength1 + dp2px(2);
		mPSRadius = dp2px(10);
		
		mPaint = new Paint();
		mPaint.setAntiAlias(true);
		mPaint.setStrokeCap(Paint.Cap.ROUND);
		
		mRectFArc = new RectF();
		mPath = new Path();
		mRectFInnerArc = new RectF();
		mRectText = new Rect();
		
		mTexts = new String[M_SECTION + 1]; // 需要显示mSection + 1个刻度读数
		for (int i = 0; i < mTexts.length; i++) {
			float n = (mMax - mMin) / M_SECTION;
			mTexts[i] = String.valueOf(mMin + i * n);
		}
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		
		mPadding = Math.max(
				Math.max(getPaddingLeft(), getPaddingTop()),
				Math.max(getPaddingRight(), getPaddingBottom())
		);
		setPadding(mPadding, mPadding, mPadding, mPadding);
		
		int width = resolveSize(dp2px(200), widthMeasureSpec);
		mRadius = (width - mPadding * 2 - mStrokeWidth * 2) / 2;
		
		mPaint.setTextSize(sp2px(16));
		if (IS_SHOW_VALUE) { // 显示实时读数,View高度增加字体高度3倍
			mPaint.getTextBounds("0", 0, "0".length(), mRectText);
		} else {
			mPaint.getTextBounds("0", 0, 0, mRectText);
		}
		// 由半径+指针短半径+实时读数文字高度确定的高度
		int height1 = mRadius + mStrokeWidth * 2 + mPSRadius + mRectText.height() * 3;
		// 由起始角度确定的高度
		float[] point1 = getCoordinatePoint(mRadius, M_START_ANGLE);
		// 由结束角度确定的高度
		float[] point2 = getCoordinatePoint(mRadius, M_START_ANGLE + M_SWEEP_ANGLE);
		// 取最大值
		int max = (int) Math.max(height1, Math.max(point1[1] + mRadius + mStrokeWidth * 2, point2[1] + mRadius + mStrokeWidth * 2));
		setMeasuredDimension(width, max + getPaddingTop() + getPaddingBottom());
		
		mCenterX = mCenterY = getMeasuredWidth() / 2f;
		mRectFArc.set(getPaddingLeft() + mStrokeWidth,
				getPaddingTop() + mStrokeWidth,
				getMeasuredWidth() - getPaddingRight() - mStrokeWidth,
				getMeasuredWidth() - getPaddingBottom() - mStrokeWidth);
		
		mPaint.setTextSize(sp2px(10));
		mPaint.getTextBounds("0", 0, "0".length(), mRectText);
		mRectFInnerArc.set(getPaddingLeft() + mLength2 + mRectText.height(),
				getPaddingTop() + mLength2 + mRectText.height(),
				getMeasuredWidth() - getPaddingRight() - mLength2 - mRectText.height(),
				getMeasuredWidth() - getPaddingBottom() - mLength2 - mRectText.height());
		
		mPLRadius = mRadius - (mLength2 + mRectText.height() + dp2px(5));
	}
	
	private float tempRealTimeValue;
	
	private int dp2px(int dp) {
		return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
				Resources.getSystem().getDisplayMetrics());
	}
	
	private int sp2px(int sp) {
		return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
				Resources.getSystem().getDisplayMetrics());
	}
	
	public float[] getCoordinatePoint(int radius, float angle) {
		float[] point = new float[2];
		
		double arcAngle = Math.toRadians(angle); //将角度转换为弧度
		if (angle < 90) {
			point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
			point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
		} else if (angle == 90) {
			point[0] = mCenterX;
			point[1] = mCenterY + radius;
		} else if (angle > 90 && angle < 180) {
			arcAngle = Math.PI * (180 - angle) / 180.0;
			point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
			point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
		} else if (angle == 180) {
			point[0] = mCenterX - radius;
			point[1] = mCenterY;
		} else if (angle > 180 && angle < 270) {
			arcAngle = Math.PI * (angle - 180) / 180.0;
			point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
			point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
		} else if (angle == 270) {
			point[0] = mCenterX;
			point[1] = mCenterY - radius;
		} else {
			arcAngle = Math.PI * (360 - angle) / 180.0;
			point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
			point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
		}
		
		return point;
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		
		//画圆弧
		mPaint.setStyle(Paint.Style.STROKE);
		mPaint.setStrokeWidth(mStrokeWidth);
		mPaint.setColor(SWEEP_ANGLE_COLOR);
		canvas.drawArc(mRectFArc, M_START_ANGLE, M_SWEEP_ANGLE, false, mPaint);
		
		//画长刻度。画好起始角度的一条刻度后通过canvas绕着原点旋转来画剩下的长刻度
		double cos = Math.cos(Math.toRadians(M_START_ANGLE - 180));
		double sin = Math.sin(Math.toRadians(M_START_ANGLE - 180));
		float x0 = (float) (mPadding + mStrokeWidth + mRadius * (1 - cos));
		float y0 = (float) (mPadding + mStrokeWidth + mRadius * (1 - sin));
		float x1 = (float) (mPadding + mStrokeWidth + mRadius - (mRadius - mLength1) * cos);
		float y1 = (float) (mPadding + mStrokeWidth + mRadius - (mRadius - mLength1) * sin);
		
		canvas.save();
		canvas.drawLine(x0, y0, x1, y1, mPaint);
		float angle = M_SWEEP_ANGLE * 1f / M_SECTION;
		for (int i = 0; i < M_SECTION; i++) {
			canvas.rotate(angle, mCenterX, mCenterY);
			canvas.drawLine(x0, y0, x1, y1, mPaint);
		}
		canvas.restore();
		
		//画短刻度。同样采用canvas的旋转原理
		canvas.save();
		mPaint.setStrokeWidth(1);
		float x2 = (float) (mPadding + mStrokeWidth + mRadius - (mRadius - mLength1 / 2f) * cos);
		float y2 = (float) (mPadding + mStrokeWidth + mRadius - (mRadius - mLength1 / 2f) * sin);
		canvas.drawLine(x0, y0, x2, y2, mPaint);
		angle = M_SWEEP_ANGLE * 1f / (M_SECTION * M_PORTION);
		for (int i = 1; i < M_SECTION * M_PORTION; i++) {
			canvas.rotate(angle, mCenterX, mCenterY);
			if (i % M_PORTION == 0) { // 避免与长刻度画重合
				continue;
			}
			canvas.drawLine(x0, y0, x2, y2, mPaint);
		}
		canvas.restore();
		
		//画长刻度读数。添加一个圆弧path,文字沿着path绘制
		mPaint.setTextSize(sp2px(10));
		mPaint.setTextAlign(Paint.Align.LEFT);
		mPaint.setStyle(Paint.Style.FILL);
		for (int i = 0; i < mTexts.length; i++) {
			mPaint.getTextBounds(mTexts[i], 0, mTexts[i].length(), mRectText);
			// 粗略把文字的宽度视为圆心角2*θ对应的弧长,利用弧长公式得到θ,下面用于修正角度
			float degree = (float) (180 * mRectText.width() / 2 /
					(Math.PI * (mRadius - mLength2 - mRectText.height())));
			
			mPath.reset();
			mPath.addArc(mRectFInnerArc,
					M_START_ANGLE + i * (M_SWEEP_ANGLE / M_SECTION) - degree, // 正起始角度减去θ使文字居中对准长刻度
					M_SWEEP_ANGLE);
			canvas.drawTextOnPath(mTexts[i], mPath, 0, 0, mPaint);
		}
		
		//画表头。没有表头就不画
		if (!TextUtils.isEmpty(M_HEADER_TEXT)) {
			mPaint.setTextSize(sp2px(14));
			mPaint.setTextAlign(Paint.Align.CENTER);
			mPaint.getTextBounds(M_HEADER_TEXT, 0, M_HEADER_TEXT.length(), mRectText);
			canvas.drawText(M_HEADER_TEXT, mCenterX, mCenterY / 2f + mRectText.height(), mPaint);
		}
		
		//画指针
		float degree = M_START_ANGLE + M_SWEEP_ANGLE * (realTimeValue - mMin) / (mMax - mMin); // 指针与水平线夹角
		int d = dp2px(5); // 指针由两个等腰三角形构成,d为共底边长的一半
		mPath.reset();
		float[] p1 = getCoordinatePoint(d, degree - 90);
		mPath.moveTo(p1[0], p1[1]);
		float[] p2 = getCoordinatePoint(mPLRadius, degree);
		mPath.lineTo(p2[0], p2[1]);
		float[] p3 = getCoordinatePoint(d, degree + 90);
		mPath.lineTo(p3[0], p3[1]);
		float[] p4 = getCoordinatePoint(mPSRadius, degree - 180);
		mPath.lineTo(p4[0], p4[1]);
		mPath.close();
		canvas.drawPath(mPath, mPaint);
		
		//画指针围绕的镂空圆心
		mPaint.setColor(Color.WHITE);
		canvas.drawCircle(mCenterX, mCenterY, dp2px(2), mPaint);
		
		//画实时度数值
		if (IS_SHOW_VALUE) {
			mPaint.setTextSize(sp2px(18));
			mPaint.setTextAlign(Paint.Align.CENTER);
			mPaint.setColor(REAL_TIME_VALUE_COLOR);
			String value = String.format(Locale.getDefault(), "%.2f", realTimeValue) /*+ " MB/S"*/;
			mPaint.getTextBounds(value, 0, value.length(), mRectText);
			canvas.drawText(value, mCenterX, mCenterY + mPSRadius + mRectText.height() * 2, mPaint);
		}
	}
	
	public float getRealTimeValue() {
		return realTimeValue;
	}
	
	public void setRealTimeValue(final float value) {
		if (this.realTimeValue == value || value < mMin || value > mMax) {
			return;
		}
		this.tempRealTimeValue = this.realTimeValue;
		
		Animation anim = new Animation() {
			@Override
			protected void applyTransformation(float interpolatedTime, Transformation t) {
				//开始动画以后applyTransformation函数会自动调用,这里的interpolatedTime是 0-1 区间的变量,反映动画的进度
				super.applyTransformation(interpolatedTime, t);
				realTimeValue = tempRealTimeValue + interpolatedTime * (value - tempRealTimeValue);
				Log.i("bqt", "interpolatedTime=" + interpolatedTime + "   realTimeValue=" + realTimeValue);
				postInvalidate();
			}
		};
		anim.setDuration(ANIMATION_DURATION);
		this.startAnimation(anim);
		
		//不知为什么不能用下面这种动画,感觉应该也是可以的
		/*ObjectAnimator anim = ObjectAnimator.ofFloat(this, "realTimeValue", value)
				.setDuration(ANIMATION_DURATION);
		anim.addUpdateListener(animation -> {
			realTimeValue = (Float) animation.getAnimatedValue();
			Log.i("bqt", "realTimeValue=" + realTimeValue);
			postInvalidate();
		});
		anim.start();*/
	}
}
289
 
1
//https://github.com/woxingxiao/DashboardView/tree/master/app/src/main/java/com/xw/sample/dashboardviewdemo
2
public class DashboardView extends View {
3
    
4
    private static final long ANIMATION_DURATION = 130; //动画时长,注意最好不要在一个动画周期内设置多个动画
5
    private static final int M_START_ANGLE = 135; // 起始角度
6
    private static final int M_SWEEP_ANGLE = 270; // 绘制角度
7
    private static final float mMin = 0; // 最小值
8
    private static final float mMax = 5; // 最大值,对应5MB/S
9
    private static final int M_SECTION = 10; // 值域(mMax-mMin)等分份数
10
    private static final int M_PORTION = 10; // 一个mSection等分份数
11
    private static final String M_HEADER_TEXT = "MB/S"; // 表头
12
    private static final int SWEEP_ANGLE_COLOR = 0x880000ff;//圆弧颜色
13
    private static final int REAL_TIME_VALUE_COLOR = 0xffff0000;//实时读数的颜色
14
    private static final boolean IS_SHOW_VALUE = true; // 是否显示实时读数
15
    private int mRadius; // 扇形半径
16
    private float realTimeValue = mMin; // 实时读数
17
    private int mStrokeWidth; // 画笔宽度
18
    private int mLength1; // 长刻度的相对圆弧的长度
19
    private int mLength2; // 刻度读数顶部的相对圆弧的长度
20
    private int mPLRadius; // 指针长半径
21
    private int mPSRadius; // 指针短半径
22
    
23
    private int mPadding;
24
    private float mCenterX, mCenterY; // 圆心坐标
25
    private Paint mPaint;
26
    private RectF mRectFArc;
27
    private Path mPath;
28
    private RectF mRectFInnerArc;
29
    private Rect mRectText;
30
    private String[] mTexts;
31
    
32
    public DashboardView(Context context) {
33
        this(context, null);
34
    }
35
    
36
    public DashboardView(Context context, AttributeSet attrs) {
37
        this(context, attrs, 0);
38
    }
39
    
40
    public DashboardView(Context context, AttributeSet attrs, int defStyleAttr) {
41
        super(context, attrs, defStyleAttr);
42
        
43
        init();
44
    }
45
    
46
    private void init() {
47
        mStrokeWidth = dp2px(1);
48
        mLength1 = dp2px(8) + mStrokeWidth;
49
        mLength2 = mLength1 + dp2px(2);
50
        mPSRadius = dp2px(10);
51
        
52
        mPaint = new Paint();
53
        mPaint.setAntiAlias(true);
54
        mPaint.setStrokeCap(Paint.Cap.ROUND);
55
        
56
        mRectFArc = new RectF();
57
        mPath = new Path();
58
        mRectFInnerArc = new RectF();
59
        mRectText = new Rect();
60
        
61
        mTexts = new String[M_SECTION + 1]; // 需要显示mSection + 1个刻度读数
62
        for (int i = 0; i < mTexts.length; i++) {
63
            float n = (mMax - mMin) / M_SECTION;
64
            mTexts[i] = String.valueOf(mMin + i * n);
65
        }
66
    }
67
    
68
    @Override
69
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
70
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
71
        
72
        mPadding = Math.max(
73
                Math.max(getPaddingLeft(), getPaddingTop()),
74
                Math.max(getPaddingRight(), getPaddingBottom())
75
        );
76
        setPadding(mPadding, mPadding, mPadding, mPadding);
77
        
78
        int width = resolveSize(dp2px(200), widthMeasureSpec);
79
        mRadius = (width - mPadding * 2 - mStrokeWidth * 2) / 2;
80
        
81
        mPaint.setTextSize(sp2px(16));
82
        if (IS_SHOW_VALUE) { // 显示实时读数,View高度增加字体高度3倍
83
            mPaint.getTextBounds("0", 0, "0".length(), mRectText);
84
        } else {
85
            mPaint.getTextBounds("0", 0, 0, mRectText);
86
        }
87
        // 由半径+指针短半径+实时读数文字高度确定的高度
88
        int height1 = mRadius + mStrokeWidth * 2 + mPSRadius + mRectText.height() * 3;
89
        // 由起始角度确定的高度
90
        float[] point1 = getCoordinatePoint(mRadius, M_START_ANGLE);
91
        // 由结束角度确定的高度
92
        float[] point2 = getCoordinatePoint(mRadius, M_START_ANGLE + M_SWEEP_ANGLE);
93
        // 取最大值
94
        int max = (int) Math.max(height1, Math.max(point1[1] + mRadius + mStrokeWidth * 2, point2[1] + mRadius + mStrokeWidth * 2));
95
        setMeasuredDimension(width, max + getPaddingTop() + getPaddingBottom());
96
        
97
        mCenterX = mCenterY = getMeasuredWidth() / 2f;
98
        mRectFArc.set(getPaddingLeft() + mStrokeWidth,
99
                getPaddingTop() + mStrokeWidth,
100
                getMeasuredWidth() - getPaddingRight() - mStrokeWidth,
101
                getMeasuredWidth() - getPaddingBottom() - mStrokeWidth);
102
        
103
        mPaint.setTextSize(sp2px(10));
104
        mPaint.getTextBounds("0", 0, "0".length(), mRectText);
105
        mRectFInnerArc.set(getPaddingLeft() + mLength2 + mRectText.height(),
106
                getPaddingTop() + mLength2 + mRectText.height(),
107
                getMeasuredWidth() - getPaddingRight() - mLength2 - mRectText.height(),
108
                getMeasuredWidth() - getPaddingBottom() - mLength2 - mRectText.height());
109
        
110
        mPLRadius = mRadius - (mLength2 + mRectText.height() + dp2px(5));
111
    }
112
    
113
    private float tempRealTimeValue;
114
    
115
    private int dp2px(int dp) {
116
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
117
                Resources.getSystem().getDisplayMetrics());
118
    }
119
    
120
    private int sp2px(int sp) {
121
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
122
                Resources.getSystem().getDisplayMetrics());
123
    }
124
    
125
    public float[] getCoordinatePoint(int radius, float angle) {
126
        float[] point = new float[2];
127
        
128
        double arcAngle = Math.toRadians(angle); //将角度转换为弧度
129
        if (angle < 90) {
130
            point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
131
            point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
132
        } else if (angle == 90) {
133
            point[0] = mCenterX;
134
            point[1] = mCenterY + radius;
135
        } else if (angle > 90 && angle < 180) {
136
            arcAngle = Math.PI * (180 - angle) / 180.0;
137
            point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
138
            point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
139
        } else if (angle == 180) {
140
            point[0] = mCenterX - radius;
141
            point[1] = mCenterY;
142
        } else if (angle > 180 && angle < 270) {
143
            arcAngle = Math.PI * (angle - 180) / 180.0;
144
            point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
145
            point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
146
        } else if (angle == 270) {
147
            point[0] = mCenterX;
148
            point[1] = mCenterY - radius;
149
        } else {
150
            arcAngle = Math.PI * (360 - angle) / 180.0;
151
            point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
152
            point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
153
        }
154
        
155
        return point;
156
    }
157
    
158
    @Override
159
    protected void onDraw(Canvas canvas) {
160
        super.onDraw(canvas);
161
        
162
        //画圆弧
163
        mPaint.setStyle(Paint.Style.STROKE);
164
        mPaint.setStrokeWidth(mStrokeWidth);
165
        mPaint.setColor(SWEEP_ANGLE_COLOR);
166
        canvas.drawArc(mRectFArc, M_START_ANGLE, M_SWEEP_ANGLE, false, mPaint);
167
        
168
        //画长刻度。画好起始角度的一条刻度后通过canvas绕着原点旋转来画剩下的长刻度
169
        double cos = Math.cos(Math.toRadians(M_START_ANGLE - 180));
170
        double sin = Math.sin(Math.toRadians(M_START_ANGLE - 180));
171
        float x0 = (float) (mPadding + mStrokeWidth + mRadius * (1 - cos));
172
        float y0 = (float) (mPadding + mStrokeWidth + mRadius * (1 - sin));
173
        float x1 = (float) (mPadding + mStrokeWidth + mRadius - (mRadius - mLength1) * cos);
174
        float y1 = (float) (mPadding + mStrokeWidth + mRadius - (mRadius - mLength1) * sin);
175
        
176
        canvas.save();
177
        canvas.drawLine(x0, y0, x1, y1, mPaint);
178
        float angle = M_SWEEP_ANGLE * 1f / M_SECTION;
179
        for (int i = 0; i < M_SECTION; i++) {
180
            canvas.rotate(angle, mCenterX, mCenterY);
181
            canvas.drawLine(x0, y0, x1, y1, mPaint);
182
        }
183
        canvas.restore();
184
        
185
        //画短刻度。同样采用canvas的旋转原理
186
        canvas.save();
187
        mPaint.setStrokeWidth(1);
188
        float x2 = (float) (mPadding + mStrokeWidth + mRadius - (mRadius - mLength1 / 2f) * cos);
189
        float y2 = (float) (mPadding + mStrokeWidth + mRadius - (mRadius - mLength1 / 2f) * sin);
190
        canvas.drawLine(x0, y0, x2, y2, mPaint);
191
        angle = M_SWEEP_ANGLE * 1f / (M_SECTION * M_PORTION);
192
        for (int i = 1; i < M_SECTION * M_PORTION; i++) {
193
            canvas.rotate(angle, mCenterX, mCenterY);
194
            if (i % M_PORTION == 0) { // 避免与长刻度画重合
195
                continue;
196
            }
197
            canvas.drawLine(x0, y0, x2, y2, mPaint);
198
        }
199
        canvas.restore();
200
        
201
        //画长刻度读数。添加一个圆弧path,文字沿着path绘制
202
        mPaint.setTextSize(sp2px(10));
203
        mPaint.setTextAlign(Paint.Align.LEFT);
204
        mPaint.setStyle(Paint.Style.FILL);
205
        for (int i = 0; i < mTexts.length; i++) {
206
            mPaint.getTextBounds(mTexts[i], 0, mTexts[i].length(), mRectText);
207
            // 粗略把文字的宽度视为圆心角2*θ对应的弧长,利用弧长公式得到θ,下面用于修正角度
208
            float degree = (float) (180 * mRectText.width() / 2 /
209
                    (Math.PI * (mRadius - mLength2 - mRectText.height())));
210
            
211
            mPath.reset();
212
            mPath.addArc(mRectFInnerArc,
213
                    M_START_ANGLE + i * (M_SWEEP_ANGLE / M_SECTION) - degree, // 正起始角度减去θ使文字居中对准长刻度
214
                    M_SWEEP_ANGLE);
215
            canvas.drawTextOnPath(mTexts[i], mPath, 0, 0, mPaint);
216
        }
217
        
218
        //画表头。没有表头就不画
219
        if (!TextUtils.isEmpty(M_HEADER_TEXT)) {
220
            mPaint.setTextSize(sp2px(14));
221
            mPaint.setTextAlign(Paint.Align.CENTER);
222
            mPaint.getTextBounds(M_HEADER_TEXT, 0, M_HEADER_TEXT.length(), mRectText);
223
            canvas.drawText(M_HEADER_TEXT, mCenterX, mCenterY / 2f + mRectText.height(), mPaint);
224
        }
225
        
226
        //画指针
227
        float degree = M_START_ANGLE + M_SWEEP_ANGLE * (realTimeValue - mMin) / (mMax - mMin); // 指针与水平线夹角
228
        int d = dp2px(5); // 指针由两个等腰三角形构成,d为共底边长的一半
229
        mPath.reset();
230
        float[] p1 = getCoordinatePoint(d, degree - 90);
231
        mPath.moveTo(p1[0], p1[1]);
232
        float[] p2 = getCoordinatePoint(mPLRadius, degree);
233
        mPath.lineTo(p2[0], p2[1]);
234
        float[] p3 = getCoordinatePoint(d, degree + 90);
235
        mPath.lineTo(p3[0], p3[1]);
236
        float[] p4 = getCoordinatePoint(mPSRadius, degree - 180);
237
        mPath.lineTo(p4[0], p4[1]);
238
        mPath.close();
239
        canvas.drawPath(mPath, mPaint);
240
        
241
        //画指针围绕的镂空圆心
242
        mPaint.setColor(Color.WHITE);
243
        canvas.drawCircle(mCenterX, mCenterY, dp2px(2), mPaint);
244
        
245
        //画实时度数值
246
        if (IS_SHOW_VALUE) {
247
            mPaint.setTextSize(sp2px(18));
248
            mPaint.setTextAlign(Paint.Align.CENTER);
249
            mPaint.setColor(REAL_TIME_VALUE_COLOR);
250
            String value = String.format(Locale.getDefault(), "%.2f", realTimeValue) /*+ " MB/S"*/;
251
            mPaint.getTextBounds(value, 0, value.length(), mRectText);
252
            canvas.drawText(value, mCenterX, mCenterY + mPSRadius + mRectText.height() * 2, mPaint);
253
        }
254
    }
255
    
256
    public float getRealTimeValue() {
257
        return realTimeValue;
258
    }
259
    
260
    public void setRealTimeValue(final float value) {
261
        if (this.realTimeValue == value || value < mMin || value > mMax) {
262
            return;
263
        }
264
        this.tempRealTimeValue = this.realTimeValue;
265
        
266
        Animation anim = new Animation() {
267
            @Override
268
            protected void applyTransformation(float interpolatedTime, Transformation t) {
269
                //开始动画以后applyTransformation函数会自动调用,这里的interpolatedTime是 0-1 区间的变量,反映动画的进度
270
                super.applyTransformation(interpolatedTime, t);
271
                realTimeValue = tempRealTimeValue + interpolatedTime * (value - tempRealTimeValue);
272
                Log.i("bqt", "interpolatedTime=" + interpolatedTime + "   realTimeValue=" + realTimeValue);
273
                postInvalidate();
274
            }
275
        };
276
        anim.setDuration(ANIMATION_DURATION);
277
        this.startAnimation(anim);
278
        
279
        //不知为什么不能用下面这种动画,感觉应该也是可以的
280
        /*ObjectAnimator anim = ObjectAnimator.ofFloat(this, "realTimeValue", value)
281
                .setDuration(ANIMATION_DURATION);
282
        anim.addUpdateListener(animation -> {
283
            realTimeValue = (Float) animation.getAnimatedValue();
284
            Log.i("bqt", "realTimeValue=" + realTimeValue);
285
            postInvalidate();
286
        });
287
        anim.start();*/
288
    }
289
}
2018-4-8




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值