最近因为项目需要找到这个demo ,顺便分享给大家。自定义图片的形状及旋转方向。
下面是自定义内容
package net.grobas.view;
import net.grobas.polygonimageview.sample.R;
import net.grobas.shapes.PolygonShape;
import net.grobas.shapes.PolygonShapeSpec;
import net.grobas.shapes.RegularPolygonShape;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ImageView;
/**
* Construct a custom ImageView with a regular polygonal form.
* The number of vertices determines the polygon form.
* Special cases for vertex number are:
* 0 -> Circle
* 1 -> Regular ImageView
* 2 -> Square
* Use square images
*
* @author Albert Grobas
*/
public class PolygonImageView extends ImageView {
//draws
private Paint mPaint;
private Paint mBorderPaint;
private Path mPath;
private PolygonShape mPolygonShape;
private PolygonShapeSpec mPolygonShapeSpec;
private int canvasWidth, canvasHeight;
String LAYER_TYPE_SOFTWARE = "";
public PolygonImageView(Context context) {
this(context, null);
}
public PolygonImageView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.polygonImageViewStyle);
}
public PolygonImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mPolygonShapeSpec = new PolygonShapeSpec();
TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.PolygonImageView, defStyle, 0);
try {
mPolygonShapeSpec.setRotation(attributes.getFloat(R.styleable.PolygonImageView_poly_rotation_angle, 0f));
mPolygonShapeSpec.setNumVertex(attributes.getInteger(R.styleable.PolygonImageView_poly_vertices, 6));
mPolygonShapeSpec.setCornerRadius(attributes.getFloat(R.styleable.PolygonImageView_poly_corner_radius, 0f));
mPolygonShapeSpec.setHasShadow(attributes.getBoolean(R.styleable.PolygonImageView_poly_shadow, false));
mPolygonShapeSpec.setShadowColor(attributes.getColor(R.styleable.PolygonImageView_poly_shadow_color, Color.BLACK));
mPolygonShapeSpec.setHasBorder(attributes.getBoolean(R.styleable.PolygonImageView_poly_border, false));
mPolygonShapeSpec.setBorderColor(attributes.getColor(R.styleable.PolygonImageView_poly_border_color, Color.WHITE));
mPolygonShapeSpec.setBorderWidth(attributes.getDimension(R.styleable.PolygonImageView_poly_border_width, 4));
} finally {
attributes.recycle();
}
init();
}
/**
* Init paints and effects
*/
@SuppressLint("NewApi")
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setPathEffect(new CornerPathEffect(mPolygonShapeSpec.getCornerRadius()));
mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setPathEffect(new CornerPathEffect(mPolygonShapeSpec.getCornerRadius()));
if(mPolygonShapeSpec.hasBorder()) {
mBorderPaint.setColor(mPolygonShapeSpec.getBorderColor());
mBorderPaint.setStrokeWidth(mPolygonShapeSpec.getBorderWidth());
}
if (mPolygonShapeSpec.hasShadow()) {
//Shadow on border even if isBordered is false. Better effect and performance that
//using shadow on main paint
mBorderPaint.setShadowLayer(mPolygonShapeSpec.getShadowRadius(), mPolygonShapeSpec.getShadowXOffset(),
mPolygonShapeSpec.getShadowYOffset(), mPolygonShapeSpec.getShadowColor());
}
// //Avoid known shadow problems
// if (Build.VERSION.SDK_INT > 13)
// setLayerType(LAYER_TYPE_SOFTWARE, null);
mPolygonShape = new RegularPolygonShape();
}
/**
* Gets incoming new canvas size and updates polygon form and image if needed.
*
* @param w new Width
* @param h new Height
* @param oldW old Width
* @param oldH old Height
*/
@Override
protected void onSizeChanged(int w, int h, int oldW, int oldH) {
super.onSizeChanged(w, h, oldW, oldH);
canvasWidth = w;
canvasHeight = h;
updatePolygonSize();
if (Math.min(canvasWidth, canvasHeight) != Math.min(oldW, oldH)) {
refreshImage();
}
}
/**
* Force Override to solve bug on Lollipop
*
* @param widthMeasureSpec Width Spec Measure
* @param heightMeasureSpec Height Spec Measure
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
setMeasuredDimension(width, height);
}
private int measureWidth(int measureSpecWidth) {
return measure(measureSpecWidth);
}
private int measureHeight(int measureSpecHeight) {
//Force do not square measure to solve bug (use base 2 better performance)
return (measure(measureSpecHeight) + 2);
}
private int measure(int measureSpec) {
int result;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY || specMode == MeasureSpec.AT_MOST) {
result = specSize;
} else {
result = Math.min(canvasWidth, canvasHeight);
}
return result;
}
/**
* Draw the polygon form.
*
* @param canvas main canvas
*/
@Override
protected void onDraw(Canvas canvas) {
if (getDrawable() == null || getDrawable().getIntrinsicWidth() == 0 ||
getDrawable().getIntrinsicHeight() == 0)
return;
switch (mPolygonShapeSpec.getNumVertex()) {
case 0: //CIRCLE
if (mPolygonShapeSpec.hasShadow() || mPolygonShapeSpec.hasBorder()) {
canvas.drawCircle(mPolygonShapeSpec.getCenterX(), mPolygonShapeSpec.getCenterY(),
mPolygonShapeSpec.getDiameter() / 2, mBorderPaint);
}
canvas.drawCircle(mPolygonShapeSpec.getCenterX(), mPolygonShapeSpec.getCenterY(),
mPolygonShapeSpec.getDiameter() / 2, mPaint);
break;
case 1: //REGULAR IMAGE VIEW
super.onDraw(canvas);
break;
case 2: //SQUARE
if (mPolygonShapeSpec.hasShadow() || mPolygonShapeSpec.hasBorder()) {
canvas.drawRect(mPolygonShapeSpec.getCenterX() - mPolygonShapeSpec.getDiameter() / 2,
mPolygonShapeSpec.getCenterY() - mPolygonShapeSpec.getDiameter() / 2,
mPolygonShapeSpec.getCenterX() + mPolygonShapeSpec.getDiameter() / 2,
mPolygonShapeSpec.getCenterY() + mPolygonShapeSpec.getDiameter() / 2,
mBorderPaint);
}
canvas.drawRect(mPolygonShapeSpec.getCenterX() - mPolygonShapeSpec.getDiameter() / 2,
mPolygonShapeSpec.getCenterY() - mPolygonShapeSpec.getDiameter() / 2,
mPolygonShapeSpec.getCenterX() + mPolygonShapeSpec.getDiameter() / 2,
mPolygonShapeSpec.getCenterY() + mPolygonShapeSpec.getDiameter() / 2,
mPaint);
break;
default: //POLYGON
if (mPolygonShapeSpec.hasShadow() || mPolygonShapeSpec.hasBorder())
canvas.drawPath(mPath, mBorderPaint);
canvas.drawPath(mPath, mPaint);
}
}
/**
* Take cares about padding changes.
*
* @param start start
* @param top top
* @param end end
* @param bottom bottom
*/
@SuppressLint("Override")
public void setPaddingRelative(int start, int top, int end, int bottom) {
super.setPadding(start, top, end, bottom);
updatePolygonSize();
invalidate();
}
/**
* Take cares about padding changes.
*
* @param left left
* @param top top
* @param right right
* @param bottom bottom
*/
@Override
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom);
updatePolygonSize(left, top, right, bottom);
invalidate(left, top, right, bottom);
}
/**
* Update image.
*
* @param bm new image.
*/
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
refreshImage();
invalidate();
}
/**
* Update image.
*
* @param drawable new image.
*/
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
refreshImage();
invalidate();
}
/**
* Update image.
*
* @param resId new image.
*/
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
refreshImage();
invalidate();
}
/**
* Update image.
*
* @param uri new image.
*/
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
refreshImage();
invalidate();
}
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
invalidate();
}
public void setColorFilterWithBorder(ColorFilter cf) {
mPaint.setColorFilter(cf);
mBorderPaint.setColorFilter(cf);
invalidate();
}
/**
* Refresh image with new canvas size or new image.
*/
@SuppressLint("NewApi")
private void refreshImage() {
Bitmap image = drawableToBitmap(getDrawable());
int canvasSize = Math.min(canvasWidth, canvasHeight);
if (canvasSize > 0 && image != null) {
//Preserve image ratio if it is not square
BitmapShader shader = new BitmapShader(ThumbnailUtils.extractThumbnail(image, canvasSize, canvasSize),
Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(shader);
}
}
/**
* Rebuild polygon after changes, take cares about padding, border and shadow radius.
* Rotate vertices with the variable angle.
*/
private void rebuildPolygon() {
//recalculate new center
float borderNeeded = mPolygonShapeSpec.hasBorder() ? mPolygonShapeSpec.getBorderWidth() : 0;
float shadowNeeded = mPolygonShapeSpec.hasShadow() ? mPolygonShapeSpec.getShadowRadius() : 0;
mPolygonShapeSpec.setCenterX(mPolygonShapeSpec.getDiameter() / 2 + (float) (getPaddingLeft() +
getPaddingRight()) / 2 + borderNeeded + shadowNeeded);
mPolygonShapeSpec.setCenterY(mPolygonShapeSpec.getDiameter() / 2 + (float) (getPaddingTop() +
getPaddingBottom()) / 2 + borderNeeded + shadowNeeded);
if (mPolygonShapeSpec.getNumVertex() < 3)
return;
mPath = mPolygonShape.getPolygonPath(mPolygonShapeSpec);
}
/**
* Update polygon size with unspecified padding.
*/
private void updatePolygonSize() {
updatePolygonSize(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom());
}
/**
* Update polygon with new padding.
*
* @param l Left padding.
* @param t Top padding.
* @param r Right padding.
* @param b Bottom padding.
*/
private void updatePolygonSize(int l, int t, int r, int b) {
if(mPolygonShapeSpec == null)
return;
float borderPadding = mPolygonShapeSpec.hasBorder() ? mPolygonShapeSpec.getBorderWidth() : 0f;
float shadowPadding = mPolygonShapeSpec.hasShadow() ? mPolygonShapeSpec.getShadowRadius() : 0f;
float xPadding = (l + r + (borderPadding * 2) + (shadowPadding * 2));
float yPadding = (t + b + (borderPadding * 2) + (shadowPadding * 2));
float diameter = Math.min((float) canvasWidth - xPadding, (float) canvasHeight - yPadding);
//if the size is changed we need to rebuild the polygon
if (diameter != mPolygonShapeSpec.getDiameter()) {
mPolygonShapeSpec.setDiameter(diameter);
rebuildPolygon();
}
}
/**
*
* @param polygonShape set new shape
*/
public void setPolygonShape(PolygonShape polygonShape) {
mPolygonShape = polygonShape;
rebuildPolygon();
invalidate();
}
/**
*
* @return get current shape
*/
public PolygonShape getPolygonShape() {
return mPolygonShape;
}
/**
*
* @return get current shape spec
*/
public PolygonShapeSpec getPolygonShapeSpec() {
return mPolygonShapeSpec;
}
/**
*
* @param spec set shape spec
*/
public void setPolygonShapeSpec(PolygonShapeSpec spec) {
this.mPolygonShapeSpec = spec;
}
/**
* Returns the rotate angle.
*
* @return angle in degrees.
*/
public float getRotationAngle() {
return mPolygonShapeSpec.getRotation();
}
/**
* Set new rotate angle and updates polygon form.
*
* @param mAngle angle in degrees.
*/
public void setRotationAngle(float mAngle) {
mPolygonShapeSpec.setRotation(mAngle);
rebuildPolygon();
invalidate();
}
/**
* Returns the vertex number.
*
* @return vertex number
*/
public int getVertices() {
return mPolygonShapeSpec.getNumVertex();
}
/**
* Sets the new vertex number and updates polygon form.
*
* @param numVertices new number of vertices
*/
public void setVertices(int numVertices) {
mPolygonShapeSpec.setNumVertex(numVertices);
rebuildPolygon();
invalidate();
}
/**
* Indicates if it's bordered.
*
* @return boolean
*/
public boolean isBordered() {
return mPolygonShapeSpec.hasBorder();
}
/**
* Enables or disables the border option.
*
* @param bordered if it's bordered
*/
public void setBorder(boolean bordered) {
mPolygonShapeSpec.setHasBorder(bordered);
updateBorderSpecs();
}
/**
* Sets new border width.
*
* @param borderWidth new width.
*/
public void setBorderWidth(float borderWidth) {
mPolygonShapeSpec.setBorderWidth(borderWidth * (getResources().getDisplayMetrics().density));
updateBorderSpecs();
}
/**
* Sets new border width and update polygon size.
*/
private void updateBorderSpecs() {
if(mPolygonShapeSpec.hasBorder()) {
mBorderPaint.setStrokeWidth(mPolygonShapeSpec.getBorderWidth());
mBorderPaint.setColor(mPolygonShapeSpec.getBorderColor());
} else {
mBorderPaint.setStrokeWidth(0);
mBorderPaint.setColor(0);
}
updatePolygonSize();
invalidate();
}
/**
* Sets new border color.
*
* @param borderColor Color class var.
*/
public void setBorderColor(int borderColor) {
mPolygonShapeSpec.setBorderColor(borderColor);
mBorderPaint.setColor(borderColor);
invalidate();
}
/**
* Sets new border color
*
* @param resourceBorderColor Resource xml color.
*/
public void setBorderColorResource(int resourceBorderColor) {
setBorderColor(getResources().getColor(resourceBorderColor));
}
public void addBorder(float borderWidth, int borderColor) {
mPolygonShapeSpec.setHasBorder(true);
mPolygonShapeSpec.setBorderWidth(borderWidth * (getResources().getDisplayMetrics().density));
mPolygonShapeSpec.setBorderColor(borderColor);
updateBorderSpecs();
}
public void addBorderResource(float borderWidth, int resourceBorderColor) {
addBorder(borderWidth, getResources().getColor(resourceBorderColor));
}
/**
* Sets new radius for corners and updates view.
*
* @param cornerRadius new corner radius
*/
public void setCornerRadius(float cornerRadius) {
mPolygonShapeSpec.setCornerRadius(cornerRadius);
mBorderPaint.setPathEffect(new CornerPathEffect(cornerRadius));
mPaint.setPathEffect(new CornerPathEffect(cornerRadius));
invalidate();
}
/**
* Adds a default shadow
*/
public void addShadow() {
startShadow();
}
/**
* Adds a specific shadow.
*
* @param radius shadow blur size.
* @param offsetX negative value moves shadow to left and positive to right.
* @param offsetY negative value moves shadow down and positive towards up.
* @param color shadow color
*/
public void addShadow(float radius, float offsetX, float offsetY, int color) {
mPolygonShapeSpec.setShadowRadius(radius);
mPolygonShapeSpec.setShadowXOffset(offsetX);
mPolygonShapeSpec.setShadowYOffset(offsetY);
mPolygonShapeSpec.setShadowColor(color);
startShadow();
}
public void addShadowResource(float radius, float offsetX, float offsetY, int color) {
addShadow(radius, offsetX, offsetY, getResources().getColor(color));
}
private void startShadow() {
mPolygonShapeSpec.setHasShadow(true);
mBorderPaint.setShadowLayer(mPolygonShapeSpec.getShadowRadius(), mPolygonShapeSpec.getShadowXOffset(),
mPolygonShapeSpec.getShadowYOffset(), mPolygonShapeSpec.getShadowColor());
updatePolygonSize();
invalidate();
}
/**
* Removes shadow
*/
public void clearShadow() {
if (!mPolygonShapeSpec.hasShadow())
return;
mPolygonShapeSpec.setHasShadow(false);
mBorderPaint.clearShadowLayer();
updatePolygonSize();
invalidate();
}
/**
* Transforms a drawable into a bitmap.
*
* @param drawable incoming drawable
* @return new bitmap
*/
private static Bitmap drawableToBitmap(Drawable drawable) {
if (drawable == null) {
return null;
} else if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
//Avoid Color Drawable special case
int width = drawable.getIntrinsicWidth();
width = width > 0 ? width : 1;
int height = drawable.getIntrinsicHeight();
height = height > 0 ? height : 1;
Bitmap bitmap;
try {
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
} catch (OutOfMemoryError e) {
Log.e("PolygonImageView", "OutOfMemory during bitmap creation");
return null;
}
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
}
下面这个是工具类
package net.grobas.view.util;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Geometry Util
*/
public class GeometryUtil {
/**
* Intersection between line, determined for two points, and a circle with radius x
* @param pointA line point a
* @param pointB line point b
* @param center point center circle
* @param radius circle radius
* @return intersection points
*/
public static List getCircleLineIntersectionPoint(Point pointA, Point pointB, Point center, double radius) {
double baX = pointB.x - pointA.x;
double baY = pointB.y - pointA.y;
double caX = center.x - pointA.x;
double caY = center.y - pointA.y;
double a = baX * baX + baY * baY;
double bBy2 = baX * caX + baY * caY;
double c = caX * caX + caY * caY - radius * radius;
double pBy2 = bBy2 / a;
double q = c / a;
double disc = pBy2 * pBy2 - q;
if (disc < 0) {
return Collections.emptyList();
}
double tmpSqrt = Math.sqrt(disc);
double abScalingFactor1 = -pBy2 + tmpSqrt;
double abScalingFactor2 = -pBy2 - tmpSqrt;
Point p1 = new Point(pointA.x - baX * abScalingFactor1, pointA.y
- baY * abScalingFactor1);
if (disc == 0) {
return Collections.singletonList(p1);
}
Point p2 = new Point(pointA.x - baX * abScalingFactor2, pointA.y
- baY * abScalingFactor2);
return Arrays.asList(p1, p2);
}
public static class Point {
public double x, y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
}
}
下面是使用的方法
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
android:id="@+id/kitty01"
android:layout_width="250dp"
android:layout_height="250dp"
android:src="@drawable/cat07"
app:poly_border="true"
app:poly_border_color="@android:color/white"
app:poly_border_width="5dp"
app:poly_corner_radius="25"
app:poly_rotation_angle="0"
app:poly_shadow="true"
app:poly_shadow_color="#f01"
app:poly_vertices="6" />
android:layout_width="250dp"
android:layout_height="250dp"
android:src="@drawable/cat03"
app:poly_border="true"
app:poly_border_color="#f01"
app:poly_border_width="5dp"
app:poly_corner_radius="5"
app:poly_rotation_angle="0"
app:poly_shadow="true"
app:poly_vertices="0" />
记得加入配置文件哦
大功告成。可以运行看看哦!
源码地址 http://download.csdn.net/detail/seven2729/8968161
转载请注明:http://blog.csdn.net/seven2729/article/details/47979263