我在xml布局中有以下内容:
注意六角形#4不与文本中心对齐。我怎么能这样做:这是我到目前为止所尝试的:
要实际获取带有#inside的视图,我会为这样的视图充气:
//my_hexagon_button.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="0dp"
tools:ignore="MissingPrefix">
<Button
android:id="@+id/tv_icon"
fontPath="proxima_nova_semi_bold.otf"
android:layout_width="16dp"
android:layout_height="17.5dp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="10dp"
android:alpha=".25"
android:background="@drawable/hexagon"
android:clickable="true"
android:contentDescription="@string/content_description"
android:focusable="false"
android:padding="0dp"
android:text="4"
android:textColor="@color/white"
android:textSize="8dp"
/>
</LinearLayout>
在给视图充气后,我会获取其绘图缓存的副本,然后在ImageSpan中使用它。这是我如何获得绘图缓存的副本:
public Bitmap getIconBitmap() {
LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout myRoot = new LinearLayout(getActivity());
// inflate and measure the button then grab its image from the view cache
ViewGroup parent = (ViewGroup) inflater.inflate(R.layout.my_hexagon_button, myRoot);
TextView tv = (TextView) parent.findViewById(R.id.tv_icon);
parent.setDrawingCacheEnabled(true);
parent.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
parent.layout(0, 0, parent.getMeasuredWidth(), parent.getMeasuredHeight());
parent.buildDrawingCache(true);
// if you need bounds on the view, swap bitmap for a drawable and call setbounds, im not using bounds
Bitmap b = Bitmap.createBitmap(parent.getDrawingCache());
parent.setDrawingCacheEnabled(false); // clear drawing cache
return b;
}
所以现在我有一个位图,看起来像我附图中的六角形#4图像。现在让我们在ImageSpan中使用它:
public Spannable createImageSpan(TextView tv,Bitmap bitmapIcon) {
Spannable span = new SpannableString(tv.getText());
int start = span.length() - 1;
int end = span.length();
ImageSpan image = new ImageSpan(new BitmapDrawable(getResources(), bitmapIcon),ImageSpan.ALIGN_BASELINE);
span.setSpan(image, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return span;
}
然后我在我的textview上设置了这个范围。并且它会消失,但图像不会在文本中对齐。注意它是如何掉到底部的。我怎样才能彻底解决这个问题?
答案 0 :(得分:3)
您可以尝试我的CenteredImageSpan
。
您可以通过计算draw
来自定义transY -= (paint.getFontMetricsInt().descent / 2 - 8);
方法。 (祝你好运。:))
public class CenteredImageSpan extends ImageSpan {
private WeakReference<Drawable> mDrawableRef;
// Extra variables used to redefine the Font Metrics when an ImageSpan is added
private int initialDescent = 0;
private int extraSpace = 0;
public CenteredImageSpan(Context context, final int drawableRes) {
super(context, drawableRes);
}
public CenteredImageSpan(Drawable drawableRes, int verticalAlignment) {
super(drawableRes, verticalAlignment);
}
@Override
public int getSize(Paint paint, CharSequence text,
int start, int end,
Paint.FontMetricsInt fm) {
Drawable d = getCachedDrawable();
Rect rect = d.getBounds();
// if (fm != null) {
// Paint.FontMetricsInt pfm = paint.getFontMetricsInt();
// // keep it the same as paint's fm
// fm.ascent = pfm.ascent;
// fm.descent = pfm.descent;
// fm.top = pfm.top;
// fm.bottom = pfm.bottom;
// }
if (fm != null) {
// Centers the text with the ImageSpan
if (rect.bottom - (fm.descent - fm.ascent) >= 0) {
// Stores the initial descent and computes the margin available
initialDescent = fm.descent;
extraSpace = rect.bottom - (fm.descent - fm.ascent);
}
fm.descent = extraSpace / 2 + initialDescent;
fm.bottom = fm.descent;
fm.ascent = -rect.bottom + fm.descent;
fm.top = fm.ascent;
}
return rect.right;
}
@Override
public void draw(@NonNull Canvas canvas, CharSequence text,
int start, int end, float x,
int top, int y, int bottom, @NonNull Paint paint) {
Drawable b = getCachedDrawable();
canvas.save();
// int drawableHeight = b.getIntrinsicHeight();
// int fontAscent = paint.getFontMetricsInt().ascent;
// int fontDescent = paint.getFontMetricsInt().descent;
// int transY = bottom - b.getBounds().bottom + // align bottom to bottom
// (drawableHeight - fontDescent + fontAscent) / 2; // align center to center
int transY = bottom - b.getBounds().bottom;
// this is the key
transY -= (paint.getFontMetricsInt().descent / 2 - 8);
// int bCenter = b.getIntrinsicHeight() / 2;
// int fontTop = paint.getFontMetricsInt().top;
// int fontBottom = paint.getFontMetricsInt().bottom;
// int transY = (bottom - b.getBounds().bottom) -
// (((fontBottom - fontTop) / 2) - bCenter);
canvas.translate(x, transY);
b.draw(canvas);
canvas.restore();
}
// Redefined locally because it is a private member from DynamicDrawableSpan
private Drawable getCachedDrawable() {
WeakReference<Drawable> wr = mDrawableRef;
Drawable d = null;
if (wr != null)
d = wr.get();
if (d == null) {
d = getDrawable();
mDrawableRef = new WeakReference<>(d);
}
return d;
}
}
修改
我实现了以上代码:
Drawable myIcon = getResources().getDrawable(R.drawable.btn_feedback_yellow);
int width = (int) Functions.convertDpToPixel(75, getActivity());
int height = (int) Functions.convertDpToPixel(23, getActivity());
myIcon.setBounds(0, 0, width, height);
CenteredImageSpan btnFeedback = new CenteredImageSpan(myIcon, ImageSpan.ALIGN_BASELINE);
ssBuilder.setSpan(
btnFeedback, // Span to add
getString(R.string.text_header_answer).length() - 1, // Start of the span (inclusive)
getString(R.string.text_header_answer).length(), // End of the span (exclusive)
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);// Do not extend the span when text add later
答案 1 :(得分:3)
对于API> 29,您可以使用ImageSpan.ALIGN_CENTER
常量来执行此操作。 (下面在Kotlin中的代码示例。)
val image: ImageSpan = ImageSpan(
BitmapDrawable(resources, bitmapIcon),
ImageSpan.ALIGN_CENTER);
span.setSpan(image, start, end, 0);
如果您需要支持低于29的API级别(我想大多数人会一段时间),您仍然需要像RoShan Shan的回答那样将ImageSpan子类化。您只需要严格重写draw即可使行为起作用,但是:
class CenteredImageSpanSubclass(
context: Context,
bitmap: Bitmap): ImageSpan(context, bitmap) {
override fun draw(...) {
canvas.save()
val transY = (bottom - top) / 2 - drawable.bounds.height() / 2
canvas.translate(x, transY.toFloat())
drawable.draw(canvas)
canvas.restore()
}
}
答案 2 :(得分:2)
您可以使用此类将ImageSpan与文本对齐
public class VerticalImageSpan extends ImageSpan {
public VerticalImageSpan(Drawable drawable) {
super(drawable);
}
/**
* update the text line height
*/
@Override
public int getSize(Paint paint, CharSequence text, int start, int end,
Paint.FontMetricsInt fontMetricsInt) {
Drawable drawable = getDrawable();
Rect rect = drawable.getBounds();
if (fontMetricsInt != null) {
Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
int fontHeight = fmPaint.descent - fmPaint.ascent;
int drHeight = rect.bottom - rect.top;
int centerY = fmPaint.ascent + fontHeight / 2;
fontMetricsInt.ascent = centerY - drHeight / 2;
fontMetricsInt.top = fontMetricsInt.ascent;
fontMetricsInt.bottom = centerY + drHeight / 2;
fontMetricsInt.descent = fontMetricsInt.bottom;
}
return rect.right;
}
/**
* see detail message in android.text.TextLine
*
* @param canvas the canvas, can be null if not rendering
* @param text the text to be draw
* @param start the text start position
* @param end the text end position
* @param x the edge of the replacement closest to the leading margin
* @param top the top of the line
* @param y the baseline
* @param bottom the bottom of the line
* @param paint the work paint
*/
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end,
float x, int top, int y, int bottom, Paint paint) {
Drawable drawable = getDrawable();
canvas.save();
Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
int fontHeight = fmPaint.descent - fmPaint.ascent;
int centerY = y + fmPaint.descent - fontHeight / 2;
int transY = centerY - (drawable.getBounds().bottom - drawable.getBounds().top) / 2;
canvas.translate(x, transY);
drawable.draw(canvas);
canvas.restore();
}
}
感谢answer
答案 3 :(得分:0)
这是我的解决方案,它支持单行和多行文本
class CenteredImageSpan(dr: Drawable) : ImageSpan(dr) {
private var mDrawableRef: WeakReference<Drawable>? = null
override fun getSize(paint: Paint, text: CharSequence?, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
val d = cachedDrawable
val rect: Rect = d!!.bounds
val pfm = paint.fontMetricsInt
if (fm != null) {
fm.ascent = -rect.height() / 2 + pfm.ascent / 2
fm.descent = Math.max(0, rect.height() / 2 + pfm.ascent / 2)
fm.top = fm.ascent
fm.bottom = fm.descent
}
return rect.right
}
override fun draw(canvas: Canvas, text: CharSequence?, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, @NonNull paint: Paint) {
val b = cachedDrawable!!
canvas.save()
var transY = (bottom + top) / 2 - b.bounds.height() / 2
canvas.translate(x, transY.toFloat())
b.draw(canvas)
canvas.restore()
}
// Redefined locally because it is a private member from DynamicDrawableSpan
private val cachedDrawable: Drawable?
private get() {
val wr: WeakReference<Drawable>? = mDrawableRef
var d: Drawable? = null
if (wr != null) d = wr.get()
if (d == null) {
d = drawable
mDrawableRef = WeakReference(d)
}
return d
}
}
答案 4 :(得分:0)
我发现一种处理所有对齐问题的简便方法是另一种方法。我们将创建一个imageSpan,但位图将来自放大视图。
像这样膨胀图像视图(对齐方式都可以在此处用边距调整):
sessionStorage
您可以通过编程方式创建父视图组,并根据需要将此电视添加到其中。
现在让我们从该电视中获取位图:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/myImage"
android:paddingLeft="3dp"
android:paddingTop="1dp"
android:paddingRight="3dp"
android:paddingBottom="1dp"
android:layout_marginEnd="6dp"
tools:text="for sale" />
现在您已将图像作为位图,只需将其应用到imageSpan并添加您认为合适的图像即可。我喜欢这种方式,因为它无法控制跨度的对齐方式,而不必依赖过于复杂的字体指标。