热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android实现字母雨的效果

在学习Android的时候见到别人实现的黑客帝国的字母雨效果,感觉效果很炫,今天我们也来实现一下。

首先来看效果:

一、实现原理

在实现过程中,主要考虑整个界面由若干个字母组成的子母线条组成,这样的话把固定数量的字母封装成一个字母线条,而每个字母又封装成一个对象,这样的话,就形成了如下组成效果:

字母对象--》字母线条对象--》界面效果

每个字母都应该知道自己的位置坐标,自己上面的字母、以及自己的透明度:

class HackCode{
     Point p = new Point();//每一个字母的坐标
     int alpha = 255;//透明度值 默认255
     String code = "A";//字母的值
  }

而每个子母线条对象都有自己这条线条的初始底部起点,内部的多个字母都是根据线条的初始底部起点依次排列,包含多个字母对象集合,以及这条线条的唯一标示:

class HackLine{
  public int NUM = 0;//用于记录这列的标示
  private Point p = new Point();//线的初始位置
  List hcs = new ArrayList();//黑客字母的一条线
  }

在初始化的时候创建所有子母线条对象以及字母对象存入集合中:

@Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    mWidth = getMeasuredWidth();//获取控件宽高
    mHeight = getMeasuredHeight();
    mHackLines.clear();//清空集合
    initPlayData();//初始化播放数据
  }

  /**
   * 初始化播放数据
   */
  public void initPlayData(){
    initHackLine(mWidth/9, mHeight/12);
    initHackLine(mWidth/9, mHeight/7);
    HackLine hl;
    for (int i = 3; i <9; i++) {
      hl= new HackLine();
      hl.p.x = mWidth/9*(i+1);
      hl.p.y = mHeight/7*(9-i);
      for (int j = 0; j <7; j++) {
        HackCode hc = new HackCode();
        hc.alpha -= 30*j;
        hc.code = CODES[new Random().nextInt(CODES.length)];
        hc.p.x = hl.p.x;
        hc.p.y = hl.p.y-dip2px(getContext(), 25)*j;
        hl.hcs.add(hc);
      }
      mHackLines.add(hl);
      hl.NUM = mHackLines.size();
    }
  }

然后在onDraw方法中绘制:

@Override
protected void onDraw(Canvas canvas) {
  for (int i = 0; i 

接下来要滚动显示由Handler发送一个延时100毫秒的消息开始:

class WeakHandler extends Handler{
    WeakReference mActivity; 
    public WeakHandler(Activity activity){
      mActivity = new WeakReference(activity);
    }
    @Override
    public void handleMessage(Message msg) {
      if(mActivity.get() != null){
        switch (msg.what) {
        case WHAT:
          nextPlay(dip2px(getContext(), 20));
          for (int i = 0; i = mHeight/2*3){
              addHackLine(mHackLines.get(i));
            }
          }
          invalidate();
          break;
        }
      }
    }
  }

让整个线条往下走其实也就只用将线条的底部初始值Y坐标不断增加,内部字母随之更新位置就可以了:

/**
   * 下一帧播放
   * @param Nnum 每次下移多远 距离
   */
  public void nextPlay(int Nnum){
    for (int i = 0; i  hcs = mHackLines.get(i).hcs;
      hcs.clear();
      mHackLines.get(i).p.y+=Nnum;
      for (int j = 0; j <7; j++) {
        HackCode hc = new HackCode();
        hc.alpha -= 30*j;
        hc.code = CODES[new Random().nextInt(CODES.length)];
        hc.p.x = mHackLines.get(i).p.x;
        hc.p.y = mHackLines.get(i).p.y-dip2px(getContext(), 25)*j;
        hcs.add(hc);
      }
    }
  }

之后我们要考虑在合适的时间移除掉不需要的字母线条并增加新的子母线条,这里我是判断如果线条底部超过屏幕高度的一半时就移除当前线条并根据唯一标示添加新的线条:

  /**
   * 删除一列 同时添加初始化一列
   * @param hackLine 
   */
  public void addHackLine(HackLine hackLine){
      if(hackLine == null){
        return;
      }
      int num = hackLine.NUM;
      mHackLines.remove(hackLine);//如果存在 删除  重新添加

      HackLine hl;
      hl= new HackLine();
      hl.p.x = mWidth/9*(num-1);
      hl.p.y = mHeight/12*(7-(num-1));
      for (int j = 0; j <7; j++) {
        HackCode hc = new HackCode();
        hc.alpha -= 30*j;
        hc.code = CODES[new Random().nextInt(CODES.length)];
        hc.p.x = hl.p.x;
        hc.p.y = hl.p.y-dip2px(getContext(), 25)*j;
        hl.hcs.add(hc);
      }
      hl.NUM = num;
      mHackLines.add(hl);
  }

最后,在控件移除屏幕的时候终止消息循环,运行时记得将根布局设置背景为黑色:

@Override
  protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    mHandler.removeCallbacksAndMessages(null);//停止刷新
  }

