美文网首页
Android自定义 view之多行文字的绘制&图文混排

Android自定义 view之多行文字的绘制&图文混排

作者: RookieRun | 来源:发表于2020-04-08 23:50 被阅读0次

    一.多行文字的绘制

    image.png

    1.1方案

    1.StaticLayout,用于多行的静态文字的绘制
    2.textPaint.breakText()动态文字的实时拆分与计算

    1.2 talk is cheap

    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.graphics.RectF;
    import android.support.annotation.Nullable;
    import android.text.Layout;
    import android.text.StaticLayout;
    import android.text.TextPaint;
    import android.text.TextUtils;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.View;
    
    
    
    /**
     * 图文混排
     * 1.画图片
     * 2.画多行的文字
     */
    public class ImageTextView extends View {
    
    
        private float width, height;
        private Bitmap bitmap;
        private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        private static final String MULTI_TEXT = "其中R.drawable.danger_build10是一个vector图片,此代码在4.4上运行正常,但在5.0以上的系统会出现空指针,原因在于此本来方法不能将vector转化为bitmap,而apk编译时为了向下兼容,会根据vector生产相应的png,而4.4的系统运行此代码时其实用的是png资源。这就是为什么5.0以上会报错,而4.4不会的原因。";
        private TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        private StaticLayout staticLayout;
        private float[] savedWidths = new float[1];
    
        public ImageTextView(Context context) {
            this(context, null);
        }
    
        public ImageTextView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public ImageTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            bitmap = getBitmap(((int) Utils.dp2px(100)));
            textPaint.setTextSize(Utils.dp2px(18));
            textPaint.setColor(Color.BLACK);
    
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            width = getWidth();
            height = getHeight();
            staticLayout = new StaticLayout(MULTI_TEXT, textPaint, ((int) width), Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            //1.画图片
    //        canvas.drawBitmap(bitmap, width - Utils.dp2px(100), 100, paint);
            //2.画多行文字
            //2.1 StaticLayout 适用于静态文字
    //        staticLayout.draw(canvas);
            //2.2 适用于动态文字,实时计算文字所需宽度并及时换行
            //先切出来第一行
            int index = 0;
            int oldIndex = 0;//用来记录上一行measure了几个字
            int count = 0;//用于记录measure了几次
            int totalOldIndex = 0;//用于记录一行的开头Index
            do {
                oldIndex = index;
                totalOldIndex += oldIndex;
                index = textPaint.breakText(MULTI_TEXT, totalOldIndex, MULTI_TEXT.length(), true, width, savedWidths);
                canvas.drawText(MULTI_TEXT, totalOldIndex, totalOldIndex + index, 0, 60 + textPaint.getFontSpacing() * count, textPaint);
                count++;
            }
            while (oldIndex != index && width != 0 && !TextUtils.isEmpty(MULTI_TEXT));
        }
    
        private Bitmap getBitmap(int width) {
            Bitmap bitmap = null;
            try {
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher, options);
                options.inJustDecodeBounds = false;
                options.inDensity = options.outWidth;
                options.inTargetDensity = width;
                bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher, options);
            } catch (Exception e) {
                Log.e("test", "exception-->" + e.getMessage());
            }
            return bitmap;
        }
    }
    

    二.图文混排

    错误的示范.jpg
    正确的效果.jpg

    2.1思路

    通过FontMetrics/TextBound计算当前行的文字底部&顶部是不是都在图片的高度之外,也就是,文字不能和图片的高度相交

    2.2 talk is cheap

    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.graphics.RectF;
    import android.support.annotation.Nullable;
    import android.text.Layout;
    import android.text.StaticLayout;
    import android.text.TextPaint;
    import android.text.TextUtils;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.View;
    
    
    /**
     * 图文混排
     * 1.画图片
     * 2.画多行的文字
     */
    public class ImageTextView extends View {
    
    
        private float width, height;
        private Bitmap bitmap;
        private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        private static final String MULTI_TEXT = "其中R.drawable.danger_build10是一个vector图片,此代码在4.4上运行正常,但在5.0以上的系统会出现空指针,原因在于此本来方法不能将vector转化为bitmap,而apk编译时为了向下兼容,会根据vector生产相应的png,而4.4的系统运行此代码时其实用的是png资源。这就是为什么5.0以上会报错,而4.4不会的原因。";
        private TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        private StaticLayout staticLayout;
        private float[] savedWidths = new float[1];
        private int bitmapBottom;
        private int bitmapTop;
        final Rect tempBoundRect = new Rect();
    
        public ImageTextView(Context context) {
            this(context, null);
        }
    
        public ImageTextView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public ImageTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            bitmap = getBitmap(((int) Utils.dp2px(100)));
            bitmapTop = 100;
            textPaint.setTextSize(Utils.dp2px(18));
            textPaint.setColor(Color.BLACK);
            //1.2拿到图片的高度范围
            final int height = bitmap.getHeight();
            bitmapBottom = bitmapTop + height;
            Log.e("test", bitmapTop+":bitmapTop=========bitmapBottom:" + bitmapBottom);
            textPaint.getTextBounds(MULTI_TEXT, 0, MULTI_TEXT.length(), tempBoundRect);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            width = getWidth();
            height = getHeight();
            staticLayout = new StaticLayout(MULTI_TEXT, textPaint, ((int) width), Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            //1.画图片
            canvas.drawBitmap(bitmap, width - Utils.dp2px(100), bitmapTop, paint);
    
            //2.画多行文字
            //2.1 StaticLayout 适用于静态文字
    //        staticLayout.draw(canvas);
            //2.2 适用于动态文字,实时计算文字所需宽度并及时换行
            //先切出来第一行
            int index = 0;
            int oldIndex = 0;//用来记录上一行measure了几个字
            int count = 0;//用于记录measure了几次
            int totalOldIndex = 0;//用于记录一行的开头Index
            int totalHeight = 0;//用于记录当前文字的总高度
            do {
                oldIndex = index;
                totalOldIndex += oldIndex;
                //这里画文字的时候,需要考虑,当前文字的上下边界是否与图片有重合的地方
                totalHeight += tempBoundRect.height();
                Log.e("test", "totalHeight:" + totalHeight);
                if (totalHeight >= bitmapTop && totalHeight <= bitmapBottom) {
                    //文字绘制区域需要减去图片的宽度
                    index = textPaint.breakText(MULTI_TEXT, totalOldIndex, MULTI_TEXT.length(), true, width - bitmap.getWidth(), savedWidths);
                    canvas.drawText(MULTI_TEXT, totalOldIndex, totalOldIndex + index, 0, 60 + textPaint.getFontSpacing() * count, textPaint);
                } else {
                    index = textPaint.breakText(MULTI_TEXT, totalOldIndex, MULTI_TEXT.length(), true, width, savedWidths);
                    canvas.drawText(MULTI_TEXT, totalOldIndex, totalOldIndex + index, 0, 60 + textPaint.getFontSpacing() * count, textPaint);
                }
                count++;
                //
            }
            while (oldIndex != index && width != 0 && !TextUtils.isEmpty(MULTI_TEXT));
        }
    
        private Bitmap getBitmap(int width) {
            Bitmap bitmap = null;
            try {
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher, options);
                options.inJustDecodeBounds = false;
                options.inDensity = options.outWidth;
                options.inTargetDensity = width;
                bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher, options);
            } catch (Exception e) {
                Log.e("test", "exception-->" + e.getMessage());
            }
            return bitmap;
        }
    }
    

    相关文章

      网友评论

          本文标题:Android自定义 view之多行文字的绘制&图文混排

          本文链接:https://www.haomeiwen.com/subject/fwffmhtx.html