ChartNetworkSeriesView.java revision f54f435f1f3215b39798c671fc64344d1867de4e
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.settings.widget; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.graphics.Canvas; 22import android.graphics.Color; 23import android.graphics.Paint; 24import android.graphics.Paint.Style; 25import android.graphics.Path; 26import android.graphics.RectF; 27import android.net.NetworkStatsHistory; 28import android.util.AttributeSet; 29import android.util.Log; 30import android.view.View; 31 32import com.android.settings.R; 33import com.google.common.base.Preconditions; 34 35/** 36 * {@link NetworkStatsHistory} series to render inside a {@link ChartView}, 37 * using {@link ChartAxis} to map into screen coordinates. 38 */ 39public class ChartNetworkSeriesView extends View { 40 private static final String TAG = "ChartNetworkSeriesView"; 41 private static final boolean LOGD = true; 42 43 private ChartAxis mHoriz; 44 private ChartAxis mVert; 45 46 private Paint mPaintStroke; 47 private Paint mPaintFill; 48 private Paint mPaintFillSecondary; 49 50 private NetworkStatsHistory mStats; 51 52 private Path mPathStroke; 53 private Path mPathFill; 54 55 private long mPrimaryLeft; 56 private long mPrimaryRight; 57 58 public ChartNetworkSeriesView(Context context) { 59 this(context, null, 0); 60 } 61 62 public ChartNetworkSeriesView(Context context, AttributeSet attrs) { 63 this(context, attrs, 0); 64 } 65 66 public ChartNetworkSeriesView(Context context, AttributeSet attrs, int defStyle) { 67 super(context, attrs, defStyle); 68 69 final TypedArray a = context.obtainStyledAttributes( 70 attrs, R.styleable.ChartNetworkSeriesView, defStyle, 0); 71 72 final int stroke = a.getColor(R.styleable.ChartNetworkSeriesView_strokeColor, Color.RED); 73 final int fill = a.getColor(R.styleable.ChartNetworkSeriesView_fillColor, Color.RED); 74 final int fillSecondary = a.getColor( 75 R.styleable.ChartNetworkSeriesView_fillColorSecondary, Color.RED); 76 77 setChartColor(stroke, fill, fillSecondary); 78 setWillNotDraw(false); 79 80 a.recycle(); 81 82 mPathStroke = new Path(); 83 mPathFill = new Path(); 84 } 85 86 void init(ChartAxis horiz, ChartAxis vert) { 87 mHoriz = Preconditions.checkNotNull(horiz, "missing horiz"); 88 mVert = Preconditions.checkNotNull(vert, "missing vert"); 89 } 90 91 public void setChartColor(int stroke, int fill, int fillSecondary) { 92 mPaintStroke = new Paint(); 93 mPaintStroke.setStrokeWidth(6.0f); 94 mPaintStroke.setColor(stroke); 95 mPaintStroke.setStyle(Style.STROKE); 96 mPaintStroke.setAntiAlias(true); 97 98 mPaintFill = new Paint(); 99 mPaintFill.setColor(fill); 100 mPaintFill.setStyle(Style.FILL); 101 mPaintFill.setAntiAlias(true); 102 103 mPaintFillSecondary = new Paint(); 104 mPaintFillSecondary.setColor(fillSecondary); 105 mPaintFillSecondary.setStyle(Style.FILL); 106 mPaintFillSecondary.setAntiAlias(true); 107 } 108 109 public void bindNetworkStats(NetworkStatsHistory stats) { 110 mStats = stats; 111 112 mPathStroke.reset(); 113 mPathFill.reset(); 114 invalidate(); 115 } 116 117 /** 118 * Set the range to paint with {@link #mPaintFill}, leaving the remaining 119 * area to be painted with {@link #mPaintFillSecondary}. 120 */ 121 public void setPrimaryRange(long left, long right) { 122 mPrimaryLeft = left; 123 mPrimaryRight = right; 124 invalidate(); 125 } 126 127 @Override 128 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 129 generatePath(); 130 } 131 132 /** 133 * Erase any existing {@link Path} and generate series outline based on 134 * currently bound {@link NetworkStatsHistory} data. 135 */ 136 public void generatePath() { 137 if (LOGD) Log.d(TAG, "generatePath()"); 138 139 mPathStroke.reset(); 140 mPathFill.reset(); 141 142 // bail when not enough stats to render 143 if (mStats == null || mStats.bucketCount < 2) return; 144 145 final int width = getWidth(); 146 final int height = getHeight(); 147 148 boolean started = false; 149 float firstX = 0; 150 float lastX = 0; 151 float lastY = 0; 152 153 // TODO: count fractional data from first bucket crossing start; 154 // currently it only accepts first full bucket. 155 156 long totalData = 0; 157 158 for (int i = 0; i < mStats.bucketCount; i++) { 159 final float x = mHoriz.convertToPoint(mStats.bucketStart[i]); 160 final float y = mVert.convertToPoint(totalData); 161 162 // skip until we find first stats on screen 163 if (i > 0 && !started && x > 0) { 164 mPathStroke.moveTo(lastX, lastY); 165 mPathFill.moveTo(lastX, lastY); 166 started = true; 167 firstX = x; 168 } 169 170 if (started) { 171 mPathStroke.lineTo(x, y); 172 mPathFill.lineTo(x, y); 173 totalData += mStats.rx[i] + mStats.tx[i]; 174 } 175 176 // skip if beyond view 177 if (x > width) break; 178 179 lastX = x; 180 lastY = y; 181 } 182 183 if (LOGD) { 184 final RectF bounds = new RectF(); 185 mPathFill.computeBounds(bounds, true); 186 Log.d(TAG, "onLayout() rendered with bounds=" + bounds.toString() + " and totalData=" 187 + totalData); 188 } 189 190 // drop to bottom of graph from current location 191 mPathFill.lineTo(lastX, height); 192 mPathFill.lineTo(firstX, height); 193 } 194 195 @Override 196 protected void onDraw(Canvas canvas) { 197 int save; 198 199 final float primaryLeftPoint = mHoriz.convertToPoint(mPrimaryLeft); 200 final float primaryRightPoint = mHoriz.convertToPoint(mPrimaryRight); 201 202 save = canvas.save(); 203 canvas.clipRect(0, 0, primaryLeftPoint, getHeight()); 204 canvas.drawPath(mPathFill, mPaintFillSecondary); 205 canvas.restoreToCount(save); 206 207 save = canvas.save(); 208 canvas.clipRect(primaryRightPoint, 0, getWidth(), getHeight()); 209 canvas.drawPath(mPathFill, mPaintFillSecondary); 210 canvas.restoreToCount(save); 211 212 save = canvas.save(); 213 canvas.clipRect(primaryLeftPoint, 0, primaryRightPoint, getHeight()); 214 canvas.drawPath(mPathFill, mPaintFill); 215 canvas.drawPath(mPathStroke, mPaintStroke); 216 canvas.restoreToCount(save); 217 218 } 219} 220