OKOK,字母雨已经出来啦~~ 思路清晰之后还是很简单的哦~

二、实现代码

整个代码也不算很长,就直接贴上了:

/**
 * 字母雨
 * @author zhang
 *
 */
public class HackView extends View {
  /** 文字的画笔 */
  private Paint mPaint;
  /** 控件的宽 */
  private int mWidth;
  /** 控件的高 */
  private int mHeight;
  /** 所有字母 */
  private static final String[] CODES = {
    "A","B","C","D","E","F","G","H","I","J","K",
    "L","M","N","O","P","Q","R","S","T","U","V",
    "W","K","Y","Z"
  };

  private static final int WHAT = 1;
  /** 所有的HackLine组合 */
  private List mHackLines = new ArrayList();

  private WeakHandler mHandler;

  public HackView(Context context) {
    this(context,null);
  }
  public HackView(Context context, AttributeSet attrs) {
    this(context, attrs,0);
  }
  public HackView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
    mHandler = new WeakHandler((Activity) context);
  }

  class WeakHandler extends Handler{
    WeakReference mActivity; 
    public WeakHandler(Activity activity){
      mActivity = new WeakReference(activity);
    }
    @Override
    public void handleMessage(Message msg) {
      if(mActivity.get() != null){
        switch (msg.what) {
        case WHAT:
          nextPlay(dip2px(getContext(), 20));
          for (int i = 0; i = mHeight/2*3){
              addHackLine(mHackLines.get(i));
            }
          }
          invalidate();
          break;
        }
      }
    }
  }
  private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.WHITE);
    mPaint.setTextSize(dip2px(getContext(), 20));
    mPaint.setStrokeCap(Cap.ROUND);
    mPaint.setStrokeWidth(dip2px(getContext(), 5));
    setLayerType(View.LAYER_TYPE_SOFTWARE, null);
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    mWidth = getMeasuredWidth();//获取控件宽高
    mHeight = getMeasuredHeight();
    mHackLines.clear();//清空集合
    initPlayData();
  }
  /**
   * 下一帧播放
   * @param Nnum 每次下移多远 距离
   */
  public void nextPlay(int Nnum){
    for (int i = 0; i  hcs = mHackLines.get(i).hcs;
      hcs.clear();
      mHackLines.get(i).p.y+=Nnum;
      for (int j = 0; j <7; j++) {
        HackCode hc = new HackCode();
        hc.alpha -= 30*j;
        hc.code = CODES[new Random().nextInt(CODES.length)];
        hc.p.x = mHackLines.get(i).p.x;
        hc.p.y = mHackLines.get(i).p.y-dip2px(getContext(), 25)*j;
        hcs.add(hc);
      }
    }
  }
  /**
   * 删除一列 同时添加初始化一列
   * @param hackLine 
   */
  public void addHackLine(HackLine hackLine){
      if(hackLine == null){
        return;
      }
      int num = hackLine.NUM;
      mHackLines.remove(hackLine);//如果存在 删除  重新添加

      HackLine hl;
      hl= new HackLine();
      hl.p.x = mWidth/9*(num-1);
      hl.p.y = mHeight/12*(7-(num-1));
      for (int j = 0; j <7; j++) {
        HackCode hc = new HackCode();
        hc.alpha -= 30*j;
        hc.code = CODES[new Random().nextInt(CODES.length)];
        hc.p.x = hl.p.x;
        hc.p.y = hl.p.y-dip2px(getContext(), 25)*j;
        hl.hcs.add(hc);
      }
      hl.NUM = num;
      mHackLines.add(hl);
  }
  /**
   * 初始化每一行数据
   * @param x
   * @param y
   */
  public void initHackLine(int x,int y){
    HackLine hl;
    for (int i = 0; i <9; i++) {
      hl= new HackLine();
      hl.p.x = x*i;
      hl.p.y = y*(7-i);
      for (int j = 0; j <7; j++) {
        HackCode hc = new HackCode();
        hc.alpha -= 30*j;
        hc.code = CODES[new Random().nextInt(CODES.length)];
        hc.p.x = hl.p.x;
        hc.p.y = hl.p.y-dip2px(getContext(), 25)*j;
        hl.hcs.add(hc);
      }
      mHackLines.add(hl);
      hl.NUM = mHackLines.size();
    }
  }
  /**
   * 初始化播放数据
   */
  public void initPlayData(){
    initHackLine(mWidth/9, mHeight/12);
    initHackLine(mWidth/9, mHeight/7);
    HackLine hl;
    for (int i = 3; i <9; i++) {
      hl= new HackLine();
      hl.p.x = mWidth/9*(i+1);
      hl.p.y = mHeight/7*(9-i);
      for (int j = 0; j <7; j++) {
        HackCode hc = new HackCode();
        hc.alpha -= 30*j;
        hc.code = CODES[new Random().nextInt(CODES.length)];
        hc.p.x = hl.p.x;
        hc.p.y = hl.p.y-dip2px(getContext(), 25)*j;
        hl.hcs.add(hc);
      }
      mHackLines.add(hl);
      hl.NUM = mHackLines.size();
    }
  }
  @Override
  protected void onDraw(Canvas canvas) {
    for (int i = 0; i  hcs = new ArrayList();//黑客字母的一条线
  }
  /**
   * 每个字母
   */
  class HackCode{
     Point p = new Point();//每一个字母的坐标
     int alpha = 255;//透明度值 默认255
     String code = "A";//字母的值
  }
  @Override
  protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    mHandler.removeCallbacksAndMessages(null);//停止刷新
  }
   /** 
   * 根据手机的分辨率从 dip 的单位 转成为 px(像素) 
   */ 
  public static int dip2px(Context context, float dpValue) { 
    final float scale = context.getResources().getDisplayMetrics().density; 
    return (int) (dpValue * scale + 0.5f); 
  } 
}

