博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android点阵屏效果的控件
阅读量:5019 次
发布时间:2019-06-12

本文共 10143 字,大约阅读时间需要 33 分钟。

最近发现了一个比较好玩的效果,android实现的LED点阵屏幕效果,挺有意思的,于是花了点时间实现了一下,这个用在演唱会上的粉丝当成牌子举是不是挺好的呢,或者是送给妹子?哈哈~

实现思路比较简单,主要是计算汉字对应的点阵矩阵,汉字通过GB2312编码,每个汉字对用两个byte来表示,而一个汉字被存储为点阵时,以16*16表示,需要16*16=256bit,也就是32byte,GB2312编码也是它在字库文件中的区码和位码,通过这两个值可以计算到这个汉字在字库文件中的相对位置,根据这个位置读取接下来的32位,就对应着这个汉字对应的字模信息,字模信息         其实就是一个byte数组,对于16*16的汉字,对应着长度为32的byte数组。

比如要在手机屏幕上显示两个“我”字,也就是上面的样子,其实对应一个矩阵,称之为点阵矩阵,在存储的时候就是需要保存一个点阵矩阵,如上图的样子,黑色点为true,空心点为false,这个点阵矩阵很容易根据每个汉字的字模信息局算出来,其实就是将字模信息中的每个byte处理一下它的每个bit。

比如“我”这个字,它的字模信息为

[4, -128, 14, -96, 120, -112, 8, -112, 8, -124, -1, -2, 8, -128, 8, -112, 10, -112, 12, 96, 24, 64, 104, -96, 9, 32, 10, 20, 40, 20, 16, 12,]

两个”我”得到就是

[4, -128, 14, -96, 120, -112, 8, -112, 8, -124, -1, -2, 8, -128, 8, -112, 10, -112, 12, 96, 24, 64, 104, -96, 9, 32, 10, 20, 40, 20, 16, 12,

4, -128, 14, -96, 120, -112, 8, -112, 8, -124, -1, -2, 8, -128, 8, -112, 10, -112, 12, 96, 24, 64, 104, -96, 9, 32, 10, 20, 40, 20, 16, 12]

接下来就是将其对应成下面的byte矩阵,当然,对于每个字节,我们保存它的位信息,即保存成boolean值。

0x04,0x80 | 0x04,0x80

0x0E,0xA0 | 0x0E,0xA0

0x78,0x90 | 0x78,0x90

0x08,0x90 | 0x08,0x90

0x08,0x84 | 0x08,0x84

0xFF,0xFE | 0xFF,0xFE

0x08,0x80 | 0x08,0x80

0x08,0x90 | 0x08,0x90

0x0A,0x90 | 0x0A,0x90

0x0C,0x60 | 0x0C,0x60

0x18,0x40 | 0x18,0x40

0x68,0xA0 | 0x68,0xA0

0x09,0x20 | 0x09,0x20

0x0A,0x14 | 0x0A,0x14

0x28,0x14 | 0x28,0x14

0x10,0x0C | 0x10,0x0C

在绘图时,最好还是用SurfaceView,因为滚动时,绘制任务较多,需要注意的几个参数

控件宽度w,高度h,s为点的半径,高度方向上的点的个数为num,于是

点的半径r=(h – (num + 1) * s ) / (2 * num)

第(i,j)个点的坐标为 { (s + r + (s + 2 * r) * j), (s + r + (s + 2 * r) * i)}

当画每个点的时候和点阵矩阵对比,如果对应位置在点阵矩阵中为true就画成实心点,否则为空心点。

如何让启动起来呢?其实也很简单,只需要定时的将这个点阵矩阵的列循环左右移动,然后重绘即可,移动的速度只需要调整定时重绘的间隔。

此外,支持设置颜色textColor,默认绿色

设置点间隔spacing,一般不用设置

设置是否滚动scroll,默认是

设置滚动方向scrollDirection,默认向左

设置滚动速度speed,可选normal和slow,默认normal

效果图

代码:

LedView.java

