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