最近发现了一个比较好玩的效果,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

<com.yqh.androidled.LedViewxmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:text="我爱你"app:scrollDirection="left"app:textColor="#f00"app:scroll="false"
/>

效果图

代码:

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 {@Overridepublic 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函数的写法即可*/@Overrideprotected 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 drawif (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;}@Overrideprotected void onDetachedFromWindow() {stopScroll();super.onDetachedFromWindow();}@Overrideprotected 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 byteint 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

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name = "LedTextView"><attr name="textColor" format="color" /><attr name="spacing" format="dimension" /><attr name="scroll" format="boolean" /><attr name="speed" ><enum name="normal" value="0"></enum><enum name="slow" value="1"></enum></attr><attr name="scrollDirection" ><enum name="left" value="0"></enum><enum name="right" value="1"></enum></attr></declare-styleable>
</resources>

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

查看原文:http://qhyuang1992.com/index.php/2016/06/25/android_dian_zhen_ping_xiao_guo_de_kong_jian/

Android点阵屏效果的控件相关推荐

  1. Android果冻效果滑动控件

    类似于iOS有阻尼效果的滑动控件,一般我们比较亲切地称之为果冻控件 比较常见的地方,如微信里[我]的那个面板模块,即使没有再多的选项,也不会很生硬的不允许用户滑动. 微信是的处理方法是让用户滑动,但最 ...

  2. Android中的基础控件TextView、Button、ImageView、EditText、ProgressBar

    文章目录 1 Android中的基础控件 1.1 控件的通用属性 2 TextView 2.1 TextView的继承关系 2.2 TextView的常用属性 3 EditText 3.1 常用属性 ...

  3. android中的标题栏是什么意思,Android通用标题栏组合控件

    原标题:Android通用标题栏组合控件 快,点击蓝色"字体"关注这个公众号,一起涨姿势 由于项目中经常用到此种组合控件,就封装了下,具体效果看下图,老司机可以绕道哈! 一.主要功 ...

  4. Android游戏开发系统控件-CheckBox

    Android游戏开发系统控件-CheckBox 2012/5/11 星期五 CheckBox是Android系统最普通的UI控件,继承了Button按钮 下面通过一个实例来学习 作者:wwj 功能: ...

  5. Android表格拖拽排序,Android 拖拽排序控件 DragGridView

    Android 拖拽排序控件 DragGridView Android 开发中,我们经常会遇到条目拖拽排序的需求,特别是在新闻类应用中就更普遍了.其实,我们在网上可以搜到许多关于拖拽排序的自定义控件, ...

  6. Android开源库集合(控件)

    RecycleView: RecycleView功能增强 https://github.com/Malinskiy/SuperRecyclerView RecycleView功能增强(拖拽,滑动删除, ...

  7. Android RadarScanView雷达扫描控件

    Android RadarScanView雷达扫描控件 简介 雷达扫描控件常用于检索场景,扫描文件.搜索目标等都可将雷达扫描识图展示给用户,增强用户体验. 样例 实现 在onDraw中使用Canvas ...

  8. android自定义波浪图,Android自定义控件--波浪图控件

    今天给大家分享一个android的波浪图控件制作.具体效果如下图所示: 上次有个app使用了这个控件,感觉特别酷炫.今天讲解一下这个控件的思路分析与代码编写. 思路分析: 1.绘制波浪图 2.移动波浪 ...

  9. Android Switch和ToggleButton控件

    1. Switch类 Switch类被用来展示状态. android:checked设置是否打开 自定义Switch, android:track设置背景 android:thumb设置选择背景 an ...

  10. 从零开始学android:Android中的基本控件(上)

    从零开始学android:Android中的基本控件(上) 本章内容较多,下面只贴代码,大家只需要贴到自己eclipse里就知道作用^^! View组件简介 Android中的View组件包含了几乎所 ...

最新文章

  1. tomcat更改端口
  2. C#中的Socket编程-TCP客户端
  3. 紫外线的形式是什么?
  4. 类进阶学习目标 java 1614957028
  5. java web 许令波_Java Web——Web概述
  6. vista 改xp BIOS设置(ASUS,dell,hp,Acer,Lenovo)
  7. 【技术帖】Apache Kylin 高级设置:层级维度(Hierarchy Dimension)原理
  8. Tomcat安装及配置教程
  9. python自然语言处理学习笔记二
  10. 使用Simu5G实现车联网V2X通信过程(两个简单的示例)
  11. 纪念特洛伊英雄 Sinon - SAP UI5 Mock Server 使用步骤和工作原理介绍
  12. 在线24点计算器工具
  13. linux修改主机名命令
  14. Maven手工安装jar包到本地仓库
  15. springboot配置类中的bean名字能不能随便写
  16. 为什么电脑唯独搜不到自己家wifi?
  17. 路由Zebra 之socket通信机制
  18. 软件产品测试的准入准出标准有哪些?
  19. VC Spyglass CDC(二)常见的CDC处理方法
  20. 《MySQL 入门教程》第 28 篇 字符集与排序规则

热门文章

  1. 大陆居民可以在香港汇丰银行开私人账户
  2. 办公室远程 办公室远程,我看这四款行
  3. simplest tensor core gemm sample
  4. 外卖cps返利定制开发源码平台小程序美团饿了么红包电影票券分销
  5. Flowable源码注释(三十二)任务超时作业
  6. 【Linux】yum(Yellow dog Updater Modified)使用简介
  7. java 生成缩略图_java实现图片生成缩略图
  8. 用什么软件编写html语言,可以用什么工具编写javascript?
  9. ios 获取是否静音模式_iOS 判断设备是否静音
  10. 半小时体验云原生:手把手教你在k8s上部署springboot应用——干货分享,建议收藏