【Android】A星算法演示代码(自定义view方便查看效果)

本博客所有文章皆为原创,欢迎转载,转载请注明出处,谢谢合作!
http://blog.csdn.net/shinnwxwx/article/details/50833150

以下代码需要理解A星算法,如果您还不了解什么是A星算法,可以前往此链接学习一下。
http://blog.csdn.net/shinnwxwx/article/details/50835517

介绍:本代码是A星算法的演示代码,使用自定义view实现效果,直接在android layout里引用即可查看效果,无需新建工程,一个类搞定a星。

你可以根据代码注释,灵活修改参数进行预览,【起】点和【终】点可以通过点击屏幕中的方块随意修改。

效果图:
这里写图片描述

不废话,直接上能用的源码,拉进工程里就能用啦!

public class ViewForCircle extends View {

    int rect[][] = new int[20][40];// 地图矩阵 x , y

    int nowx = 2;// 起点x
    int nowy = 2;// 起点y
    int endx = rect.length - 3;// 终点x
    int endy = rect[0].length - 3;// 终点y

    List<Point> list_open = new ArrayList<Point>();// 保存开放列表
    List<Point> list_close = new ArrayList<Point>();// 保存关闭列表
    List<Point> list_result = new ArrayList<Point>();// 保存最终路径列表

    float paddingframe;// 与屏幕边缘的距离
    float view_width;// 显示的宽度
    float view_height;// 显示的高度
    float every_width;// 每个方块的宽度
    float every_height;// 每个方块的高度

    Paint p;

    int press_flag = 0;

    FontMetrics fm;

    Context context;

    boolean isLoading = false;
    boolean isPlaying = false;

    int animNum = 0;// 动画步数
    int speed = 100;// 动画速度

    public class Point {

        public int x;// x坐标
        public int y;// y坐标
        public int g;// 移动权重
        public int h;// 目标权重
        public int f;// 总权重
        public Point fatherpoint;// 父节点

        public Point(int x, int y, int g, int h, int f, Point fatherPoint) {
            this.x = x;
            this.y = y;
            this.g = g;
            this.h = h;
            this.f = f;
            this.fatherpoint = fatherPoint;
        }
    }

    public ViewForCircle(Context context) {
        super(context);
        this.context = context;
    }

    public ViewForCircle(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
    }

