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<HackCode> hcs = new ArrayList<HackView.HackCode>();//黑客字母的一条线
  }

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

@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 < mHackLines.size(); i++) {
    drawText(i, canvas);
  }
  mHandler.sendEmptyMessageDelayed(WHAT, 100);//用于开启循环 线条滚动
  }

public void drawText(int nindex,Canvas canvas){
    HackLine hackLine = mHackLines.get(nindex);
    for (int i = 0; i < hackLine.hcs.size(); i++) {
      HackCode hackCode = hackLine.hcs.get(i);
      mPaint.setAlpha(hackCode.alpha);
      canvas.drawText(hackCode.code, hackCode.p.x, hackCode.p.y, mPaint);
    }
  }

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

class WeakHandler extends Handler{
    WeakReference<Activity> mActivity;
    public WeakHandler(Activity activity){
      mActivity = new WeakReference<Activity>(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 < mHackLines.size(); i++) {
            if(mHackLines.get(i).p.y >= mHeight/2*3){
              addHackLine(mHackLines.get(i));
            }
          }
          invalidate();
          break;
        }
      }
    }
  }

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

/**
   * 下一帧播放
   * @param Nnum 每次下移多远 距离
   */
  public void nextPlay(int Nnum){
    for (int i = 0; i < mHackLines.size(); i++) {
      List<HackCode> 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<HackLine> mHackLines = new ArrayList<HackView.HackLine>();

  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<Activity> mActivity;
    public WeakHandler(Activity activity){
      mActivity = new WeakReference<Activity>(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 < mHackLines.size(); i++) {
            if(mHackLines.get(i).p.y >= 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 < mHackLines.size(); i++) {
      List<HackCode> 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 < mHackLines.size(); i++) {
      drawText(i, canvas);
    }
    mHandler.sendEmptyMessageDelayed(WHAT, 100);
  }

  public void drawText(int nindex,Canvas canvas){
    HackLine hackLine = mHackLines.get(nindex);
    for (int i = 0; i < hackLine.hcs.size(); i++) {
      HackCode hackCode = hackLine.hcs.get(i);
      mPaint.setAlpha(hackCode.alpha);
      canvas.drawText(hackCode.code, hackCode.p.x, hackCode.p.y, mPaint);
    }
  }
  /**
   * 每条线 包含多个字母
   **/
  class HackLine{
    public int NUM = 0;//用于记录这列的标示
    private Point p = new Point();//线的初始位置
    List<HackCode> hcs = new ArrayList<HackView.HackCode>();//黑客字母的一条线
  }
  /**
   * 每个字母
   */
  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:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#000"
  tools:context=".MainActivity" >

  <com.zk.hack.HackView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />

</RelativeLayout>

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

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索android
, 文字雨效果
, 下雨效果
下雨动画效果
android弹幕效果实现、android 阴影效果实现、android 实现画廊效果、android实现抽屉效果、android磨砂效果实现,以便于您获取更多的相关知识。

时间: 2024-10-28 05:34:08

Android实现字母雨的效果_Android的相关文章

Android实现自定义的弹幕效果_Android

一.效果图 先来看看效果图吧~~ 二.实现原理方案 1.自定义ViewGroup-XCDanmuView,继承RelativeLayout来实现,当然也可以继承其他三大布局类哈 2.初始化若干个TextView(弹幕的item View,这里以TextView 为例,当然也可以其他了~),然后通过addView添加到自定义View中 3.通过addView添加到XCDanmuView中,位置在坐标,为了实现 从屏幕外移动进来的效果 我们还需要修改添加进来TextView的位置,以从右向左移动方向

Android自定义View实现打字机效果_Android

一.先来看看效果演示 二.实现原理: 这个其实不难实现,通过一个定时器不断调用TextView的setText就行了,在setText的时候播放打字的音效. 具体代码如下: import java.util.Timer; import java.util.TimerTask; import android.content.Context; import android.media.MediaPlayer; import android.text.TextUtils; import android

Android实现Flip翻转动画效果_Android

本文实例讲述了Android实现Flip翻转动画效果的方法,分享给大家供大家学习借鉴. 具体实现代码如下: LinearLayout locationLL = (LinearLayout) findViewById(R.id.locationLL); LinearLayout baseLL = (LinearLayout) findViewById(R.id.baseLL); private void flipit() { Interpolator accelerator = new Accel

简单实用的Android UI微博动态点赞效果_Android

说起空间动态.微博的点赞效果,网上也是很泛滥,各种实现与效果一大堆.而详细实现的部分,讲述的也是参差不齐,另一方面估计也有很多大侠也不屑一顾,觉得完全没必要单独开篇来写和讲解吧.毕竟,也就是两个view和一些简单的动画效果罢了. 单若是只讲这些,我自然也是不愿花这番功夫的.虽然自己很菜,可也不甘于太菜.所以偶尔看到些好东西,可以延伸学写下,我还是很情愿拿出来用用,顺带秀一秀逼格什么的. 不扯太多,先说说今天实现点赞效果用到的自以为不错的两个点: Checkable 用来扩展View实现选中状态切

Android项目实现黑名单拦截效果_Android

本文实例讲述了Android编程中黑名单的实现方法.分享给大家供大家参考,具体如下: 1,黑名单数据库创建 三个字段(_id 自增长字段 phone 黑名单号码 mode 拦截类型) 创建表的sql语句 create table blacknumber (_id integer primary key autoincrement , phone varchar(20), mode varchar(5)); 结合项目,去创建数据库,以及相应的表 2.BlackNumberDao BlackNumb

Android仿人人网滑动侧边栏效果_Android

很多应用为了节省空间而又使界面能够充足的显示信息,大多数应用都采用了侧边栏的方式,如下图:        来说说它的思路,底下是两个或多个视图,分别通过控制它们的宽度.左边距来控制它们的显示,来看看代码  activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/too

Android实现创意LoadingView动画效果_Android

Android上的热火锅煮萝卜蔬菜的Loading动画效果. 这是一个锅煮萝卜的Loading动画,效果仿照自之前IOS上看到的一个效果,觉得挺有意思,就移植过来了,在此完成了Dialog的样式,方便使用者作为LoadingView去使用. 关键性代码: package yellow5a5.demo.boilingloadingview.View; import android.animation.Animator; import android.animation.AnimatorListen

Android新闻广告条滚动效果_Android

项目中需要用到类似公告栏的控件,能用的基本不支持多行显示,于是只好自己动手,苦于没有自定义过一个像样的控件,借鉴Android公告条demo,实现了多行向上滚动的控件.在原控件基础之上添加如下功能:  •传入数据分页显示  •添加Left Drawable  •手指触摸事件处理  •添加3D动画翻滚效果 效果图 源码 package com.android.view; import android.content.Context; import android.content.res.Typed

Android打造流畅九宫格抽奖活动效果_Android

因为company项目中需要做九宫格抽奖活动,以前都没有做过类似的功能,虽然之前在浏览大神们的博客中,无意中也看到了好多关于抽奖的项目,但因为项目中没有需要,一直都没有点击进去看.这次不去看估计不行.直到公司计划要做抽奖功能,才迫不得已上网查找demo 网上找了大半天,好不容易找到了几个demo,下载下来,解压缩包发现竟然里面空空如也,只有几张九宫格的图片,害我白白浪费了几个CSDN积分.后面在eoe网站那发现了一个demo,于是好开心,下载下来后马上导入到工程中,运行看了效果,九宫格是出来了,