/* * Copyright (C) 2015 University of South Florida (sjbarbeau@gmail.com) * * 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 org.onebusaway.android.view; import org.onebusaway.android.R; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.os.Build; import android.support.v4.view.animation.FastOutLinearInInterpolator; import android.util.AttributeSet; import android.view.View; import android.view.ViewTreeObserver; import android.view.animation.Animation; import android.view.animation.Transformation; /** * View that animates a circle expanding and contracting to indicate real-time information */ public class RealtimeIndicatorView extends View { private Paint mFillPaint; private Paint mLinePaint; private Animation mAnimation1; private float mNewRadius; /** * Percentage of total view size that the circle should be drawn */ private final static float SCALE = 0.45f; /** * Default fill color */ public int mFillColor = 0xbbFFFFFF; /** * Default line color */ public int mLineColor = 0xFFFFFFFF; /** * Default duration */ private int mDuration = 1000; // ms /** * Set to true after the animation has been initialized */ private boolean mInitComplete = false; public RealtimeIndicatorView(Context context, AttributeSet attrs) { super(context, attrs); // Get the custom attributes defined in XML TypedArray a = context .obtainStyledAttributes(attrs, R.styleable.RealtimeIndicatorView, 0, 0); mFillColor = a.getColor(R.styleable.RealtimeIndicatorView_fillColor, mFillColor); mLineColor = a.getColor(R.styleable.RealtimeIndicatorView_lineColor, mLineColor); mDuration = a.getInteger(R.styleable.RealtimeIndicatorView_duration, mDuration); a.recycle(); mFillPaint = new Paint(); mFillPaint.setColor(mFillColor); mFillPaint.setStyle(Paint.Style.FILL); mFillPaint.setAntiAlias(true); mLinePaint = new Paint(); mLinePaint.setStrokeWidth(3); mLinePaint.setColor(mLineColor); mLinePaint.setStyle(Paint.Style.STROKE); mLinePaint.setAntiAlias(true); setOnMeasureCallback(); ensureInit(); } private void setOnMeasureCallback() { getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { removeOnGlobalLayoutListener(this); initAnimation(); } }); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) { if (Build.VERSION.SDK_INT < 16) { getViewTreeObserver().removeGlobalOnLayoutListener(listener); } else { getViewTreeObserver().removeOnGlobalLayoutListener(listener); } } /** * Makes sure the animation has been initialized. This is a workaround to make sure * the animation is shown when this view is used within a list with an adapter. */ private synchronized void ensureInit() { initAnimation(); this.postDelayed(new Runnable() { @Override public void run() { if (!mInitComplete) { initAnimation(); } } }, 500); } /** * Setup the animation */ private synchronized void initAnimation() { // Animate circle expand/contract mAnimation1 = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { int height = getHeight(); mNewRadius = height * interpolatedTime; invalidate(); } }; mAnimation1.setDuration(mDuration); mAnimation1.setRepeatMode(Animation.REVERSE); mAnimation1.setInterpolator(new FastOutLinearInInterpolator()); mAnimation1.setRepeatCount(Animation.INFINITE); startAnimation(mAnimation1); mInitComplete = true; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int height = getHeight(); int width = getWidth(); float x = width / 2.0f; float y = height / 2.0f; canvas.drawCircle(x, y, mNewRadius * SCALE, mFillPaint); canvas.drawCircle(x, y, mNewRadius * SCALE, mLinePaint); } /** * Set the fill color to be used when drawing the expanding/contracting circle, in the format * 0xbbFFFFFF. * * @param color the fill color to be used when drawing the expanding/contracting circle, in the * format * 0xbbFFFFFF. */ public void setFillColor(int color) { mFillColor = color; mFillPaint.setColor(color); } /** * Set the line (i.e., stroke) color to be used when drawing the expanding/contracting circle, * in the format * 0xbbFFFFFF. * * @param color the line (i.e., stroke) to be used when drawing the expanding/contracting * circle, in the format * 0xbbFFFFFF. */ public void setLineColor(int color) { mLineColor = color; mLinePaint.setColor(color); } }