文档章节

Android自定义View 实现图文混排

SuShine
 SuShine
发布于 2015/06/24 13:55
字数 740
阅读 43
收藏 0
点赞 0
评论 0
package com.su.imagetextview;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Paint.FontMetrics;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;


public class FloatImageText extends View {
	private Bitmap mBitmap;
	private final Rect bitmapFrame = new Rect();
	private final Rect tmp = new Rect();
	private int mTargetDentity = DisplayMetrics.DENSITY_DEFAULT;

	private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
	private String mText;
	private ArrayList<TextLine> mTextLines;
	private final int[] textSize = new int[2];

	public FloatImageText(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init();
	}

	public FloatImageText(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	public FloatImageText(Context context) {
		super(context);
		init();
	}

	private void init() {
		mTargetDentity = getResources().getDisplayMetrics().densityDpi;
		mTextLines = new ArrayList<TextLine>();

		mPaint.setTextSize(30);
		mPaint.setColor(Color.RED);

	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int w = 0, h = 0;
		// 图像巨细
		w += bitmapFrame.width();
		h += bitmapFrame.height();

		// 文本宽度
		if (null != mText && mText.length() > 0) {
			mTextLines.clear();
			int size = resolveSize(Integer.MAX_VALUE, widthMeasureSpec);
			measureAndSplitText(mPaint, mText, size);
			final int textWidth = textSize[0], textHeight = textSize[1];
			w += textWidth; // 内容宽度
			if (h < textHeight) { // 内容高度
				h = (int) textHeight;
			}
		}

		w = Math.max(w, getSuggestedMinimumWidth());
		h = Math.max(h, getSuggestedMinimumHeight());

		setMeasuredDimension(resolveSize(w, widthMeasureSpec),
				resolveSize(h, heightMeasureSpec));
	}

	@Override
	protected void onDraw(Canvas canvas) {
		// 绘制图像
		if (null != mBitmap) {
			canvas.drawBitmap(mBitmap, null, bitmapFrame, null);
		}

		// 绘制文本
		TextLine line;
		final int size = mTextLines.size();
		for (int i = 0; i < size; i++) {
			line = mTextLines.get(i);
			canvas.drawText(line.text, line.x, line.y, mPaint);
		}
		System.out.println(mTextLines);
	}

	public void setImageBitmap(Bitmap bm) {
		setImageBitmap(bm, null);
	}

	public void setImageBitmap(Bitmap bm, int left, int top) {
		setImageBitmap(bm, new Rect(left, top, 0, 0));
	}

	public void setImageBitmap(Bitmap bm, Rect bitmapFrame) {
		mBitmap = bm;
		computeBitmapSize(bitmapFrame);
		requestLayout();
		invalidate();
	}

	public void setText(String text) {
		mText = text;
		requestLayout();
		invalidate();
	}

	private void computeBitmapSize(Rect rect) {
		if (null != rect) {
			bitmapFrame.set(rect);
		}
		if (null != mBitmap) {
			if (rect.right == 0 && rect.bottom == 0) {
				final Rect r = bitmapFrame;
				r.set(r.left, r.top,
						r.left + mBitmap.getScaledHeight(mTargetDentity), r.top
								+ mBitmap.getScaledHeight(mTargetDentity));
			}
		} else {
			bitmapFrame.setEmpty();
		}
	}

	private void measureAndSplitText(Paint p, String content, int maxWidth) {
		FontMetrics fm = mPaint.getFontMetrics();
		final int lineHeight = (int) (fm.bottom - fm.top);

		final Rect r = new Rect(bitmapFrame);
		// r.inset(-5, -5);

		final int length = content.length();
		int start = 0, end = 0, offsetX = 0, offsetY = 0;
		int availWidth = maxWidth;
		TextLine line;
		boolean onFirst = true;
		boolean newLine = true;
		while (start < length) {
			end++;
			if (end == length) { // 残剩的不足一行的文本
				if (start <= length - 1) {
					if (newLine)
						offsetY += lineHeight;
					line = new TextLine();
					line.text = content.substring(start, end - 1);
					line.x = offsetX;
					line.y = offsetY;
					mTextLines.add(line);
				}
				break;
			}
			p.getTextBounds(content, start, end, tmp);
			if (onFirst) { // 确定每个字符串的坐标
				onFirst = false;
				final int height = lineHeight + offsetY;
				if (r.top >= height) { // 顶部可以放下一行文字
					offsetX = 0;
					availWidth = maxWidth;
					newLine = true;
				} else if (newLine
						&& (r.bottom >= height && r.left >= tmp.width())) { // 中部左边可以放文字
					offsetX = 0;
					availWidth = r.left;
					newLine = false;
				} else if (r.bottom >= height
						&& maxWidth - r.right >= tmp.width()) { // 中部右边
					offsetX = r.right;
					availWidth = maxWidth - r.right;
					newLine = true;
				} else { // 底部
					offsetX = 0;
					availWidth = maxWidth;
					if (offsetY < r.bottom)
						offsetY = r.bottom;
					newLine = true;
				}
			}

			if (tmp.width() > availWidth) { // 保留一行能放置的最大字符串
				onFirst = true;
				line = new TextLine();
				line.text = content.substring(start, end - 1);
				line.x = offsetX;
				mTextLines.add(line);
				if (newLine) {
					offsetY += lineHeight;
					line.y = offsetY;
				} else {
					line.y = offsetY + lineHeight;
				}

				start = end - 1;
			}
		}
		textSize[1] = offsetY;
	}

	class TextLine {
		String text;
		int x;
		int y;

		@Override
		public String toString() {
			return "TextLine [text=" + text + ", x=" + x + ", y=" + y + "]";
		}
	}
}


package com.su.imagetextview;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.Menu;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		FloatImageText view = new FloatImageText(this);

