/** Copyright 2015 Tim Engler, Rareventure LLC This file is part of Tiny Travel Tracker. Tiny Travel Tracker is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Tiny Travel Tracker is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Tiny Travel Tracker. If not, see <http://www.gnu.org/licenses/>. */ package com.rareventure.android.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Point; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import com.rareventure.gps2.R; /** * Displays a speech bubble widget that has a single child view inside of it */ public class SpeechBubbleWidget extends ViewGroup { private Rect innerRect; private SpeechBubbleDrawer speechBubbleDrawer; public SpeechBubbleWidget(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SpeechBubbleWidget); this.speechBubbleDrawer = new SpeechBubbleDrawer( a.getDimensionPixelOffset(R.styleable.SpeechBubbleWidget_speechBubbleRoundedCorner, 5), a.getFloat(R.styleable.SpeechBubbleWidget_speechBubbleTriXPerc, .75f), a.getDimensionPixelOffset(R.styleable.SpeechBubbleWidget_speechBubbleTriWidth, 5), a.getDimensionPixelOffset(R.styleable.SpeechBubbleWidget_speechBubbleTriHeight, 5), a.getDimensionPixelOffset(R.styleable.SpeechBubbleWidget_speechBubbleInnerPad, 5)); } /** * This weird method must be in every subclass of ViewGroup or Layouts within the class won't display properly * We need to call onMeasure on the child in it. */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); Rect measuredInnerRect = speechBubbleDrawer.calcInnerRect(0,0, width, height); final View child = getChildAt(0); final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, //this next thing is the padding between the size of the entire widget //(which currently extends to the edge of the screen) getPaddingLeft() + getPaddingRight() + width - measuredInnerRect.width(), child.getLayoutParams().width); final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom() + height - measuredInnerRect.height(), child.getLayoutParams().height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); setMeasuredDimension(resolveSize(child.getMeasuredWidth(), widthMeasureSpec), resolveSize(child.getMeasuredHeight(), heightMeasureSpec)); } @Override protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { final LayoutParams lp = child.getLayoutParams(); Rect measuredInnerRect = calcInnerRect(0,0,lp.width,lp.height); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft() + getPaddingRight(), measuredInnerRect.width()); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, getPaddingTop() + getPaddingBottom(), measuredInnerRect.height()); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); super.measureChild(child, parentWidthMeasureSpec, parentHeightMeasureSpec); } private Rect calcInnerRect(int l, int t, int r, int b) { return speechBubbleDrawer.calcInnerRect( l + getPaddingLeft(), t + getPaddingTop(), r - getPaddingRight(), b - getPaddingBottom() ); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { innerRect = calcInnerRect(l,t,r,b); getChildAt(0).layout(innerRect.left, innerRect.top, innerRect.right, innerRect.bottom); } @Override public void addView(View child, int index) { if (getChildCount() > 1) { throw new IllegalStateException("only one child"); } super.addView(child, index); } @Override public void addView(View child, LayoutParams params) { if (getChildCount() > 1) { throw new IllegalStateException("only one child"); } super.addView(child, params); } @Override public void addView(View child, int index, LayoutParams params) { if (getChildCount() > 1) { throw new IllegalStateException("only one child"); } super.addView(child, index, params); } //TODO 2.5: find out why trip through naples has so few points @Override protected void dispatchDraw(Canvas canvas) { if(super.getVisibility() == VISIBLE) { speechBubbleDrawer.draw(canvas, innerRect.left, innerRect.top, innerRect.width(), innerRect.height()); super.dispatchDraw(canvas); } } /** * @return the location of the \| */ public Point getPoint() { return speechBubbleDrawer.getPointLocation(getLeft()+getPaddingLeft(), getTop()+getPaddingTop(),getWidth()-getPaddingLeft()-getPaddingRight(), getHeight() - getPaddingTop() - getPaddingBottom()); } public void drawBubble(Canvas canvas, String name, int x, int y) { } }