1d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen/* 2d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * Copyright (C) 2015 The Android Open Source Project 3d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * 4d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * Licensed under the Apache License, Version 2.0 (the "License"); 5d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * you may not use this file except in compliance with the License. 6d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * You may obtain a copy of the License at 7d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * 8d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * http://www.apache.org/licenses/LICENSE-2.0 9d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * 10d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * Unless required by applicable law or agreed to in writing, software 11d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * distributed under the License is distributed on an "AS IS" BASIS, 12d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * See the License for the specific language governing permissions and 14d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * limitations under the License. 15d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 16d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 17d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassenpackage com.android.deskclock.widget; 18d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 19d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassenimport android.annotation.SuppressLint; 20d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassenimport android.content.Context; 21d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassenimport android.content.res.TypedArray; 22d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassenimport android.graphics.Canvas; 23d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassenimport android.graphics.Color; 24d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassenimport android.graphics.Paint; 25d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassenimport android.util.AttributeSet; 26d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassenimport android.util.Property; 27d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassenimport android.view.Gravity; 28d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassenimport android.view.View; 29d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 30d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassenimport com.android.deskclock.R; 31d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 32d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen/** 33d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * A {@link View} that draws primitive circles. 34d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 35d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassenpublic class CircleView extends View { 36d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 37d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 38d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * A Property wrapper around the fillColor functionality handled by the 39d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * {@link #setFillColor(int)} and {@link #getFillColor()} methods. 40d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 41d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public final static Property<CircleView, Integer> FILL_COLOR = 42d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen new Property<CircleView, Integer>(Integer.class, "fillColor") { 43d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen @Override 44d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public Integer get(CircleView view) { 45d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen return view.getFillColor(); 46d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 47d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 48d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen @Override 49d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public void set(CircleView view, Integer value) { 50d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen view.setFillColor(value); 51d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 52d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen }; 53d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 54d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 55d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * A Property wrapper around the radius functionality handled by the 56d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * {@link #setRadius(float)} and {@link #getRadius()} methods. 57d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 58d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public final static Property<CircleView, Float> RADIUS = 59d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen new Property<CircleView, Float>(Float.class, "radius") { 60d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen @Override 61d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public Float get(CircleView view) { 62d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen return view.getRadius(); 63d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 64d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 65d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen @Override 66d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public void set(CircleView view, Float value) { 67d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen view.setRadius(value); 68d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 69d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen }; 70d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 71d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 72d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * The {@link Paint} used to draw the circle. 73d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 74d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen private final Paint mCirclePaint = new Paint(); 75d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 76d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen private int mGravity; 77d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen private float mCenterX; 78d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen private float mCenterY; 79d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen private float mRadius; 80d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 81d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public CircleView(Context context) { 82d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen this(context, null /* attrs */); 83d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 84d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 85d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public CircleView(Context context, AttributeSet attrs) { 86d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen this(context, attrs, 0 /* defStyleAttr */); 87d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 88d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 89d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public CircleView(Context context, AttributeSet attrs, int defStyleAttr) { 90d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen super(context, attrs, defStyleAttr); 91d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 92d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen final TypedArray a = context.obtainStyledAttributes( 93d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen attrs, R.styleable.CircleView, defStyleAttr, 0 /* defStyleRes */); 94d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 95d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mGravity = a.getInt(R.styleable.CircleView_android_gravity, Gravity.NO_GRAVITY); 96d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mCenterX = a.getDimension(R.styleable.CircleView_centerX, 0.0f); 97d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mCenterY = a.getDimension(R.styleable.CircleView_centerY, 0.0f); 98d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mRadius = a.getDimension(R.styleable.CircleView_radius, 0.0f); 99d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 100d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mCirclePaint.setColor(a.getColor(R.styleable.CircleView_fillColor, Color.WHITE)); 101d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 102d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen a.recycle(); 103d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 104d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 105d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen @Override 106d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public void onRtlPropertiesChanged(int layoutDirection) { 107d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen super.onRtlPropertiesChanged(layoutDirection); 108d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 109d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen if (mGravity != Gravity.NO_GRAVITY) { 110d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen applyGravity(mGravity, layoutDirection); 111d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 112d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 113d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 114d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen @Override 115d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 116d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen super.onLayout(changed, left, top, right, bottom); 117d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 118d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen if (mGravity != Gravity.NO_GRAVITY) { 119d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen applyGravity(mGravity, getLayoutDirection()); 120d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 121d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 122d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 123d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen @Override 124d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen protected void onDraw(Canvas canvas) { 125d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen super.onDraw(canvas); 126d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 127d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen // draw the circle, duh 128d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen canvas.drawCircle(mCenterX, mCenterY, mRadius, mCirclePaint); 129d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 130d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 131d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen @Override 132d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public boolean hasOverlappingRendering() { 133d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen // only if we have a background, which we shouldn't... 134d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen return getBackground() != null && getBackground().getCurrent() != null; 135d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 136d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 137d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 138d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @return the current {@link Gravity} used to align/size the circle 139d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 140d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public final int getGravity() { 141d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen return mGravity; 142d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 143d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 144d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 145d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * Describes how to align/size the circle relative to the view's bounds. Defaults to 146d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * {@link Gravity#NO_GRAVITY}. 147d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * <p/> 148d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * Note: using {@link #setCenterX(float)}, {@link #setCenterY(float)}, or 149d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * {@link #setRadius(float)} will automatically clear any conflicting gravity bits. 150d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * 151d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @param gravity the {@link Gravity} flags to use 152d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @return this object, allowing calls to methods in this class to be chained 153d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @see R.styleable#CircleView_android_gravity 154d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 155d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public CircleView setGravity(int gravity) { 156d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen if (mGravity != gravity) { 157d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mGravity = gravity; 158d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 159d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen if (gravity != Gravity.NO_GRAVITY && isLayoutDirectionResolved()) { 160d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen applyGravity(gravity, getLayoutDirection()); 161d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 162d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 163d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen return this; 164d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 165d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 166d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 167d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @return the ARGB color used to fill the circle 168d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 169d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public final int getFillColor() { 170d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen return mCirclePaint.getColor(); 171d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 172d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 173d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 174d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * Sets the ARGB color used to fill the circle and invalidates only the affected area. 175d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * 176d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @param color the ARGB color to use 177d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @return this object, allowing calls to methods in this class to be chained 178d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @see R.styleable#CircleView_fillColor 179d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 180d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public CircleView setFillColor(int color) { 181d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen if (mCirclePaint.getColor() != color) { 182d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mCirclePaint.setColor(color); 183d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 184d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen // invalidate the current area 185d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen invalidate(mCenterX, mCenterY, mRadius); 186d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 187d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen return this; 188d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 189d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 190d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 191d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @return the x-coordinate of the center of the circle 192d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 193d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public final float getCenterX() { 194d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen return mCenterX; 195d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 196d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 197d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 198d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * Sets the x-coordinate for the center of the circle and invalidates only the affected area. 199d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * 200d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @param centerX the x-coordinate to use, relative to the view's bounds 201d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @return this object, allowing calls to methods in this class to be chained 202d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @see R.styleable#CircleView_centerX 203d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 204d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public CircleView setCenterX(float centerX) { 205d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen final float oldCenterX = mCenterX; 206d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen if (oldCenterX != centerX) { 207d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mCenterX = centerX; 208d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 209d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen // invalidate the old/new areas 210d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen invalidate(oldCenterX, mCenterY, mRadius); 211d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen invalidate(centerX, mCenterY, mRadius); 212d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 213d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 214d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen // clear the horizontal gravity flags 215d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mGravity &= ~Gravity.HORIZONTAL_GRAVITY_MASK; 216d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 217d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen return this; 218d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 219d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 220d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 221d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @return the y-coordinate of the center of the circle 222d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 223d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public final float getCenterY() { 224d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen return mCenterY; 225d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 226d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 227d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 228d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * Sets the y-coordinate for the center of the circle and invalidates only the affected area. 229d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * 230d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @param centerY the y-coordinate to use, relative to the view's bounds 231d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @return this object, allowing calls to methods in this class to be chained 232d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @see R.styleable#CircleView_centerY 233d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 234d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public CircleView setCenterY(float centerY) { 235d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen final float oldCenterY = mCenterY; 236d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen if (oldCenterY != centerY) { 237d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mCenterY = centerY; 238d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 239d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen // invalidate the old/new areas 240d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen invalidate(mCenterX, oldCenterY, mRadius); 241d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen invalidate(mCenterX, centerY, mRadius); 242d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 243d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 244d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen // clear the vertical gravity flags 245d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mGravity &= ~Gravity.VERTICAL_GRAVITY_MASK; 246d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 247d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen return this; 248d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 249d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 250d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 251d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @return the radius of the circle 252d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 253d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public final float getRadius() { 254d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen return mRadius; 255d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 256d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 257d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 258d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * Sets the radius of the circle and invalidates only the affected area. 259d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * 260d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @param radius the radius to use 261d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @return this object, allowing calls to methods in this class to be chained 262d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @see R.styleable#CircleView_radius 263d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 264d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen public CircleView setRadius(float radius) { 265d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen final float oldRadius = mRadius; 266d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen if (oldRadius != radius) { 267d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mRadius = radius; 268d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 269d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen // invalidate the old/new areas 270d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen invalidate(mCenterX, mCenterY, oldRadius); 271d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen if (radius > oldRadius) { 272d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen invalidate(mCenterX, mCenterY, radius); 273d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 274d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 275d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 276d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen // clear the fill gravity flags 277d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen if ((mGravity & Gravity.FILL_HORIZONTAL) == Gravity.FILL_HORIZONTAL) { 278d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mGravity &= ~Gravity.FILL_HORIZONTAL; 279d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 280d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen if ((mGravity & Gravity.FILL_VERTICAL) == Gravity.FILL_VERTICAL) { 281d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mGravity &= ~Gravity.FILL_VERTICAL; 282d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 283d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 284d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen return this; 285d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 286d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 287d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 288d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * Invalidates the rectangular area that circumscribes the circle defined by {@code centerX}, 289d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * {@code centerY}, and {@code radius}. 290d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 291d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen private void invalidate(float centerX, float centerY, float radius) { 292d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen invalidate((int) (centerX - radius - 0.5f), (int) (centerY - radius - 0.5f), 293d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen (int) (centerX + radius + 0.5f), (int) (centerY + radius + 0.5f)); 294d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 295d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 296d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen /** 297d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * Applies the specified {@code gravity} and {@code layoutDirection}, adjusting the alignment 298d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * and size of the circle depending on the resolved {@link Gravity} flags. Also invalidates the 299d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * affected area if necessary. 300d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * 301d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @param gravity the {@link Gravity} the {@link Gravity} flags to use 302d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen * @param layoutDirection the layout direction used to resolve the absolute gravity 303d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen */ 304d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen @SuppressLint("RtlHardcoded") 305d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen private void applyGravity(int gravity, int layoutDirection) { 306d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); 307d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 308d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen final float oldRadius = mRadius; 309d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen final float oldCenterX = mCenterX; 310d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen final float oldCenterY = mCenterY; 311d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 312d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 313d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen case Gravity.LEFT: 314d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mCenterX = 0.0f; 315d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen break; 316d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen case Gravity.CENTER_HORIZONTAL: 317d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen case Gravity.FILL_HORIZONTAL: 318d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mCenterX = getWidth() / 2.0f; 319d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen break; 320d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen case Gravity.RIGHT: 321d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mCenterX = getWidth(); 322d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen break; 323d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 324d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 325d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) { 326d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen case Gravity.TOP: 327d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mCenterY = 0.0f; 328d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen break; 329d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen case Gravity.CENTER_VERTICAL: 330d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen case Gravity.FILL_VERTICAL: 331d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mCenterY = getHeight() / 2.0f; 332d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen break; 333d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen case Gravity.BOTTOM: 334d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mCenterY = getHeight(); 335d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen break; 336d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 337d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 338d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen switch (absoluteGravity & Gravity.FILL) { 339d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen case Gravity.FILL: 340d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mRadius = Math.min(getWidth(), getHeight()) / 2.0f; 341d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen break; 342d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen case Gravity.FILL_HORIZONTAL: 343d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mRadius = getWidth() / 2.0f; 344d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen break; 345d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen case Gravity.FILL_VERTICAL: 346d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen mRadius = getHeight() / 2.0f; 347d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen break; 348d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 349d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen 350d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen if (oldCenterX != mCenterX || oldCenterY != mCenterY || oldRadius != mRadius) { 351d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen invalidate(oldCenterX, oldCenterY, oldRadius); 352d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen invalidate(mCenterX, mCenterY, mRadius); 353d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 354d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen } 355d904cd4d97b5f10c0f65642c85d375ffc9b2ec3cJustin Klaassen} 356