android TextView 文本前后添加图片或者布局

效果图:

代码实现(kotlin版,java 版本在最后):

MainActivity:
package com.tencent.myapplication

import android.annotation.SuppressLint
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Paint.FontMetricsInt
import android.os.Bundle
import android.text.Spannable
import android.text.SpannableString
import android.text.style.ReplacementSpan
import android.view.LayoutInflater
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity


fun dp2px(dpValue: Float): Int {
    val scale = Resources.getSystem().displayMetrics.density
    return (dpValue * scale + 0.5f).toInt()
}

fun view2Bitmap(view: View?): Bitmap? {
    if (view == null) return null
    val drawingCacheEnabled = view.isDrawingCacheEnabled
    val willNotCacheDrawing = view.willNotCacheDrawing()
    view.isDrawingCacheEnabled = true
    view.setWillNotCacheDrawing(false)
    var drawingCache = view.drawingCache
    val bitmap: Bitmap
    if (null == drawingCache || drawingCache.isRecycled) {
        view.measure(
            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
        )
        view.layout(0, 0, view.measuredWidth, view.measuredHeight)
        view.buildDrawingCache()
        drawingCache = view.drawingCache
        if (null == drawingCache || drawingCache.isRecycled) {
            bitmap = Bitmap.createBitmap(
                view.measuredWidth,
                view.measuredHeight,
                Bitmap.Config.RGB_565
            )
            val canvas = Canvas(bitmap)
            view.draw(canvas)
        } else {
            bitmap = Bitmap.createBitmap(drawingCache)
        }
    } else {
        bitmap = Bitmap.createBitmap(drawingCache)
    }
    view.setWillNotCacheDrawing(willNotCacheDrawing)
    view.isDrawingCacheEnabled = drawingCacheEnabled
    return bitmap
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val tagStr = "热门"
        val name = "名称特别特别特别特别特别特别特别特别特别特别特别特别特别特别特别特别特别特别特别特别长"
        val stateStr = "进行中"

        val title = tagStr + name + stateStr
        val spannableStr = SpannableString(title)
        // 设置 tag。
        spannableStr.setSpan(
            TagSpan(tagStr),
            title.indexOf(tagStr),
            title.indexOf(tagStr) + tagStr.length,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
        // 设置 state。
        spannableStr.setSpan(
            StateSpan(stateStr),
            title.indexOf(stateStr),
            title.indexOf(stateStr) + stateStr.length,
            Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
        val tvTitle = findViewById<TextView>(R.id.text_view)
        tvTitle.text = spannableStr
    }

    @SuppressLint("InflateParams")
    inner class TagSpan(tagStr: String) : ReplacementSpan() {
        private val view: View =
            LayoutInflater.from(this@MainActivity).inflate(R.layout.layout_tag, null)
        private val marginEnd: Int = dp2px(5f)
        private val mWidth: Int

        init {
            val tvName = view.findViewById<TextView>(R.id.tv_name_tag)
            tvName.text = tagStr
            val width = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            val height = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            view.measure(width, height)
            mWidth = view.measuredWidth // 获取宽度
        }

        override fun getSize(
            paint: Paint,
            text: CharSequence,
            start: Int,
            end: Int,
            fm: FontMetricsInt?
        ): Int {
            return mWidth + marginEnd
        }

        override fun draw(
            canvas: Canvas,
            text: CharSequence,
            start: Int,
            end: Int,
            x: Float,
            top: Int,
            y: Int, bottom: Int,
            paint: Paint
        ) {
            paint.isAntiAlias = true
            val bmp: Bitmap? = view2Bitmap(view)
            bmp?.let {
                canvas.drawBitmap(bmp, x, y + paint.ascent(), paint)
            }
        }
    }

    @SuppressLint("InflateParams")
    inner class StateSpan(stateStr: String) : ReplacementSpan() {
        private val view: TextView = LayoutInflater.from(this@MainActivity)
            .inflate(R.layout.layout_state, null) as TextView
        private val marginStart: Int = dp2px(5f)
        private val mWidth: Int

        init {
            val tvName = view.findViewById<TextView>(R.id.tv_name_state)
            tvName.text = stateStr
            val width = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            val height = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            view.measure(width, height)
            mWidth = view.measuredWidth // 获取宽度
        }

        override fun getSize(
            paint: Paint,
            text: CharSequence,
            start: Int,
            end: Int,
            fm: FontMetricsInt?
        ): Int {
            return mWidth + marginStart
        }

        override fun draw(
            canvas: Canvas,
            text: CharSequence,
            start: Int,
            end: Int,
            x: Float,
            top: Int,
            y: Int, bottom: Int,
            paint: Paint
        ) {
            paint.isAntiAlias = true
            val bmp: Bitmap? = view2Bitmap(view)
            bmp?.let {
                canvas.drawBitmap(bmp, x + marginStart, y + paint.ascent(), paint)
            }
        }
    }
}

布局文件:

layout_tag:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/shape_tag_bg"
    android:paddingHorizontal="6dp"
    android:paddingVertical="2dp">

    <ImageView
        android:id="@+id/iv_icon_tag"
        android:layout_width="14dp"
        android:layout_height="14dp"
        android:src="@drawable/ic_tag_hot"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_name_tag"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="热门"
        android:textColor="#FFFFFF"
        android:textSize="12dp"
        app:layout_constraintBottom_toBottomOf="@id/iv_icon_tag"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/iv_icon_tag"
        app:layout_constraintTop_toTopOf="@id/iv_icon_tag" />

</androidx.constraintlayout.widget.ConstraintLayout>

layout_state:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tv_name_state"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/shape_state_bg"
    android:paddingHorizontal="6dp"
    android:text="进行中"
    android:textColor="#D1291A" />

shape_tag_bg:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <gradient
        android:endColor="#D1291A"
        android:startColor="#FC3B16" />
    <corners
        android:bottomLeftRadius="0dp"
        android:bottomRightRadius="8dp"
        android:topLeftRadius="8dp"
        android:topRightRadius="0dp" />
</shape>

shape_state_bg:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <corners android:radius="99dp" />

    <stroke
        android:width="1dp"
        android:color="#D1291A" />
</shape>

代码实现(java版),布局文件跟上面一样:

package com.tencent.myapplication;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ReplacementSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity2 extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        TextView tvTitle = findViewById(R.id.text_view);
        String tagStr = "热门";
        String name = "名称特别特别特别特别特别特别特别特别特别特别特别特别特别特别特别特别特别特别特别特别长";
        String stateStr = "进行中";

        String title = tagStr + name + stateStr;
        SpannableString spannableStr = new SpannableString(title);
        // 设置 tag。
        spannableStr.setSpan(new TagSpan(tagStr), title.indexOf(tagStr), title.indexOf(tagStr) + tagStr.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
        // 设置 state。
        spannableStr.setSpan(new StateSpan(stateStr), title.indexOf(stateStr), title.indexOf(stateStr) + stateStr.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
        tvTitle.setText(spannableStr);
    }

    public class TagSpan extends ReplacementSpan {
        private final View view;

        private final int marginEnd = dp2px(5);
        private final int mWidth;

        public TagSpan(String tagStr) {
            view = LayoutInflater.from(MainActivity2.this).inflate(R.layout.layout_tag, null);
            TextView tvName = view.findViewById(R.id.tv_name_tag);
            tvName.setText(tagStr);
            int width = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
            int height = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
            view.measure(width, height);
            mWidth = view.getMeasuredWidth(); // 获取宽度
        }

        @Override
        public int getSize(@NonNull Paint paint,
                           CharSequence text,
                           int start,
                           int end,
                           Paint.FontMetricsInt fm) {
            return mWidth + marginEnd;
        }

        @Override
        public void draw(@NonNull Canvas canvas,
                         CharSequence text,
                         int start,
                         int end,
                         float x,
                         int top,
                         int y, int bottom,
                         @NonNull Paint paint) {
            paint.setAntiAlias(true);
            Bitmap bmp = view2Bitmap(view);
            canvas.drawBitmap(bmp, x, y + paint.ascent(), paint);
        }
    }

    public class StateSpan extends ReplacementSpan {
        private final TextView view;

        private final int marginStart = dp2px(5);
        private final int mWidth;

        public StateSpan(String stateStr) {
            view = (TextView) LayoutInflater.from(MainActivity2.this).inflate(R.layout.layout_state, null);
            view.setText(stateStr);
            int width = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
            int height = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
            view.measure(width, height);
            mWidth = view.getMeasuredWidth(); // 获取宽度
        }

        @Override
        public int getSize(@NonNull Paint paint,
                           CharSequence text,
                           int start,
                           int end,
                           Paint.FontMetricsInt fm) {
            return mWidth + marginStart;
        }

        @Override
        public void draw(@NonNull Canvas canvas,
                         CharSequence text,
                         int start,
                         int end,
                         float x,
                         int top,
                         int y, int bottom,
                         @NonNull Paint paint) {
            paint.setAntiAlias(true);
            Bitmap bmp = view2Bitmap(view);
            canvas.drawBitmap(bmp, x + marginStart, y + paint.ascent(), paint);
        }
    }

    public static int dp2px(final float dpValue) {
        final float scale = Resources.getSystem().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public static Bitmap view2Bitmap(final View view) {
        if (view == null) {
            return null;
        }
        boolean drawingCacheEnabled = view.isDrawingCacheEnabled();
        boolean willNotCacheDrawing = view.willNotCacheDrawing();
        view.setDrawingCacheEnabled(true);
        view.setWillNotCacheDrawing(false);
        Bitmap drawingCache = view.getDrawingCache();
        Bitmap bitmap;
        if (null == drawingCache || drawingCache.isRecycled()) {
            view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
            view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
            view.buildDrawingCache();
            drawingCache = view.getDrawingCache();
            if (null == drawingCache || drawingCache.isRecycled()) {
                bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.RGB_565);
                Canvas canvas = new Canvas(bitmap);
                view.draw(canvas);
            } else {
                bitmap = Bitmap.createBitmap(drawingCache);
            }
        } else {
            bitmap = Bitmap.createBitmap(drawingCache);
        }
        view.setWillNotCacheDrawing(willNotCacheDrawing);
        view.setDrawingCacheEnabled(drawingCacheEnabled);
        return bitmap;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值