Home | History | Annotate | Download | only in widget
      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) {
    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 ChartGridView) {
    122                 // Grid uses some extra room for labels
    123                 Gravity.apply(params.gravity, width, height, parentRect, childRect);
    124                 child.layout(childRect.left, childRect.top, childRect.right,
    125                         childRect.bottom + child.getPaddingBottom());
    126 
    127             } else if (child instanceof ChartSweepView) {
    128                 layoutSweep((ChartSweepView) child, parentRect, childRect);
    129                 child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
    130             }
    131         }
    132     }
    133 
    134     protected void layoutSweep(ChartSweepView sweep) {
    135         final Rect parentRect = new Rect(mContent);
    136         final Rect childRect = new Rect();
    137 
    138         layoutSweep(sweep, parentRect, childRect);
    139         sweep.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
    140     }
    141 
    142     protected void layoutSweep(ChartSweepView sweep, Rect parentRect, Rect childRect) {
    143         final Rect sweepMargins = sweep.getMargins();
    144 
    145         // sweep is always placed along specific dimension
    146         if (sweep.getFollowAxis() == ChartSweepView.VERTICAL) {
    147             parentRect.top += sweepMargins.top + (int) sweep.getPoint();
    148             parentRect.bottom = parentRect.top;
    149             parentRect.left += sweepMargins.left;
    150             parentRect.right += sweepMargins.right;
    151             Gravity.apply(SWEEP_GRAVITY, parentRect.width(), sweep.getMeasuredHeight(),
    152                     parentRect, childRect);
    153 
    154         } else {
    155             parentRect.left += sweepMargins.left + (int) sweep.getPoint();
    156             parentRect.right = parentRect.left;
    157             parentRect.top += sweepMargins.top;
    158             parentRect.bottom += sweepMargins.bottom;
    159             Gravity.apply(SWEEP_GRAVITY, sweep.getMeasuredWidth(), parentRect.height(),
    160                     parentRect, childRect);
    161         }
    162     }
    163 }
    164