background
Implement a trapezoid TextView.
1. Graphical effect
Code 2.
public class LadderTextView extends android.support.v7.widget.AppCompatTextView {
private static final String TAG = "LadderView";
private Path linePath;
private Paint paint, textPaint;
private int width, height;
private float strokeWidth = 2;
private Region mRegion;
private String textContent;
private int lineOffset = 0;// The offset of the line
private int textOffset = 0;// The offset of the text
private float offsetScale = 1;// The ratio of the difference between the height of the ladder and the bottom (the bottom is longer than the top)
private boolean isLeft = true;// Can be divided into left and right oblique trapezoid mode
private boolean isSelected = false;// Is selected
private int selectedColor = Color.BLACK;
public LadderTextView(Context context) {
super(context);
init();
}
public LadderTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initAttributes(context, attrs);
init();
}
public LadderTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttributes(context, attrs);
init();
}
private void initAttributes(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LadderTextView);
textContent = typedArray.getString(R.styleable.LadderTextView_textContent);
offsetScale = typedArray.getFloat(R.styleable.LadderTextView_offsetScale, 0.5 f);
isLeft = typedArray.getBoolean(R.styleable.LadderTextView_isLeft, true);
isSelected = typedArray.getBoolean(R.styleable.LadderTextView_isSelected, true);
selectedColor = typedArray.getColor(R.styleable.LadderTextView_selectedColor, Color.GREEN);
strokeWidth = typedArray.getDimension(R.styleable.LadderTextView_strokeWidth, 1);
typedArray.recycle();
}
private void init(a) {
Log.v(TAG, "init");
mRegion = new Region();
paint = new Paint();
textPaint = new Paint();
linePath = new Path();
paint.setAntiAlias(true);
paint.setStrokeWidth(dp2px(getContext(), strokeWidth));
paint.setColor(selectedColor);
paint.setStyle(isSelected ? Paint.Style.FILL_AND_STROKE : Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
textPaint.setAntiAlias(true);
textPaint.setTextSize(getTextSize());/ / pass TextSize (px)
textPaint.setColor(isSelected ? Color.WHITE : selectedColor);
setText("");// Remove the original Text content
lineOffset = dp2px(getContext(), strokeWidth) / 2;
textOffset = (int) (getTextSize() / 2) + getBaseline() * 2;
Log.v(TAG, "lineOffset textOffset ->" + lineOffset + "" + textOffset);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
width = getWidth();
height = getHeight();
Log.v(TAG, "width height->" + width + "" + height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.v(TAG, "onDraw");
if (isLeft) {
linePath.moveTo(0 + lineOffset, 0 + lineOffset);
linePath.lineTo(width, 0 + lineOffset);
linePath.lineTo((int) (width - offsetScale * height), height - lineOffset);
linePath.lineTo(0 + lineOffset, height - lineOffset);
linePath.close();
setTextAlignment(TEXT_ALIGNMENT_TEXT_START);
canvas.drawPath(linePath, paint);
canvas.drawText(textContent == null ? "" : textContent,
getPaddingStart() + lineOffset,
height / 2 + textOffset, textPaint);
} else {
linePath.moveTo(0 + lineOffset + offsetScale * height, 0 + lineOffset);
linePath.lineTo(width - lineOffset, 0 + lineOffset);
linePath.lineTo(width - lineOffset, height - lineOffset);
linePath.lineTo(0, height - lineOffset);
linePath.close();
setTextAlignment(TEXT_ALIGNMENT_TEXT_END);
canvas.drawPath(linePath, paint);
canvas.drawText(textContent == null ? "" : textContent,
getWidth() - lineOffset - getPaddingEnd() - getDrawTextWidth(textPaint, textContent),
height / 2+ textOffset, textPaint); }}@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if(! isInRegion(event)) {// Do not respond if the position of the click point is not within the range
return false; }}return super.dispatchTouchEvent(event);
}
/** * Determine whether the click position is within the required range *@param event
* @return* /
public boolean isInRegion(MotionEvent event) {
RectF rectF = new RectF();
linePath.computeBounds(rectF, true);
mRegion.setPath(linePath, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
return mRegion.contains((int) event.getX(), (int) event.getY());
}
/** * gets the width of the string to draw **@param paint
* @param textContent
* @return* /
private int getDrawTextWidth(Paint paint, String textContent) {
float totalWidth = 0f;
if(textContent ! =null && textContent.length() > 0) {
int len = textContent.length();
float[] widths = new float[len];
paint.getTextWidths(textContent, widths);
for (int j = 0; j < len; j++) { totalWidth += widths[j]; }}return (int) Math.ceil(totalWidth);
}
/ * * *@paramDpValue (density in the DisplayMetrics class) *@return* /
private int dp2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5 f);
}
public void setTextContent(String textContent) {
this.textContent = textContent;
invalidate();
}
public void setMSelected(boolean isSelected) {
textPaint.setColor(isSelected ? Color.WHITE : selectedColor);
paint.setStyle(isSelected ? Paint.Style.FILL_AND_STROKE : Paint.Style.STROKE);
this.isSelected = isSelected;
invalidate();
}
@Override
public boolean isSelected(a) {
returnisSelected; }}Copy the code
Key points analysis
1. Drawing background and text content
After calculating the position line of the four points, the default text content of TextView is set as an empty string, and the text display is realized by drawText. It is important to calculate the size of the text, the size of the font, the color, the position in the view, the offset, etc.
2. Click event processing inside and outside the trapezoid range
According to the design, only the click in the trapezoid can respond, so it needs to calculate whether the click position is in the trapezoid, and then distribute the event through dispatchTouchEvent.
/** * Determine whether the click position is within the required range *@param event
* @return* /
public boolean isInRegion(MotionEvent event) {
RectF rectF = new RectF();
linePath.computeBounds(rectF, true);
mRegion.setPath(linePath, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
return mRegion.contains((int) event.getX(), (int) event.getY());
}
Copy the code
3. Other custom attributes
Depending on the requirements, note the invalidate.
In the end,Full code view >>