		view.setText("电视里发生了房间里是积分拉萨积分拉萨积分拉萨减肥啦空间  撒旦法发大水发撒旦法看完了鸡肉味容积率为热键礼物i经二路文件容量为积分拉萨解放路口上飞机撒离开房间爱水立方法拉圣诞节福禄寿");
		Bitmap bm = BitmapFactory.decodeResource(getResources(),
				R.drawable.ic_launcher);
		view.setImageBitmap(bm, 480 - bm.getWidth(),15);

		setContentView(view);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.activity_main, menu);

		return true;
	}

}


本文转载自:http://blog.csdn.net/sfshine/article/details/8451653

共有 人打赏支持
SuShine
粉丝 118
博文 435
码字总数 88625
作品 0
青岛
后端工程师
Android 解决图文混排,图片和文字居中对齐问题(ImageSpan)

一. 概述 众所周知,Android图文混排可以通过SpannableString来实现,通过在相应位置使用ImageSpan替换即可,但是使用过的人会发现,imageSpan只提供了两种对齐方式,如下所示: 使用和运行效...

Mr云台 ⋅ 2017/04/10 ⋅ 0

Android动画:献上一份详细 & 全面的动画知识学习攻略

前言 动画的使用 是 开发中常用的知识 可是动画的种类繁多、使用复杂,每当需要 采用自定义动画 实现 复杂的动画效果时,很多开发者就显得束手无策 本文将献上一份动画的全面介绍攻略,包括动...

Carson_Ho ⋅ 06/06 ⋅ 0

Android 动画:这是一份详细 & 清晰的 动画学习指南

前言 动画的使用 是 开发中常用的知识 可是动画的种类繁多、使用复杂,每当需要 采用自定义动画 实现 复杂的动画效果时,很多开发者就显得束手无策 本文将献上一份动画的全面介绍攻略,包括动...

Carson_Ho ⋅ 05/03 ⋅ 0

android高仿抖音、点餐界面、天气项目、自定义view指示、爬取美女图片等源码