public class LedView extends TextView {	private FontUtils utils;	/*	 * 一个字用16*16的点阵表示	 */	private int dots = 16;	/*	 * 点阵之间的距离 	 */	private float spacing = 10;	/*	 * 点阵中点的半径	 */	private float radius;	private Paint normalPaint;	private Paint selectPaint;		private String text;	/*	 * 汉字对应的点阵矩阵	 */	private boolean[][] matrix;	/*	 * 是否开启滚动	 */	private boolean scroll;	/*	 * 默认颜色绿色	 */	private int paintColor = Color.GREEN;		private Thread thread;	/*	 * 滚动的text	 */	private volatile boolean scrollText = true;	/*	 * 用来调整滚动速度	 */	private int sleepTime = 100;		/*	 * 滚动方向,默认0向左	 */	private int scrollDirection = 0;		public LedView(Context context, AttributeSet attrs, int defStyleAttr) {		super(context, attrs, defStyleAttr);		TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LedTextView);		int n = typedArray.getIndexCount();		for (int i = 0; i < n; i++) {            int attr = typedArray.getIndex(i);            switch (attr) {            case R.styleable.LedTextView_textColor:            	paintColor = typedArray.getColor(R.styleable.LedTextView_textColor, Color.GREEN);                break;            case R.styleable.LedTextView_spacing:                spacing = typedArray.getDimension(R.styleable.LedTextView_spacing, 10);                break;            case R.styleable.LedTextView_scroll:            	scroll = typedArray.getBoolean(R.styleable.LedTextView_scroll, true);                break;	    case R.styleable.LedTextView_speed:		int speed = typedArray.getInt(R.styleable.LedTextView_speed, 0);		if (0 == speed) {			sleepTime = 100;		}		else {			sleepTime = 300;		}		break;	    case R.styleable.LedTextView_scrollDirection:		scrollDirection = typedArray.getInt(R.styleable.LedTextView_scrollDirection, 0);		break;            }        }		typedArray.recycle();		selectPaint = new Paint();		selectPaint.setStyle(Style.FILL);		selectPaint.setColor(paintColor);		normalPaint = new Paint();		normalPaint.setStyle(Style.STROKE);		normalPaint.setColor(paintColor);		text = getText().toString();		if (1 == scrollDirection) {			text = reverseString(text);		}				utils = new FontUtils(context);		matrix = utils.getWordsInfo(text);		if (scroll) {			thread = new ScrollThread();			thread.start();		}	}	public LedView(Context context, AttributeSet attrs) {		this(context, attrs, 0);	}	public LedView(Context context) {		this(context, null, 0);	}	/**	 * 翻转字符串,当设置享有滚动时调用	 * @param str	 * @return	 */	private String reverseString(String str){		StringBuffer sb = new StringBuffer(str);		sb=sb.reverse();		return sb.toString();	}		/*	 * 用于控制滚动,仅在开启滚动时启动。	 */	private class ScrollThread extends Thread {		@Override		public void run() {			while (scrollText) {				try {					Thread.sleep(sleepTime);				} catch (InterruptedException e) {					e.printStackTrace();				}				if (0 == scrollDirection) {					matrixLeftMove(matrix);				} else {					matrixRightMove(matrix);				}				postInvalidate();			}		}	}		/**	 * 向左滚动时调用,列循环左移	 * @param matrix	 */	private void matrixLeftMove(boolean [][] matrix){		for (int i = 0; i < matrix.length; i++) {			boolean tmp = matrix[i][0];			System.arraycopy(matrix[i], 1, matrix[i], 0, matrix[0].length - 1);			matrix[i][matrix[0].length - 1] = tmp;		}	}		/**	 * 向右滚动时调用,列循环右移	 * @param matrix	 */	private void matrixRightMove(boolean [][] matrix){		for (int i = 0; i < matrix.length; i++) {			boolean tmp = matrix[i][matrix[0].length - 1];			System.arraycopy(matrix[i], 0, matrix[i], 1, matrix[0].length - 1);			matrix[i][0] = tmp;		}	}		/**	 * 主要是想处理AT_MOST的情况,我觉得View默认的情况就挺好的,由于继承自TextView,而TextView重	 * 写了onMeasure,因此这里参考View#onMeasure函数的写法即可	 */	@Override	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {		setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));	}		private void drawText(Canvas canvas) {		radius = (getHeight() - (dots + 1) * spacing) / (2 * dots);		// 行		int row = 0;		// 列		int column = 0;		while (getYPosition(row) < getHeight()) {			while (getXPosition(column) < getWidth()) {				// just draw				if (row < matrix.length && column < matrix[0].length && matrix[row][column]) {					canvas.drawCircle(getXPosition(column), getYPosition(row), radius, selectPaint);				} else {					canvas.drawCircle(getXPosition(column), getYPosition(row), radius, normalPaint);				}				column++;			}			row++;			column = 0;		}	}		/**	 * 获取绘制第column列的点的X坐标	 * @param column	 * @return	 */	private float getXPosition(int column) {		return spacing + radius + (spacing + 2 * radius) * column;	}	/**	 * 获取绘制第row行的点的Y坐标	 * @param row	 * @return	 */	private float getYPosition(int row) {		return spacing + radius + (spacing + 2 * radius) * row;	}		private void stopScroll() {		scrollText = false;	}	@Override	protected void onDetachedFromWindow() {		stopScroll();		super.onDetachedFromWindow();	}	@Override	protected void onDraw(Canvas canvas) {//		super.onDraw(canvas);		drawText(canvas);	}}

FontUtils.java

public class FontUtils {	private Context context;	/*	 * 字库名	 */	private static String dotMatrixFont = "HZK16";;	/*	 * 编码 GB2312	 */	private final static String ENCODE = "GB2312";		/*	 * 16X16的字库用16个点表示	 */	private int dots = 16;		/*	 * 一个字用点表示需要多少字节,16X16的字体需要32个字节	 */	private int wordByteByDots = 32;		public FontUtils(Context context) {		this.context = context;	}	/**	 * 获取字符串的点阵矩阵	 * @param str	 * @return	 */	public boolean[][] getWordsInfo(String str) {		byte[] dataBytes = null;		try {			dataBytes = str.getBytes(ENCODE);		} catch (UnsupportedEncodingException e) {			e.printStackTrace();		}		// 汉字对应的byte数组		int[] byteCode = new int[dataBytes.length];		// 当成无符号对待		for (int i = 0; i < dataBytes.length; i++) {			// 根据每两个byte数组计算一个汉字的相对位置			byteCode[i] = dataBytes[i] < 0 ? 256 + dataBytes[i] : dataBytes[i];		}		// 用来存放所有汉字对应的字模信息		// 一个汉字32 byte		int wordNums = byteCode.length/2;		boolean[][] matrix = new boolean[dots][wordNums * dots];		byte [] dataResult = new byte[wordNums * wordByteByDots];		for (int i = 0, numIndex = 0; i < byteCode.length; i += 2, numIndex++) {			// 依次读取到这个汉字对应的32位的字模信息			byte[] data = read(byteCode[i], byteCode[i+1]);			System.arraycopy(data, 0, dataResult, numIndex * data.length, data.length);		}		for (int num = 0; num < wordNums; num++) {			for (int i = 0; i < dots; i++) {				for (int j1 = 0; j1 < 2; j1++) {					// 对每个字节进行解析					byte tmp = dataResult[num * wordByteByDots + i * 2 + j1];					for (int j2 = 0; j2 < 8; j2++) {						if (((tmp >> (7 - j2)) & 1) == 1) { 							matrix[i][num*dots + j1 * 8 + j2] = true;						} else {							matrix[i][num*dots + j1 * 8 + j2] = false;						}					}				}			}		}		return matrix;	}	/**	 * 获取一个汉字的点阵数组,开始测试代码时用。。	 * @see getWordsInfo	 * @param str	 * @param font	 * @return	 */	@SuppressWarnings("unused")	private boolean[][] getWordInfo(String str, String font) {		boolean[][] matrix = new boolean[16][16];		int[] byteCode = new int[2];		try {			byte[] data = str.getBytes(ENCODE);			// 当成无符号对待			byteCode[0] = data[0] < 0 ? 256 + data[0] : data[0];			byteCode[1] = data[1] < 0 ? 256 + data[1] : data[1];		} catch (UnsupportedEncodingException e) {			e.printStackTrace();		}		// 读取到这个汉子对应的32位的字模信息		byte[] data = read(byteCode[0], byteCode[1]);		// 填充16x16的矩阵		for (int i = 0; i < dots; i++) {			for (int j1 = 0; j1 < 2; j1++) {				// 对每个字节进行解析				byte tmp = data[i * 2 + j1];				for (int j2 = 0; j2 < 8; j2++) {					if (((tmp >> (7 - j2)) & 1) == 1) {						matrix[i][j1 * 8 + j2] = true;					} else {						matrix[i][j1 * 8 + j2] = false;					}				}			}		}		return matrix;	}	/**	 * 从字库中找到这个汉字的字模信息	 * @param areaCode 区码,对应编码的第一个字节	 * @param posCode 位码,对应编码的第二个字节	 * @return	 */	protected byte[] read(int areaCode, int posCode) {		byte[] data = null;		try {			int area = areaCode - 0xa0;			int pos = posCode - 0xa0;			InputStream in = context.getResources().getAssets().open(dotMatrixFont);			long offset = wordByteByDots * ((area - 1) * 94 + pos - 1);			in.skip(offset);			data = new byte[wordByteByDots];			in.read(data, 0, wordByteByDots);			in.close();		} catch (Exception ex) {		}		return data;	}}

自定义参数文件attrs.xml

使用时需要将16*16的点阵字库放到assets文件夹中。

查看原文:

转载于:https://www.cnblogs.com/qhyuan1992/p/6071975.html

你可能感兴趣的文章
[小技巧] 快速给目录打包
查看>>
ACM-ICPC 2018 沈阳赛区网络预赛 J. Ka Chang
查看>>
D. Kay and Snowflake 树的重心
查看>>
http://blog.csdn.net/hgf1011/article/details/4342311
查看>>
ajax跨域问题
查看>>
Apache Beam中的函数式编程理念
查看>>
如何在cmd窗口里快速且正确打开任意位置路径(各版本windows系统都适合)(图文详解)(博主推荐)...
查看>>
Elasticsearch之中文分词器插件es-ik的自定义词库
查看>>
为什么选择Solr?
查看>>
HUE配置文件hue.ini 的desktop模块详解(图文详解)(分HA集群)
查看>>
jQuery页面替换+php代码实现搜索后分页
查看>>
IsolationLevel 枚举
查看>>
Java中的线程池
查看>>
面向对象设计
查看>>
PHP 运算符 详解
查看>>
Class to connect postgres with python in psycopg2
查看>>
安利demo
查看>>
c++基础(一)
查看>>
Codeforces Round #441 (Div. 2, by Moscow Team Olympiad)(A B C D)
查看>>
牛客小白月赛2
查看>>