Created
January 29, 2018 12:21
-
-
Save mobiRic/b390545ad3ddcafd9fb7206cef0d4a44 to your computer and use it in GitHub Desktop.
Yet another circle ImageView for Android
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2018 Glowworm Software | |
* | |
* 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 mobi.glowworm.lib.ui.widget; | |
import android.annotation.SuppressLint; | |
import android.annotation.TargetApi; | |
import android.content.Context; | |
import android.graphics.Canvas; | |
import android.graphics.Color; | |
import android.graphics.Paint; | |
import android.graphics.Path; | |
import android.os.Build; | |
import android.support.annotation.ColorInt; | |
import android.support.annotation.Nullable; | |
import android.util.AttributeSet; | |
import android.widget.ImageView; | |
/** | |
* A simple {@link ImageView} that crops to a circle shape. | |
*/ | |
@SuppressLint("AppCompatCustomView") | |
public class CircleImageView extends ImageView { | |
private static final int DEFAULT_FRAME_WIDTH_DP = 1 /*dp*/; | |
@ColorInt | |
private static final int DEFAULT_FRAME_COLOR = Color.BLACK; | |
private Paint framePaint; | |
private float frameWidth; | |
private int x; | |
private int y; | |
private float radius; | |
private Path frameClip; | |
public CircleImageView(Context context) { | |
super(context); | |
init(context, null, 0, 0); | |
} | |
public CircleImageView(Context context, @Nullable AttributeSet attrs) { | |
super(context, attrs); | |
init(context, attrs, 0, 0); | |
} | |
public CircleImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { | |
super(context, attrs, defStyleAttr); | |
init(context, attrs, defStyleAttr, 0); | |
} | |
@TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
public CircleImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { | |
super(context, attrs, defStyleAttr, defStyleRes); | |
init(context, attrs, defStyleAttr, defStyleRes); | |
} | |
private void init(Context context, @Nullable AttributeSet attrs, int defStyle, int defStyleRes) { | |
// TODO initialise xml properties | |
float scale = getResources().getDisplayMetrics().density; | |
frameWidth = (int) (DEFAULT_FRAME_WIDTH_DP * scale + 0.5f); | |
framePaint = new Paint(); | |
framePaint.setAntiAlias(true); | |
framePaint.setColor(DEFAULT_FRAME_COLOR); | |
framePaint.setStyle(Paint.Style.STROKE); | |
framePaint.setStrokeWidth(frameWidth); | |
frameClip = new Path(); | |
} | |
@Override | |
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | |
super.onMeasure(widthMeasureSpec, heightMeasureSpec); | |
// check if anything has changed | |
if (((getMeasuredWidth() / 2) == x) && ((getMeasuredHeight() / 2) == y)) { | |
return; | |
} | |
x = getMeasuredWidth() / 2; | |
y = getMeasuredHeight() / 2; | |
radius = Math.min(x, y) - (frameWidth / 2); | |
frameClip.reset(); | |
frameClip.addCircle( | |
x, y, | |
radius, | |
Path.Direction.CW); | |
frameClip.close(); | |
} | |
@Override | |
protected void onDraw(Canvas canvas) { | |
// circle crop the canvas | |
final int save = canvas.save(); | |
canvas.clipPath(frameClip); | |
{ | |
super.onDraw(canvas); | |
} | |
canvas.restoreToCount(save); | |
// cropping does not anti-alias so mask with a frame | |
canvas.drawCircle( | |
x, y, | |
radius, | |
framePaint); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A simple circle
ImageView
implementation.This version attempts to optimise for memory and does not create an extra bitmap in memory like many implementations.
Instead the drawing canvas is clipped to a circular path before drawing. There are 2 considerations to this approach:
The first issue is worked around by overdrawing a circular frame which masks the aliasing. The circle drawing is anti-aliased, and this is fine for most use cases.
The second issue is the compromise for not creating a second bitmap in memory - trading off speed for memory (although no performance tests have been done on this yet).
TODO:
AppCompatImageView
to add support for advanced features