Android精选源码 一个爬取美女图片的app(http://www.apkbus.com/thread-600077-1-1.html) Android高仿抖音(http://www.apkbus.com/thread-600081-1-1.html) android一个可以上拉下滑的Ui效果......

逆鳞龙 ⋅ 06/12 ⋅ 0

Android图文混排实现方式详解

在使用TextView的时候,我们经常需要在TextView中进行图文混排,比如在QQ中聊天的消息中的表情,底部tab图标等。 一、场景 二、实现方式 Android官方对TextView的图文混排提供了支持,我们可...

Ruheng ⋅ 2017/08/31 ⋅ 0

Android图文混排实现方式详解

在使用TextView的时候,我们经常需要在TextView中进行图文混排,比如在QQ中聊天的消息中的表情,底部tab图标等。 一、场景 二、实现方式 Android官方对TextView的图文混排提供了支持,我们可...

Ruheng ⋅ 2017/08/30 ⋅ 0

Qt (5.10.0)for android

Qt 是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架,至此实现了全面支持iOS、Android、WP,它提供给应用程序开发者建立艺术级的图形用户界面所需的所有功能。那么如何在...

SmileAgain_ ⋅ 05/14 ⋅ 0

android悬浮窗、收款二维码、相机处理、事件通知库、NFC读取等源码

Android精选源码 一个漂亮而强大的自定义view SeekBar(http://www.apkbus.com/thread-598080-1-1.html) 适用于Android的简单NFC读取源码(http://www.apkbus.com/thread-598082-1-1.html) 安卓......

逆鳞龙 ⋅ 04/24 ⋅ 0

彻底理解 Android 中的阴影

如果我们想创造更好的 Android App,我相信我们需要遵循 Material Design 的设计规范。一般而言,Material Design 是一个包含光线,材质和投影的三维环境。如果我们想要在 App 的开发过程中,...

丁佳辉 ⋅ 04/18 ⋅ 0

android--------自定义控件 之 基本流程篇

在我们平常的Android开发中经常和控件打交道,有时Android提供的控件未必能满足业务的需求,这个时候就需要我们实现自定义一些控件 自定义控件可以设计出很多你想要的功能和模块,在开发中是...

切切歆语 ⋅ 04/30 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

vim编辑模式、vim命令模式、vim实践

vim编辑模式 编辑模式用来输入或修改文本内容,编辑模式除了Esc外其他键几乎都是输入 如何进入编辑模式 一般模式输入以下按键,均可进入编辑模式,左下角提示 insert(中文为插入) 字样 i ...

蛋黄Yolks ⋅ 27分钟前 ⋅ 0

大数据入门基础:SSH介绍

什么是ssh 简单说,SSH是一种网络协议,用于计算机之间的加密登录。 如果一个用户从本地计算机,使用SSH协议登录另一台远程计算机,我们就可以认为,这种登录是安全的,即使被中途截获,密码...

董黎明 ⋅ 46分钟前 ⋅ 0

web3j教程

web3j是一个轻量级、高度模块化、响应式、类型安全的Java和Android类库提供丰富API,用于处理以太坊智能合约及与以太坊网络上的客户端(节点)进行集成。 汇智网最新发布的web3j教程,详细讲解...

汇智网教程 ⋅ 53分钟前 ⋅ 0

谷歌:安全问题机制并不如你想象中安全

腾讯科技讯 5月25日,如今的你或许已经对许多网站所使用的“安全问题机制”习以为常了,但你真的认为包括“你第一个宠物的名字是什么?”这些问题能够保障你的帐户安全吗? 根据谷歌(微博)安...

问题终结者 ⋅ 53分钟前 ⋅ 0

聊聊spring cloud gateway的RedisRateLimiter

序 本文主要研究下spring cloud gateway的RedisRateLimiter GatewayRedisAutoConfiguration spring-cloud-gateway-core-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/gateway/con......

go4it ⋅ 今天 ⋅ 0

169. Majority Element - LeetCode

Question 169. Majority Element Solution 思路:构造一个map存储每个数字出现的次数,然后遍历map返回出现次数大于数组一半的数字. 还有一种思路是:对这个数组排序,次数超过n/2的元素必然在中...

yysue ⋅ 今天 ⋅ 0

NFS

14.1 NFS介绍 NFS是Network File System的缩写 NFS最早由Sun公司开发,分2,3,4三个版本,2和3由Sun起草开发,4.0开始Netapp公司参与并主导开发,最新为4.1版本 NFS数据传输基于RPC协议,RPC...

派派菠菜 ⋅ 今天 ⋅ 0

18.进入编辑模式 vim命令模式 实践

5.5 进入编辑模式 5.6 vim命令模式 5.7 vim实践 5.5 进入编辑模式: i 在当前字符前插入 I 在光标所在行的行首插入 a 在当前字符后插入 A 在光标所在行的行尾插入 o 在当前所在行的下一行插入...

王鑫linux ⋅ 今天 ⋅ 0

阻塞队列(2)--LinkedBlockingDeque底层实现

2.1 LinkedBlockingQueue是什么? 1.1 LinkedBlockingQueue是一个阻塞式的队列,继承自AbstractBlockingQueue,间接的实现了Queue接口和Collection接口。底层以链表的形式保存数据(双向链表,...

yokol ⋅ 今天 ⋅ 0

NFS介绍 NFS服务端安装配置 NFS配置选项

14.1 NFS介绍 14.2 NFS服务端安装配置 14.3 NFS配置选项 NFS介绍 NFS是Network File System的缩写;这个文件系统是基于网路层面,通过网络层面实现数据同步 NFS最早由Sun公司开发,分2,3,4三...

lyy549745 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部