    public ViewForCircle(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.context = context;
    }

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        init();
    }

    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {

            if (event.getX() - paddingframe < 0 || event.getY() - paddingframe < 0
                    || event.getX() - (paddingframe + view_width) > 0 || event.getY() - (paddingframe + view_height) > 0
                    || isPlaying || isLoading)
                return true;

            int x = (int) ((event.getX() - paddingframe) / view_width * rect.length);
            int y = (int) ((event.getY() - paddingframe) / view_height * rect[0].length);

            if (isCanMove(x, y) == false)
                return true;

            press_flag++;
            if (press_flag % 2 == 1) {
                nowx = x;
                nowy = y;
            } else if (press_flag % 2 == 0) {
                endx = x;
                endy = y;
            }
            updateRun();
        }
        return true;
    }

    public void init() {

        // 初始化属性
        p = new Paint();
        p.setTextAlign(Paint.Align.CENTER);
        p.setTextSize(40);
        p.setStyle(Paint.Style.FILL);
        p.setAntiAlias(true);

        fm = p.getFontMetrics();

        paddingframe = 50;
        view_width = getWidth() - paddingframe * 2;
        every_width = view_width / rect.length;
        view_height = getHeight() - paddingframe * 2;
        every_height = view_height / rect[0].length;

        // 创建障碍
        updateObject(rect[0].length * rect.length / 4);

        // 寻路
        updateRun();
    }

    public void updateObject(int randomnum) {
        Random rd = new Random();
        List<Integer> list_object = new ArrayList<Integer>();
        for (int i = 0; i < rect.length * rect[0].length; i++) {
            if (i == nowy * rect.length + nowx)
                continue;
            else if (i == endy * rect.length + endx)
                continue;
            list_object.add(i);
        }
        for (int i = 0; i < randomnum; i++) {
            int index = (rd.nextInt() >>> 1) % list_object.size();
            int result = list_object.get(index);
            rect[result % rect.length][result / rect.length] = 1;
            list_object.remove(index);
        }

    }

    public void updateRun() {

        new Thread() {

            public void run() {

                handler.sendEmptyMessage(0);

                list_result.clear();
                list_open.clear();
                list_close.clear();

                // 将起点加入到关闭列表中
                list_close.add(new Point(nowx, nowy, 0, Math.abs(nowx - endx) + Math.abs(nowy - endy),
                        Math.abs(nowx - endx) + Math.abs(nowy - endy), null));

                boolean isLooper = true;

                while (isinEnd() == false && isLooper) {

                    // 将符合条件的节点加入开放列表
                    for (int i = 0; i < list_close.size(); i++) {
                        addPoint(list_close.get(i));
                    }

                    // 更新关闭列表和开放列表中的节点
                    if (updatePoint() == false) {
                        list_result.clear();
                        list_open.clear();
                        list_close.clear();
                        isLooper = false;
                    }
                }

                // 将最终结果筛选,并保存到最终路径列表中
                refreshPoint();

                handler.sendEmptyMessage(1);
            }

        }.start();

    }

    Handler handler = new Handler() {

        public void handleMessage(Message msg) {
            if (msg.what == 0) {
                // 显示loading
                isLoading = true;
            } else if (msg.what == 1) {
                // 隐藏loading
                isLoading = false;
            } else if (msg.what == 2) {
                // 刷新步数
                if (animNum == list_result.size()) {
                    isPlaying = false;
                } else {
                    animNum++;
                }
            }
            invalidate();
        }

    };

    public boolean updatePoint() {
        Point minPoint = null;
        for (int i = 0; i < list_open.size(); i++) {
            if (minPoint == null)
                minPoint = list_open.get(i);
            else {
                if (minPoint.f >= list_open.get(i).f)
                    minPoint = list_open.get(i);
            }
        }
        if (minPoint != null) {
            list_close.add(new Point(minPoint.x, minPoint.y, minPoint.g, minPoint.h, minPoint.f, minPoint.fatherpoint));
            list_open.remove(minPoint);
            return true;
        } else {
            return false;
        }
    }

    public void refreshPoint() {
        // 获得最终路径列表
        if (list_close.size() != 0) {
            Point endPoint = list_close.get(list_close.size() - 1);
            while (endPoint != null) {
                list_result.add(0, endPoint);
                endPoint = endPoint.fatherpoint;
            }
            animNum = 0;
            isPlaying = true;
        }
    }

    // 检查当前节点周围节点
    public void addPoint(Point p) {
        int arrow_up = 0;
        int arrow_down = 0;
        int arrow_left = 0;
        int arrow_right = 0;

        // 判断是否越界
        if (isOut(p.x, p.y - 1) || isCanMove(p.x, p.y - 1) == false)
            arrow_up = 1;
        if (isOut(p.x, p.y + 1) || isCanMove(p.x, p.y + 1) == false)
            arrow_down = 1;
        if (isOut(p.x - 1, p.y) || isCanMove(p.x - 1, p.y) == false)
            arrow_left = 1;
        if (isOut(p.x + 1, p.y) || isCanMove(p.x + 1, p.y) == false)
            arrow_right = 1;

        // 如果arrow方向所对应的值仍然为0,那么判断节点是否在关闭列表中
        for (int i = 0; i < list_close.size(); i++) {
            int tempx = list_close.get(i).x;
            int tempy = list_close.get(i).y;
            if (arrow_up == 0 && tempx == p.x && tempy == p.y - 1) {
                arrow_up = 1;
            } else if (arrow_down == 0 && tempx == p.x && tempy == p.y + 1) {
                arrow_down = 1;
            } else if (arrow_left == 0 && tempx == p.x - 1 && tempy == p.y) {
                arrow_left = 1;
            } else if (arrow_right == 0 && tempx == p.x + 1 && tempy == p.y) {
                arrow_right = 1;
            }
        }

        // 如果arrow方向所对应的值仍然为0,那么判断节点是否在开放列表中
        for (int i = 0; i < list_open.size(); i++) {
            int tempx = list_open.get(i).x;
            int tempy = list_open.get(i).y;
            if (arrow_up == 0 && tempx == p.x && tempy == p.y - 1) {
                arrow_up = 1;
            } else if (arrow_down == 0 && tempx == p.x && tempy == p.y + 1) {
                arrow_down = 1;
            } else if (arrow_left == 0 && tempx == p.x - 1 && tempy == p.y) {
                arrow_left = 1;
            } else if (arrow_right == 0 && tempx == p.x + 1 && tempy == p.y) {
                arrow_right = 1;
            }
        }

        // 将符合条件的节点加入开放列表中
        if (arrow_up == 0) {
            list_open.add(new Point(p.x, p.y - 1, p.g + 1, Math.abs(p.x - endx) + Math.abs(p.y - 1 - endy),
                    p.g + 1 + Math.abs(p.x - endx) + Math.abs(p.y - 1 - endy), p));
        }
        if (arrow_down == 0) {
            list_open.add(new Point(p.x, p.y + 1, p.g + 1, Math.abs(p.x - endx) + Math.abs(p.y + 1 - endy),
                    p.g + 1 + Math.abs(p.x - endx) + Math.abs(p.y + 1 - endy), p));
        }
        if (arrow_left == 0) {
            list_open.add(new Point(p.x - 1, p.y, p.g + 1, Math.abs(p.x - 1 - endx) + Math.abs(p.y - endy),
                    p.g + 1 + Math.abs(p.x - 1 - endx) + Math.abs(p.y - endy), p));
        }
        if (arrow_right == 0) {
            list_open.add(new Point(p.x + 1, p.y, p.g + 1, Math.abs(p.x + 1 - endx) + Math.abs(p.y - endy),
                    p.g + 1 + Math.abs(p.x + 1 - endx) + Math.abs(p.y - endy), p));
        }
    }

    // x,y是否可以移动
    private boolean isCanMove(int x, int y) {
        if (rect[x][y] == 1)
            return false;
        return true;
    }

    // x,y是否出界
    private boolean isOut(int x, int y) {
        if (x < 0 || x >= rect.length || y < 0 || y >= rect[0].length)
            return true;
        else
            return false;
    }

    // 终点x,y是否在关闭列表中
    public boolean isinEnd() {
        for (int i = 0; i < list_close.size(); i++) {
            int tempx = list_close.get(i).x;
            int tempy = list_close.get(i).y;
            if (tempx == endx && tempy == endy) {
                return true;
            }
        }
        return false;
    }

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawRGB(255, 255, 255);

        // 显示障碍
        p.setColor(Color.parseColor("#cccccc"));
        for (int i = 0; i < rect.length; i++) {
            for (int j = 0; j < rect[i].length; j++) {
                if (rect[i][j] == 1)
                    canvas.drawRect(paddingframe + every_width * i, paddingframe + every_height * j,
                            paddingframe + every_width * (i + 1), paddingframe + every_height * (j + 1), p);
            }
        }

        // 显示路径
        p.setColor(Color.parseColor("#ff9912"));
        for (int i = 0; i < list_result.size(); i++) {
            if (i <= animNum) {
                canvas.drawRect(paddingframe + (every_width * list_result.get(i).x),
                        paddingframe + (every_height * list_result.get(i).y),
                        paddingframe + (every_width * (list_result.get(i).x + 1)),
                        paddingframe + (every_height * (list_result.get(i).y + 1)), p);
            }
        }
        if (animNum == list_result.size()) {
            isPlaying = false;
        } else {
            new Thread() {
                public void run() {
                    try {
                        Thread.sleep(speed);
                        handler.sendEmptyMessage(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }

        // 显示地图表格
        p.setColor(Color.parseColor("#666666"));
        for (int i = 0; i < rect[0].length + 1; i++) {
            canvas.drawLine(paddingframe, paddingframe + every_height * i, paddingframe + view_width,
                    paddingframe + every_height * i, p);
        }
        for (int i = 0; i < rect.length + 1; i++) {
            canvas.drawLine(paddingframe + every_width * i, paddingframe, paddingframe + every_width * i,
                    paddingframe + view_height, p);
        }

        // 显示起点,终点
        canvas.drawText("起", paddingframe + every_width * nowx + every_width / 2,
                paddingframe + every_height * nowy + every_height / 2 + (fm.descent - fm.ascent) / 2 - fm.descent, p);
        canvas.drawText("终", paddingframe + every_width * endx + every_width / 2,
                paddingframe + every_height * endy + every_height / 2 + (fm.descent - fm.ascent) / 2 - fm.descent, p);

        if (isLoading) {
            p.setColor(Color.parseColor("#ff0000"));
            canvas.drawText("寻路中...请稍后...", getWidth() / 2, getHeight() / 2 + (fm.descent - fm.ascent) / 2 - fm.descent,
                    p);
        }

    }

}

具体功能性函数已经写好注释,大家可以直接在自己的layout里引用这个自定义的View设置好宽高位置即可,附上例子layout代码

<这里写上你的包名.ViewForCircle
        android:id="@+id/circleview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

只要这些步骤,你就可以在自己的项目里预览A星算法的全部过程,代码已经经过优化修改,注释写的比较完整,如果你还有什么问题,可以再此留言,我们一起交流与学习!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值