来自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" />
TextViewpackage 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" />
Viewpackage 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; } } } }
使用方法 同上