来自github的一位国外大神写的,忘了地址,感谢项目里有用到。
直接上代码
xml 配置 字段含义
app:label_backgroundColor="#C2185B" 标签背景颜色
app:label_distance="20dp" 标签距离
app:label_height="20dp" 标签高度
app:label_orientation="LEFT_TOP" 标签位置 LEFT_TOP 左上 RIGHT_TOP 右上 LEFT_BOTTOM 左下 RIGHT_BOTTOM 右下
app:label_text="HD" 标签文字
app:label_textSize="12sp" 标签字体大小
关键辅助类
LabelViewHelper
package com.textstay;
import com.example.testdemo.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
public class LabelViewHelper {
private static final int LEFT_TOP = 1;
private static final int RIGHT_TOP = 2;
private static final int LEFT_BOTTOM = 3;
private static final int RIGHT_BOTTOM = 4;
private static final int DEFAULT_DISTANCE = 40;
private static final int DEFAULT_HEIGHT = 20;
private static final int DEFAULT_TEXT_SIZE = 14;
private static final int DEFAULT_BACKGROUND_COLOR = 0x9F27CDC0;
private static final int DEFAULT_TEXT_COLOR = 0xFFFFFFFF;
private static final int DEFAULT_ORIENTATION = LEFT_TOP;
private int distance;
private int height;
private String text;
private int backgroundColor;
private int textSize;
private int textColor;
private boolean visual;
private int orientation;
private float startPosX;
private float startPosY;
private float endPosX;
private float endPosY;
private Paint rectPaint;
// simulator
private Path rectPath;
private Paint textPaint;
private Rect textBound;
private Context context;
private int alpha;
public LabelViewHelper(Context context, AttributeSet attrs, int defStyleAttr) {
this.context = context;
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.LabelView, defStyleAttr, 0);
distance = attributes.getDimensionPixelSize(R.styleable.LabelView_label_distance, dip2Px(DEFAULT_DISTANCE));
height = attributes.getDimensionPixelSize(R.styleable.LabelView_label_height, dip2Px(DEFAULT_HEIGHT));
text = attributes.getString(R.styleable.LabelView_label_text);
backgroundColor = attributes.getColor(R.styleable.LabelView_label_backgroundColor, DEFAULT_BACKGROUND_COLOR);
textSize = attributes.getDimensionPixelSize(R.styleable.LabelView_label_textSize, dip2Px(DEFAULT_TEXT_SIZE));
textColor = attributes.getColor(R.styleable.LabelView_label_textColor, DEFAULT_TEXT_COLOR);
visual = attributes.getBoolean(R.styleable.LabelView_label_visual, true);
orientation = attributes.getInteger(R.styleable.LabelView_label_orientation, DEFAULT_ORIENTATION);
attributes.recycle();
rectPaint = new Paint();
rectPaint.setDither(true);
rectPaint.setAntiAlias(true);
rectPaint.setStyle(Paint.Style.STROKE);
rectPaint.setStrokeJoin(Paint.Join.ROUND);
rectPaint.setStrokeCap(Paint.Cap.SQUARE);
rectPath = new Path();
rectPath.reset();
textPaint = new Paint();
textPaint.setDither(true);
textPaint.setAntiAlias(true);
textPaint.setStrokeJoin(Paint.Join.ROUND);
textPaint.setStrokeCap(Paint.Cap.SQUARE);
textBound = new Rect();
}
public void onDraw(Canvas canvas, int measuredWidth, int measuredHeight) {
if (!visual || text == null) {
return;
}
float actualDistance = distance + height / 2;
calcOffset(actualDistance, measuredWidth, measuredHeight);
rectPaint.setColor(backgroundColor);
if (alpha != 0) {
rectPaint.setAlpha(alpha);
}
rectPaint.setStrokeWidth(height);
rectPath.reset();
rectPath.moveTo(startPosX, startPosY);
rectPath.lineTo(endPosX, endPosY);
canvas.drawPath(rectPath, rectPaint);
textPaint.setTextSize(textSize);
textPaint.setColor(textColor);
textPaint.getTextBounds(text, 0, text.length(), textBound);
float begin_w_offset = (1.4142135f * actualDistance) / 2 - textBound.width() / 2;
if (begin_w_offset < 0) begin_w_offset = 0;
canvas.drawTextOnPath(text, rectPath, begin_w_offset, textBound.height() / 2, textPaint);
}
private void calcOffset(float actualDistance, int measuredWidth, int measuredHeight) {
switch (orientation) {
case 1:
startPosX = 0;
startPosY = actualDistance;
endPosX = actualDistance;
endPosY = 0;
break;
case 2:
startPosX = measuredWidth - actualDistance;
startPosY = 0;
endPosX = measuredWidth;
endPosY = actualDistance;
break;
case 3:
startPosX = 0;
startPosY = measuredHeight - actualDistance;
endPosX = actualDistance;
endPosY = measuredHeight;
break;
case 4:
startPosX = measuredWidth - actualDistance;
startPosY = measuredHeight;
endPosX = measuredWidth;
endPosY = measuredHeight - actualDistance;
break;
}
}
private int dip2Px(float dip) {
return (int) (dip * context.getResources().getDisplayMetrics().density + 0.5f);
}
private int px2Dip(float px) {
return (int) (px / context.getResources().getDisplayMetrics().density + 0.5f);
}
public void setLabelHeight(View view, int height) {
if (this.height != dip2Px(height)) {
this.height = dip2Px(height);
view.invalidate();
}
}
public int getLabelHeight() {
return px2Dip(this.height);
}
public void setLabelDistance(View view, int distance) {
if (this.distance != dip2Px(distance)) {
this.distance = dip2Px(distance);
view.invalidate();
}
}
public int getLabelDistance() {
return px2Dip(this.distance);
}
public boolean isLabelVisual() {
return visual;
}
public void setLabelVisual(View view, boolean visual) {
if (this.visual != visual) {
this.visual = visual;
view.invalidate();
}
}
public int getLabelOrientation() {
return orientation;
}
public void setLabelOrientation(View view, int orientation) {
if (this.orientation != orientation && orientation <= 4 && orientation >= 1) {
this.orientation = orientation;
view.invalidate();
}
}
public int getLabelTextColor() {
return textColor;
}
public void setLabelTextColor(View view, int textColor) {
if (this.textColor != textColor) {
this.textColor = textColor;
view.invalidate();
}
}
public int getLabelBackgroundColor() {
return backgroundColor;
}
public void setLabelBackgroundColor(View view, int backgroundColor) {
if (this.backgroundColor != backgroundColor) {
this.backgroundColor = backgroundColor;
view.invalidate();
}
}
public void setLabelBackgroundAlpha(View view, int alpha) {
if (this.alpha != alpha) {
this.alpha = alpha;
view.invalidate();
}
}
public String getLabelText() {
return text;
}
public void setLabelText(View view, String text) {
if (this.text == null || !this.text.equals(text)) {
this.text = text;
view.invalidate();
}
}
public int getLabelTextSize() {
return px2Dip(this.textSize);
}
public void setLabelTextSize(View view, int textSize) {
if (this.textSize != textSize) {
this.textSize = textSize;
view.invalidate();
}
}
}
Button
package com.textstay;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.Button;
public class LabelButtonView extends Button {
LabelViewHelper utils;
public LabelButtonView(Context context) {
this(context, null);
}
public LabelButtonView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LabelButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
utils = new LabelViewHelper(context, attrs, defStyleAttr);
}
@SuppressLint("WrongCall") @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
utils.onDraw(canvas, getMeasuredWidth(), getMeasuredHeight());
}
public void setLabelHeight(int height) {
utils.setLabelHeight(this, height);
}
public int getLabelHeight() {
return utils.getLabelHeight();
}
public void setLabelDistance(int distance) {
utils.setLabelDistance(this, distance);
}
public int getLabelDistance() {
return utils.getLabelDistance();
}
public boolean isLabelVisual() {
return utils.isLabelVisual();
}
public void setLabelVisual(boolean enable) {
utils.setLabelVisual(this, enable);
}
public int getLabelOrientation() {
return utils.getLabelOrientation();
}
public void setLabelOrientation(int orientation) {
utils.setLabelOrientation(this, orientation);
}
public int getLabelTextColor() {
return utils.getLabelTextColor();
}
public void setLabelTextColor(int textColor) {
utils.setLabelTextColor(this, textColor);
}
public int getLabelBackgroundColor() {
return utils.getLabelBackgroundColor();
}
public void setLabelBackgroundColor(int backgroundColor) {
utils.setLabelBackgroundColor(this, backgroundColor);
}
public String getLabelText() {
return utils.getLabelText();
}
public void setLabelText(String text) {
utils.setLabelText(this, text);
}
public int getLabelTextSize() {
return utils.getLabelTextSize();
} public void setLabelTextSize(int textSize) { utils.setLabelTextSize(this, textSize); }}
使用方法
<com.textstay.LabelButtonView
android:id="@+id/labelbutton"
android:layout_width="200dp"
android:layout_height="48dp"
android:background="#03a9f4"
android:gravity="center"
android:text="Button"
android:textColor="#ffffff"
app:label_backgroundColor="#C2185B"
app:label_distance="20dp"
app:label_height="20dp"
app:label_orientation="LEFT_TOP"
app:label_text="HD"
app:label_textSize="12sp" />
Imageview
package com.textstay;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.ImageView;
public class LabelImageView extends ImageView {
LabelViewHelper utils;
public LabelImageView(Context context) {
this(context, null);
}
public LabelImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LabelImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
utils = new LabelViewHelper(context, attrs, defStyleAttr);
}
@SuppressLint("WrongCall") @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
utils.onDraw(canvas, getMeasuredWidth(), getMeasuredHeight());
}
public void setLabelHeight(int height) {
utils.setLabelHeight(this, height);
}
public int getLabelHeight() {
return utils.getLabelHeight();
}
public void setLabelDistance(int distance) {
utils.setLabelDistance(this, distance);
}
public int getLabelDistance() {
return utils.getLabelDistance();
}
public boolean isLabelVisual() {
return utils.isLabelVisual();
}
public void setLabelVisual(boolean enable) {
utils.setLabelVisual(this, enable);
}
public int getLabelOrientation() {
return utils.getLabelOrientation();
}
public void setLabelOrientation(int orientation) {
utils.setLabelOrientation(this, orientation);
}
public int getLabelTextColor() {
return utils.getLabelTextColor();
}
public void setLabelTextColor(int textColor) {
utils.setLabelTextColor(this, textColor);
}
public int getLabelBackgroundColor() {
return utils.getLabelBackgroundColor();
}
public void setLabelBackgroundColor(int backgroundColor) {
utils.setLabelBackgroundColor(this, backgroundColor);
}
public void setLabelBackgroundAlpha(int alpha) {
utils.setLabelBackgroundAlpha(this, alpha);
}
public String getLabelText() {
return utils.getLabelText();
}
public void setLabelText(String text) {
utils.setLabelText(this, text);
}
public int getLabelTextSize() {
return utils.getLabelTextSize();
}
public void setLabelTextSize(int textSize) {
utils.setLabelTextSize(this, textSize);
}
}
<com.textstay.LabelImageView
android:layout_width="200dp"
android:layout_height="100dp"
android:src="@drawable/ic_launcher"
app:label_backgroundColor="#C2185B"
app:label_distance="20dp"
app:label_height="20dp"
app:label_orientation="LEFT_TOP"
app:label_text="HD"
app:label_textSize="12sp"
/>
TextView
package com.textstay;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.TextView;
public class LabelTextView extends TextView {
LabelViewHelper utils;
public LabelTextView(Context context) {
this(context, null);
}
public LabelTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LabelTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
utils = new LabelViewHelper(context, attrs, defStyleAttr);
}
@SuppressLint("WrongCall") @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
utils.onDraw(canvas, getMeasuredWidth(), getMeasuredHeight());
}
public void setLabelHeight(int height) {
utils.setLabelHeight(this, height);
}
public int getLabelHeight() {
return utils.getLabelHeight();
}
public void setLabelDistance(int distance) {
utils.setLabelDistance(this, distance);
}
public int getLabelDistance() {
return utils.getLabelDistance();
}
public boolean isLabelEnable() {
return utils.isLabelVisual();
}
public void setLabelEnable(boolean enable) {
utils.setLabelVisual(this, enable);
}
public int getLabelOrientation() {
return utils.getLabelOrientation();
}
public void setLabelOrientation(int orientation) {
utils.setLabelOrientation(this, orientation);
}
public int getLabelTextColor() {
return utils.getLabelTextColor();
}
public void setLabelTextColor(int textColor) {
utils.setLabelTextColor(this, textColor);
}
public int getLabelBackgroundColor() {
return utils.getLabelBackgroundColor();
}
public void setLabelBackgroundColor(int backgroundColor) {
utils.setLabelBackgroundColor(this, backgroundColor);
}
public String getLabelText() {
return utils.getLabelText();
}
public void setLabelText(String text) {
utils.setLabelText(this, text);
}
public int getLabelTextSize() {
return utils.getLabelTextSize();
}
public void setLabelTextSize(int textSize) {
utils.setLabelTextSize(this, textSize);
}
}
使用方法
<com.textstay.LabelTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="标签textview"
android:textSize="20sp"
app:label_backgroundColor="#C2185B"
app:label_distance="20dp"
app:label_height="10dp"
app:label_orientation="LEFT_TOP"
app:label_text="HOT"
app:label_textSize="12sp"
android:layout_marginBottom="10dp"
/>
View
package com.textstay;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.concurrent.atomic.AtomicInteger;
public class LabelView extends TextView {
private float _offsetx;
private float _offsety;
private float _anchorx;
private float _anchory;
private float _angel;
private int _labelViewContainerID;
private Animation _animation = new Animation() {
protected void applyTransformation(float interpolatedTime, Transformation t) {
Matrix tran = t.getMatrix();
tran.postTranslate(_offsetx, _offsety);
tran.postRotate(_angel, _anchorx, _anchory);
}
};
public enum Gravity {
LEFT_TOP, RIGHT_TOP
}
public LabelView(Context context) {
this(context, null);
}
public LabelView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.textViewStyle);
}
public LabelView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
_animation.setFillBefore(true);
_animation.setFillAfter(true);
_animation.setFillEnabled(true);
}
private void init() {
if (!(getLayoutParams() instanceof ViewGroup.LayoutParams)) {
LayoutParams layoutParams =
new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
setLayoutParams(layoutParams);
}
// the default value
//setPadding(dip2Px(40), dip2Px(2), dip2Px(40), dip2Px(2));
_labelViewContainerID = -1;
setGravity(android.view.Gravity.CENTER);
setTextColor(Color.WHITE);
setTypeface(Typeface.DEFAULT_BOLD);
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
setBackgroundColor(Color.BLUE);
}
public void setTargetView(View target, int distance, Gravity gravity) {
if (!replaceLayout(target)) {
return;
}
final int d = dip2Px(distance);
final Gravity g = gravity;
final View v = target;
ViewTreeObserver vto = getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
getViewTreeObserver().removeGlobalOnLayoutListener(this);
calcOffset(getMeasuredWidth(), d, g, v.getMeasuredWidth(), false);
}
});
}
public void setTargetViewInBaseAdapter(View target, int targetWidth, int distance, Gravity gravity) {
if (!replaceLayout(target)) {
return;
}
//measure(0, 0);
//calcOffset(getMeasuredWidth(), distance, gravity, targetWidth, true);
calcOffset(dip2Px(targetWidth), distance, gravity, targetWidth, true);
}
public void remove() {
if (getParent() == null || _labelViewContainerID == -1) {
return;
}
ViewGroup frameContainer = (ViewGroup) getParent();
assert (frameContainer.getChildCount() == 2);
View target = frameContainer.getChildAt(0);
ViewGroup parentContainer = (ViewGroup) frameContainer.getParent();
int groupIndex = parentContainer.indexOfChild(frameContainer);
if (frameContainer.getParent() instanceof RelativeLayout) {
for (int i = 0; i < parentContainer.getChildCount(); i++) {
if (i == groupIndex) {
continue;
}
View view = parentContainer.getChildAt(i);
RelativeLayout.LayoutParams para = (RelativeLayout.LayoutParams) view.getLayoutParams();
for (int j = 0; j < para.getRules().length; j++) {
if (para.getRules()[j] == _labelViewContainerID) {
para.getRules()[j] = target.getId();
}
}
view.setLayoutParams(para);
}
}
ViewGroup.LayoutParams frameLayoutParam = frameContainer.getLayoutParams();
target.setLayoutParams(frameLayoutParam);
parentContainer.removeViewAt(groupIndex);
frameContainer.removeView(target);
frameContainer.removeView(this);
parentContainer.addView(target,groupIndex);
_labelViewContainerID = -1;
}
@SuppressLint("NewApi") private boolean replaceLayout(View target) {
if (getParent() != null || target == null || target.getParent() == null || _labelViewContainerID != -1) {
return false;
}
ViewGroup parentContainer = (ViewGroup) target.getParent();
if (target.getParent() instanceof FrameLayout) {
((FrameLayout) target.getParent()).addView(this);
} else if (target.getParent() instanceof ViewGroup) {
int groupIndex = parentContainer.indexOfChild(target);
_labelViewContainerID = generateViewId();
// relativeLayout need copy rule
if (target.getParent() instanceof RelativeLayout) {
for (int i = 0; i < parentContainer.getChildCount(); i++) {
if (i == groupIndex) {
continue;
}
View view = parentContainer.getChildAt(i);
RelativeLayout.LayoutParams para = (RelativeLayout.LayoutParams) view.getLayoutParams();
for (int j = 0; j < para.getRules().length; j++) {
if (para.getRules()[j] == target.getId()) {
para.getRules()[j] = _labelViewContainerID;
}
}
view.setLayoutParams(para);
}
}
parentContainer.removeView(target);
// new dummy layout
FrameLayout labelViewContainer = new FrameLayout(getContext());
ViewGroup.LayoutParams targetLayoutParam = target.getLayoutParams();
labelViewContainer.setLayoutParams(targetLayoutParam);
target.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
// add target and label in dummy layout
labelViewContainer.addView(target);
labelViewContainer.addView(this);
labelViewContainer.setId(_labelViewContainerID);
// add dummy layout in parent container
parentContainer.addView(labelViewContainer, groupIndex, targetLayoutParam);
}
return true;
}
private void calcOffset(int labelWidth, int distance, Gravity gravity, int targetWidth, boolean isDP) {
int d = dip2Px(distance);
int tw = isDP ? dip2Px(targetWidth) : targetWidth;
float edge = (float) ((labelWidth - 2 * d) / (2 * 1.414));
if (gravity == Gravity.LEFT_TOP) {
_anchorx = -edge;
_offsetx = _anchorx;
_angel = -45;
} else if (gravity == Gravity.RIGHT_TOP) {
_offsetx = tw + edge - labelWidth;
_anchorx = tw + edge;
_angel = 45;
}
_anchory = (float) (1.414 * d + edge);
_offsety = _anchory;
clearAnimation();
startAnimation(_animation);
}
private int dip2Px(float dip) {
return (int) (dip * getContext().getResources().getDisplayMetrics().density + 0.5f);
}
private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
public static int generateViewId() {
for (; ; ) {
final int result = sNextGeneratedId.get();
// aapt-generated IDs have the high byte nonzero; clamp to the range under that.
int newValue = result + 1;
if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
if (sNextGeneratedId.compareAndSet(result, newValue)) {
return result;
}
}
}
}
使用方法 同上