xml:



  

以上就是基于Android实现字母雨的效果全部内容,效果很好,有需要的小伙伴们可以参考学习。


推荐阅读
  • Android 九宫格布局详解及实现:人人网应用示例
    本文深入探讨了人人网Android应用中独特的九宫格布局设计,解析其背后的GridView实现原理,并提供详细的代码示例。这种布局方式不仅美观大方,而且在现代Android应用中较为少见,值得开发者借鉴。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • Android LED 数字字体的应用与实现
    本文介绍了一种适用于 Android 应用的 LED 数字字体(digital font),并详细描述了其在 UI 设计中的应用场景及其实现方法。这种字体常用于视频、广告倒计时等场景,能够增强视觉效果。 ... [详细]
  • 本文总结了2018年的关键成就,包括职业变动、购车、考取驾照等重要事件,并分享了读书、工作、家庭和朋友方面的感悟。同时,展望2019年,制定了健康、软实力提升和技术学习的具体目标。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文将详细介绍如何使用剪映应用中的镜像功能,帮助用户轻松实现视频的镜像效果。通过简单的步骤,您可以快速掌握这一实用技巧。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • 在当前众多持久层框架中,MyBatis(前身为iBatis)凭借其轻量级、易用性和对SQL的直接支持,成为许多开发者的首选。本文将详细探讨MyBatis的核心概念、设计理念及其优势。 ... [详细]
author-avatar
c72586051
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有