SerfaceView 自定义View 运动小球 区块链 星空图

本文介绍如何使用自定义View和SurfaceView在Android中实现引力效果。通过创建多个随机移动的小球,当它们接近到一定距离时,用线连接,且距离越近线条越粗。文章详细展示了Po类和GravitationView的实现代码,以及如何在布局中运行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先上图,本文用自定义View简单实现一个这样的效果,动态图看着有点卡卡的,帧率问题,运行起来是很流畅的 ----- 文末添加用SerfaceView的实现方式
在这里插入图片描述
思路
N个小球随机方向,随机速度,在视图中移动;任意两个小球靠近到一定距离就连线,距离越近,线越粗
分以下步骤实现
1.创建小球类,这里我用点来表示 取类名Po
2.创建一个定时器,16ms 刷新一次小球的位置,并重绘视图

这里定义的Po类有X,Y坐标,X,Y方向移动速度,以及绘制的半径,(半径是预留出来改变球的大小的,这个字段暂时没使用,球大小固定,就写了个固定值)

package com.zcy.mycanvas.view;

public class Po implements Runnable {

    private boolean isRemove;

    float x;//x坐标(有效范围0-1,0-1代表在承载视图左到右 , 用这个值乘以视图宽度就得到X方向坐标 , 下面同理)
    float y;//Y坐标
    float r;//半径
    float vx;//X方向速度
    float vy;//Y方向速度

    public Po(float x, float y, float r, float vx, float vy) {
        this.x = x;
        this.y = y;
        this.r = r;
        this.vx = vx;
        this.vy = vy;
    }

    /**
     * 用来刷新位置
     */
    @Override
    public void run() {
        if ((x < - 0.01f && vx < 0f) || ( x > 1.01f && vx > 0f)) vx = - vx;//超出显示边缘百分之1回弹
        if ((y < - 0.01f && vy < 0f) || ( y > 1.01f && vy > 0f)) vy = - vy;//超出显示边缘百分之1回弹
        x += vx;
        y += vy;
    }

    /**
     * 计算本点和目标点的距离,
     * @param o
     * @return
     */
    public float lenght(Po o){
        float lx = x - o.x;
        float ly = y - o.y;
        return (float) Math.sqrt(lx*lx + ly*ly);
    }

    public boolean isRemove() {
        return isRemove;
    }

    public void setRemove(boolean remove) {
        isRemove = remove;
    }
}

下面自定义一个GravitationView 继承View ,贴上代码,看init 和onDraw,的实现就行了,然后再布局中运行出来就是上图的效果

package com.zcy.mycanvas.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class GravitationView extends View implements Runnable{

    private List<Po> pos;//点集
    private Paint p;//画笔
    private Random random;

    protected void init(Context context, AttributeSet attrs) {
        p = new Paint(Paint.ANTI_ALIAS_FLAG);
        p.setStrokeWidth(10);
        p.setStrokeCap(Paint.Cap.ROUND);
        p.setColor(Color.WHITE);
        random = new Random();
        pos = new ArrayList<>();

        //模拟随机生成50个点
        for (int i = 0 ; i < 50 ; i++ ){
            Po po = new Po(nextFloat(),nextFloat(),nextFloat(),random.nextBoolean() ? nextFloat()/1000f : -nextFloat()/1000f,random.nextBoolean() ? nextFloat()/1000f : -nextFloat()/1000f );
            pos.add(po);
        }
        TimerUtils.addR(this);//这个TimerUtils类会每隔16毫秒调用一次run方法
    }

    private float nextFloat(){
        return random.nextFloat();
    }

    public GravitationView(Context context) {
        super(context);
        init(context,null);
    }

    public GravitationView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context,attrs);
    }

    public GravitationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context,attrs);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float width = getWidth();
        float height = getHeight();
        //外层循环绘制每一个点
        for (int i = 0 ; i < pos.size() ; i++){
            Po po = pos.get(i);
            p.setStrokeWidth(10);//设置点的大小
            canvas.drawPoint(po.x * width , po.y* height ,p);//绘制点
            //内层循环会将该点与后面的点逐一比较,计算距离
            for (int j = i+ 1; j < pos.size() ; j++){
                Po poj = pos.get(j);
                float lenght = po.lenght(poj);
                //距离在0f 和 0.2f之间的点就会被连线(这个值可根据需求随意调整)
                if (lenght > 0f && lenght < 0.2f){
                    p.setStrokeWidth(5*(0.2f - lenght));//距离越近,线越粗
                    canvas.drawLine(po.x*width,po.y*height,poj.x*width,poj.y*height,p);//绘制线
                }
            }
        }
    }

    @Override
    public void run() {
        for (Po o : pos)o.run();
        postInvalidate();
    }
}

*****************************surfaceView实现 *********************************
代码可直接运行,注释写的很清楚了

package com.zcy.mycanvas.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/*
* zcy   1084204954@qq.com
* */
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable{

    private List<Po> pos;//点集
    private Paint p;//画笔
    private Random random;

    private SurfaceHolder mHolder;
    private Canvas mCanvas;//绘图的画布
    private boolean mIsDrawing;//控制绘画线程的标志位

    public MySurfaceView(Context context) {
        super(context);
        init();
    }

    public MySurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init(){
        mHolder = getHolder();
        mHolder.addCallback(this);
        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);

        p = new Paint(Paint.ANTI_ALIAS_FLAG);
        p.setStrokeWidth(10);
        p.setStrokeCap(Paint.Cap.ROUND);
        p.setColor(Color.WHITE);
        random = new Random();
        pos = new ArrayList<>();
        //模拟随机生成50个点
        for (int i = 0 ; i < 50 ; i++ ){
            Po po = new Po(nextFloat(),nextFloat(),nextFloat(),random.nextBoolean() ? nextFloat()/1000f : -nextFloat()/1000f,random.nextBoolean() ? nextFloat()/1000f : -nextFloat()/1000f );
            pos.add(po);
        }
    }

    private float nextFloat(){
        return random.nextFloat();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mIsDrawing = true;
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mIsDrawing = false;
    }

    @Override
    public void run() {
        while (mIsDrawing){
            long startTime = System.currentTimeMillis();
            for (Po o: pos){
                o.run();
            }
            draw();
            while (System.currentTimeMillis() - startTime < 30L){
                Thread.yield();//耗时少于刷新间隔,线程等待
            }
        }
    }

    private void draw(){
        try {
            mCanvas = mHolder.lockCanvas();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (null != mCanvas){

                mCanvas.drawColor(Color.BLACK);
                float width = getWidth();
                float height = getHeight();
                //外层循环绘制每一个点
                for (int i = 0 ; i < pos.size() ; i++){
                    Po po = pos.get(i);
                    p.setStrokeWidth(10);//设置点的大小
                    mCanvas.drawPoint(po.x * width , po.y* height ,p);//绘制点
                    //内层循环会将该点与后面的点逐一比较,计算距离
                    for (int j = i+ 1; j < pos.size() ; j++){
                        Po poj = pos.get(j);
                        float lenght = po.lenght(poj);
                        //距离在0f 和 0.2f之间的点就会被连线(这个值可根据需求随意调整)
                        if (lenght > 0f && lenght < 0.2f){
                            p.setStrokeWidth(5*(0.2f - lenght));//距离越近,线越粗
                            mCanvas.drawLine(po.x*width,po.y*height,poj.x*width,poj.y*height,p);//绘制线
                        }
                    }
                }

                mHolder.unlockCanvasAndPost(mCanvas);
            }
        }

    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值