public SoundLevels(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); // Safe source, replaced with system one when attached. mLevelSource = new AudioLevelSource(); mLevelSource.setSpeechLevel(0); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SoundLevels, defStyle, 0); mMaximumLevelSize = a.getDimensionPixelOffset(R.styleable.SoundLevels_maxLevelRadius, 0); mMinimumLevelSize = a.getDimensionPixelOffset(R.styleable.SoundLevels_minLevelRadius, 0); mMinimumLevel = mMinimumLevelSize / mMaximumLevelSize; mPrimaryLevelPaint = new Paint(); mPrimaryLevelPaint.setColor(a.getColor(R.styleable.SoundLevels_primaryColor, Color.BLACK)); mPrimaryLevelPaint.setFlags(Paint.ANTI_ALIAS_FLAG); a.recycle(); // This animator generates ticks that invalidate the // view so that the animation is synced with the global animation loop. // TODO: We could probably remove this in favor of using postInvalidateOnAnimation // which might improve things further. mSpeechLevelsAnimator = new TimeAnimator(); mSpeechLevelsAnimator.setRepeatCount(ObjectAnimator.INFINITE); mSpeechLevelsAnimator.setTimeListener( new TimeListener() { @Override public void onTimeUpdate( final TimeAnimator animation, final long totalTime, final long deltaTime) { invalidate(); } }); }
@Override protected void onDraw(final Canvas canvas) { if (!mIsEnabled) { return; } if (!mCenterDefined) { // One time computation here, because we can't rely on getWidth() to be computed at // constructor time or in onFinishInflate :(. mCenterX = getWidth() / 2; mCenterY = getWidth() / 2; mCenterDefined = true; } final int level = mLevelSource.getSpeechLevel(); // Either ease towards the target level, or decay away from it depending on whether // its higher or lower than the current. if (level > mCurrentVolume) { mCurrentVolume = mCurrentVolume + ((level - mCurrentVolume) / 4); } else { mCurrentVolume = mCurrentVolume * 0.95f; } final float radius = mMinimumLevel + (1f - mMinimumLevel) * mCurrentVolume / 100; mPrimaryLevelPaint.setStyle(Style.FILL); canvas.drawCircle(mCenterX, mCenterY, radius * mMaximumLevelSize, mPrimaryLevelPaint); }