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 17 package com.android.settings.widget; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 21 import android.content.Context; 22 import android.content.res.TypedArray; 23 import android.graphics.Rect; 24 import android.util.AttributeSet; 25 import android.view.Gravity; 26 import android.view.View; 27 import android.view.ViewDebug; 28 import android.widget.FrameLayout; 29 30 import com.android.settings.R; 31 32 /** 33 * Container for two-dimensional chart, drawn with a combination of 34 * {@link ChartGridView}, {@link ChartNetworkSeriesView} and {@link ChartSweepView} 35 * children. The entire chart uses {@link ChartAxis} to map between raw values 36 * and screen coordinates. 37 */ 38 public class ChartView extends FrameLayout { 39 // TODO: extend something that supports two-dimensional scrolling 40 41 private static final int SWEEP_GRAVITY = Gravity.TOP | Gravity.LEFT; 42 43 ChartAxis mHoriz; 44 ChartAxis mVert; 45 46 @ViewDebug.ExportedProperty 47 private int mOptimalWidth = -1; 48 private float mOptimalWidthWeight = 0; 49 50 private Rect mContent = new Rect(); 51 52 public ChartView(Context context) { 53 this(context, null, 0); 54 } 55 56 public ChartView(Context context, AttributeSet attrs) { 57 this(context, attrs, 0); 58 } 59 60 public ChartView(Context context, AttributeSet attrs, int defStyle) { 61 super(context, attrs, defStyle); 62 63 final TypedArray a = context.obtainStyledAttributes( 64 attrs, R.styleable.ChartView, defStyle, 0); 65 setOptimalWidth(a.getDimensionPixelSize(R.styleable.ChartView_optimalWidth, -1), 66 a.getFloat(R.styleable.ChartView_optimalWidthWeight, 0)); 67 a.recycle(); 68 69 setClipToPadding(false); 70 setClipChildren(false); 71 } 72 73 void init(ChartAxis horiz, ChartAxis vert) { 74 mHoriz = checkNotNull(horiz, "missing horiz"); 75 mVert = checkNotNull(vert, "missing vert"); 76 } 77 78 public void setOptimalWidth(int optimalWidth, float optimalWidthWeight) { 79 mOptimalWidth = optimalWidth; 80 mOptimalWidthWeight = optimalWidthWeight; 81 requestLayout(); 82 } 83 84 @Override 85 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 86 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 87 88 final int slack = getMeasuredWidth() - mOptimalWidth; 89 if (mOptimalWidth > 0 && slack > 0) { 90 final int targetWidth = (int) (mOptimalWidth + (slack * mOptimalWidthWeight)); 91 widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY); 92 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 93 } 94 } 95 96 @Override 97 protected void onLayout(boolean changed, int l, int t, int r, int b) { 98 mContent.set(getPaddingLeft(), getPaddingTop(), r - l - getPaddingRight(), 99 b - t - getPaddingBottom()); 100 final int width = mContent.width(); 101 final int height = mContent.height(); 102 103 // no scrolling yet, so tell dimensions to fill exactly 104 mHoriz.setSize(width); 105 mVert.setSize(height); 106 107 final Rect parentRect = new Rect(); 108 final Rect childRect = new Rect(); 109 110 for (int i = 0; i < getChildCount(); i++) { 111 final View child = getChildAt(i); 112 final LayoutParams params = (LayoutParams) child.getLayoutParams(); 113 114 parentRect.set(mContent); 115 116 if (child instanceof ChartNetworkSeriesView || child instanceof ChartGridView) { 117 // series are always laid out to fill entire graph area 118 // TODO: handle scrolling for series larger than content area 119 Gravity.apply(params.gravity, width, height, parentRect, childRect); 120 child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); 121 122 } else if (child instanceof ChartSweepView) { 123 layoutSweep((ChartSweepView) child, parentRect, childRect); 124 child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); 125 } 126 } 127 } 128 129 protected void layoutSweep(ChartSweepView sweep) { 130 final Rect parentRect = new Rect(mContent); 131 final Rect childRect = new Rect(); 132 133 layoutSweep(sweep, parentRect, childRect); 134 sweep.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); 135 } 136 137 protected void layoutSweep(ChartSweepView sweep, Rect parentRect, Rect childRect) { 138 final Rect sweepMargins = sweep.getMargins(); 139 140 // sweep is always placed along specific dimension 141 if (sweep.getFollowAxis() == ChartSweepView.VERTICAL) { 142 parentRect.top += sweepMargins.top + (int) sweep.getPoint(); 143 parentRect.bottom = parentRect.top; 144 parentRect.left += sweepMargins.left; 145 parentRect.right += sweepMargins.right; 146 Gravity.apply(SWEEP_GRAVITY, parentRect.width(), sweep.getMeasuredHeight(), 147 parentRect, childRect); 148 149 } else { 150 parentRect.left += sweepMargins.left + (int) sweep.getPoint(); 151 parentRect.right = parentRect.left; 152 parentRect.top += sweepMargins.top; 153 parentRect.bottom += sweepMargins.bottom; 154 Gravity.apply(SWEEP_GRAVITY, sweep.getMeasuredWidth(), parentRect.height(), 155 parentRect, childRect); 156 } 157 } 158 159 } 160