[英]How to draw background color that wraps text length in textview?
我想創建一個多行文本視圖,其背景顏色跟隨文本長度並具有圓角。 就像instagram一樣。
這是我到目前為止一直在嘗試的
public class BgColorTextView extends TextView {
...
@Override
public void draw(Canvas canvas) {
int lineCount = getLayout().getLineCount();
RectF rect = new RectF();
Paint paint = new Paint();
paint.setColor(bgColor);
for (int i = 0; i < lineCount; i++) {
rect.top = (int) (getLayout().getLineTop(i)+20);
rect.left = (int) (getLayout().getLineLeft(i)+10);
rect.right = (int) (getLayout().getLineRight(i)+40);
rect.bottom = (int) (getLayout().getLineBottom(i) - ((i + 1 == lineCount) ? 0 : getLayout().getSpacingAdd())+40);
canvas.drawRoundRect(rect, 25, 25, paint);
}
super.draw(canvas);
}
這是我得到的結果:
有人可以指出我該如何解決嗎? 唯一的問題是其他兩個角落沒有變圓。
使用簡單的Canvas內置形狀繪制方法實際上無法做到這一點,因為您需要繪制凹角弧。 但是,您可以使用Path原語操作來構建所需的形狀。
可以想象您可以創建一條路徑來環繞文本,但是我發現為每條線創建單獨的路徑並確定是否需要像普通的圓角矩形(即凸形,正半徑)那樣上角或將彎角(凹形上)變得更簡單。 (負半徑),具體取決於線邊界與上下邊界的對齊方式。
我在下面提供了一個示例,說明如何將其作為派生的TextView類的完全實現實現。
關於此實現的一些其他注意事項:
生成路徑的主要工作是在onSizeChanged()
方法中完成的:在draw()
方法中進行大量計算和/或分配是不好的做法。 相反, draw()
方法只是在畫布上繪制預先計算的路徑。
棘手的地方是Path roundedRect()
方法,該方法為每條線創建路徑。 如果所有角的半徑均為零,則RectF
參數為矩形的形狀,每個float
參數分別給出左上角,右上角,左下角和右下角的半徑。 正半徑指定正常的圓角,負半徑指定倒角的凹角,零則指定不帶圓角的直角。
也可以添加自定義屬性來控制自定義窗口小部件的外觀。 我已經在示例attrs.xml
和小部件構造函數中顯示了這一點,以指定要使用的最大拐角半徑。 您可能希望添加其他控件來控制顏色,邊距等,而不是像我在示例中所做的那樣對它們進行硬編碼。
我希望這有幫助。
BgColourTextView.java:
package com.example.bgcolourtextview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.widget.TextView;
import java.util.ArrayList;
public class BgColourTextView extends TextView
{
private static final float DEFAULT_MAX_CORNER_RADIUS_DIP = 10f;
private final Paint mPaint;
private final ArrayList<Path> mOutlines;
private final float mMaxCornerRadius;
public BgColourTextView(Context context) {
this(context, null, 0);
}
public BgColourTextView(Context context, AttributeSet attr) {
this(context, attr, 0);
}
public BgColourTextView(Context context, AttributeSet attr, int defStyle) {
super(context, attr, defStyle);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mOutlines = new ArrayList<>();
TypedArray ta = context.getTheme().obtainStyledAttributes(attr, R.styleable.BgColourTextView, 0, defStyle);
try {
mMaxCornerRadius = ta.getDimension(R.styleable.BgColourTextView_maxCornerRadius,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_MAX_CORNER_RADIUS_DIP,
context.getResources().getDisplayMetrics()));
} finally {
ta.recycle();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mOutlines.clear();
final ArrayList<RectF> lineBounds = new ArrayList<>();
for (int i = 0; i < getLayout().getLineCount(); i++) {
RectF rect = new RectF(getLayout().getLineLeft(i), getLayout().getLineTop(i), getLayout().getLineRight(i), getLayout().getLineBottom(i));
rect.offset(getPaddingLeft(), getPaddingTop());
rect.inset(-getPaddingLeft() / 2f, 0f);
lineBounds.add(rect);
}
for (int i = 0; i < lineBounds.size(); i++) {
float rTl = limitRadius(i == 0 ? lineBounds.get(i).width() / 2f : (lineBounds.get(i - 1).left - lineBounds.get(i).left) / 2f);
float rTr = limitRadius(i == 0 ? lineBounds.get(i).width() / 2f : (lineBounds.get(i).right - lineBounds.get(i - 1).right) / 2f);
float rBl = limitRadius(i == lineBounds.size() - 1 ? lineBounds.get(i).width() / 2f : (lineBounds.get(i + 1).left - lineBounds.get(i).left) / 2f);
float rBr = limitRadius(i == lineBounds.size() - 1 ? lineBounds.get(i).width() / 2f : (lineBounds.get(i).right - lineBounds.get(i + 1).right) / 2f);
mOutlines.add(roundedRect(lineBounds.get(i), rTl, rTr, rBl, rBr));
}
}
@Override
public void draw(Canvas canvas) {
for (Path p: mOutlines) {
canvas.drawPath(p, mPaint);
}
super.draw(canvas);
}
/**
* Limit the corner radius to a maximum absolute value.
*/
private float limitRadius(float aRadius) {
return Math.abs(aRadius) < mMaxCornerRadius ? aRadius : mMaxCornerRadius * aRadius / Math.abs(aRadius);
}
/**
* Generate a rectangular path with rounded corners, where a positive corner radius indicates a normal convex corner and
* negative indicates a concave corner (turning out horizontally rather than round).
*/
private Path roundedRect(RectF aRect, float rTl, float rTr, float rBl, float rBr) {
Log.d("BgColourTextView", String.format("roundedRect(%s, %s, %s, %s, %s)", aRect, rTl, rTr, rBl, rBr));
Path path = new Path();
path.moveTo(aRect.right, aRect.top + Math.abs(rTr));
if (rTr > 0) {
path.arcTo(new RectF(aRect.right - 2 * rTr, aRect.top, aRect.right, aRect.top + 2 * rTr), 0, -90, false);
} else if (rTr < 0) {
path.arcTo(new RectF(aRect.right , aRect.top, aRect.right - 2 * rTr, aRect.top - 2 * rTr), 180, 90, false);
}
path.lineTo(aRect.left + 2 * Math.abs(rTl), aRect.top);
if (rTl > 0) {
path.arcTo(new RectF(aRect.left, aRect.top, aRect.left + 2 * rTl, aRect.top + 2 * rTl), 270, -90, false);
} else if (rTl < 0) {
path.arcTo(new RectF(aRect.left + 2 * rTl, aRect.top, aRect.left, aRect.top - 2 * rTl), 270, 90, false);
}
path.lineTo(aRect.left, aRect.bottom - 2 * Math.abs(rBl));
if (rBl > 0) {
path.arcTo(new RectF(aRect.left, aRect.bottom - 2 * rBl, aRect.left + 2 * rBl, aRect.bottom), 180, -90, false);
} else if (rBl < 0) {
path.arcTo(new RectF(aRect.left + 2 * rBl, aRect.bottom + 2 * rBl, aRect.left, aRect.bottom), 0, 90, false);
}
path.lineTo(aRect.right - 2 * Math.abs(rBr), aRect.bottom);
if (rBr > 0) {
path.arcTo(new RectF(aRect.right - 2 * rBr, aRect.bottom - 2 * rBr, aRect.right, aRect.bottom), 90, -90, false);
} else if (rBr < 0) {
path.arcTo(new RectF(aRect.right, aRect.bottom + 2 * rBr, aRect.right - 2 * rBr, aRect.bottom), 90, 90, false);
}
path.lineTo(aRect.right, aRect.top + Math.abs(rTr));
return path;
}
res / values / attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="BgColourTextView">
<attr name="maxCornerRadius" format="dimension" />
</declare-styleable>
</resources>
RES /布局/ main.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<com.example.bgcolourtextview.BgColourTextView
android:text="Example\n10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:gravity="center_horizontal"
android:textSize="32sp"
app:maxCornerRadius="10dp" />
<com.example.bgcolourtextview.BgColourTextView
android:text="Example 15dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:gravity="center_horizontal"
android:textSize="32sp"
app:maxCornerRadius="15dp" />
<com.example.bgcolourtextview.BgColourTextView
android:text="Example\n20dp radius\ntext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:gravity="center_horizontal"
android:textSize="32sp"
app:maxCornerRadius="20dp" />
<com.example.bgcolourtextview.BgColourTextView
android:text="Example\ntext\n5dp radius\ntext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:gravity="center_horizontal"
android:textSize="32sp"
app:maxCornerRadius="5dp" />
</LinearLayout>
輸出示例:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.