/* * Copyright 2013 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.maps.android.ui; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.google.maps.android.R; /** * TextIconGenerator generates icons that contain text (or custom content) within an info * window-like shape. * <p/> * The icon {@link Bitmap}s generated by the factory should be used in conjunction with a {@link * com.google.android.gms.maps.model.BitmapDescriptorFactory}. * <p/> * This class is not thread safe. */ public class TextIconGenerator { private final Context mContext; private ViewGroup mContainer; private RotationLayout mRotationLayout; private TextView mTextView; private View mContentView; private int mRotation; private float mAnchorU = 0.5f; private float mAnchorV = 1f; /** * Creates a new TextIconGenerator with the default style. */ public TextIconGenerator(Context context) { mContext = context; } /** * Sets the text content, then creates an icon with the current style. * * @param text the text content to display inside the icon. */ public Bitmap makeIcon(String text) { ensureViewsSetUp(); if (mTextView != null) { mTextView.setText(text); } return makeIcon(); } /** * Creates an icon with the current content and style. * <p/> * This method is useful if a custom view has previously been set, or if text content is not * applicable. */ public Bitmap makeIcon() { ViewGroup container = getContainer(); int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); container.measure(measureSpec, measureSpec); int measuredWidth = container.getMeasuredWidth(); int measuredHeight = container.getMeasuredHeight(); container.layout(0, 0, measuredWidth, measuredHeight); if (mRotation == 1 || mRotation == 3) { measuredHeight = container.getMeasuredWidth(); measuredWidth = container.getMeasuredHeight(); } Bitmap r = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888); r.eraseColor(Color.TRANSPARENT); Canvas canvas = new Canvas(r); if (mRotation == 0) { // do nothing } else if (mRotation == 1) { canvas.translate(measuredWidth, 0); canvas.rotate(90); } else if (mRotation == 2) { canvas.rotate(180, measuredWidth / 2, measuredHeight / 2); } else { canvas.translate(0, measuredHeight); canvas.rotate(270); } container.draw(canvas); return r; } /** * Sets the child view for the icon. * <p/> * If the view contains a {@link TextView} with the id "text", operations such as {@link * #setTextAppearance} and {@link #makeIcon(String)} will operate upon that {@link TextView}. */ public void setContentView(View contentView) { ensureViewsSetUp(); mRotationLayout.removeAllViews(); mRotationLayout.addView(contentView); mContentView = contentView; final View view = mRotationLayout.findViewById(R.id.text); mTextView = view instanceof TextView ? (TextView) view : null; } /** * Rotates the contents of the icon. * * @param degrees the amount the contents should be rotated, as a multiple of 90 degrees. */ public void setContentRotation(int degrees) { ensureViewsSetUp(); mRotationLayout.setViewRotation(degrees); } /** * Rotates the icon. * * @param degrees the amount the icon should be rotated, as a multiple of 90 degrees. */ public void setRotation(int degrees) { mRotation = ((degrees + 360) % 360) / 90; } /** * @return u coordinate of the anchor, with rotation applied. */ public float getAnchorU() { return rotateAnchor(mAnchorU, mAnchorV); } /** * @return v coordinate of the anchor, with rotation applied. */ public float getAnchorV() { return rotateAnchor(mAnchorV, mAnchorU); } /** * Rotates the anchor around (u, v) = (0, 0). */ private float rotateAnchor(float u, float v) { switch (mRotation) { case 0: return u; case 1: return 1 - v; case 2: return 1 - u; case 3: return v; } throw new IllegalStateException(); } /** * Sets the text color, size, style, hint color, and highlight color from the specified * <code>TextAppearance</code> resource. * * @param resid the identifier of the resource. */ public void setTextAppearance(Context context, int resid) { ensureViewsSetUp(); if (mTextView != null) { mTextView.setTextAppearance(context, resid); } } /** * Sets the style of the icon. The style consists of a background and text appearance. */ public void setStyle(int style) { setBackground(mContext.getResources().getDrawable(getBackground(style))); setTextAppearance(mContext, getTextStyle(style)); } /** * Set the background to a given Drawable, or remove the background. * * @param background the Drawable to use as the background, or null to remove the background. */ @SuppressWarnings("deprecation") // View#setBackgroundDrawable is compatible with pre-API level 16 (Jelly Bean). public void setBackground(Drawable background) { getContainer().setBackgroundDrawable(background); } /** * Not thread safe. */ private ViewGroup getContainer() { ensureViewsSetUp(); return mContainer; } /** * Ensure views are ready. This allows us to lazily inflate the main layout. */ private void ensureViewsSetUp() { if (mContainer == null) { mContainer = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.text_bubble, null); mRotationLayout = (RotationLayout) mContainer.getChildAt(0); mContentView = mTextView = (TextView) mRotationLayout.findViewById(R.id.text); } } /** * Sets the padding of the content view. The default padding of the content view (i.e. text * view) is 5dp top/bottom and 10dp left/right. * * @param left the left padding in pixels. * @param top the top padding in pixels. * @param right the right padding in pixels. * @param bottom the bottom padding in pixels. */ public void setContentPadding(int left, int top, int right, int bottom) { ensureViewsSetUp(); mContentView.setPadding(left, top, right, bottom); } public static final int STYLE_DEFAULT = 1; public static final int STYLE_WHITE = 2; public static final int STYLE_RED = 3; public static final int STYLE_BLUE = 4; public static final int STYLE_GREEN = 5; public static final int STYLE_PURPLE = 6; public static final int STYLE_ORANGE = 7; private static int getBackground(int style) { switch (style) { default: case STYLE_DEFAULT: case STYLE_WHITE: return R.drawable.bubble_white; case STYLE_RED: return R.drawable.bubble_red; case STYLE_BLUE: return R.drawable.bubble_blue; case STYLE_GREEN: return R.drawable.bubble_green; case STYLE_PURPLE: return R.drawable.bubble_purple; case STYLE_ORANGE: return R.drawable.bubble_orange; } } private static int getTextStyle(int style) { switch (style) { default: case STYLE_DEFAULT: case STYLE_WHITE: return R.style.Bubble_TextAppearance_Dark; case STYLE_RED: case STYLE_BLUE: case STYLE_GREEN: case STYLE_PURPLE: case STYLE_ORANGE: return R.style.Bubble_TextAppearance_Light; } } }