神奇的流星雨——字母雨的实现

这里写图片描述

当然这么神奇的效果我们做为小辈是没有办法写出来的,那么我们就看看大牛是如何实现的

实现原理
整个界面由若干个字母组成的子母线条组成,这样的话把固定数量的字母封装成一个字母线条,而每个字母又封装成一个对象(字母——字母线——试图界面)

 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>();//黑客字母的一条线
    }

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

 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坐标不断增加,内部字母随之更新位置就可以了:

  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);//停止刷新
    }

下边看具体代码实现
view

package com.example.administrator.statusbarproject;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * Created by Administrator on 2016/8/26.
 */
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<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(Paint.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

<?xml version="1.0" encoding="utf-8"?>
<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"
    >

    <com.example.administrator.statusbarproject.HackView
       android:id="@+id/hackView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</RelativeLayout>

activity


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        HackView hackView= (HackView) findViewById(R.id.hackView);
    }

}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值