• Android中贝塞尔曲线的绘制方法


    贝塞尔曲线,很多人可能不太了解,什么叫做贝塞尔曲线呢?这里先做一下简单介绍:贝塞尔曲线也可以叫做贝济埃曲线或者贝兹曲线,它由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋。一般的矢量图形软件常利用贝塞尔曲线来精确画出曲线。

           上面的介绍中,“线段像可伸缩的皮筋”这句话非常关键,但也特别好理解。至于贝塞尔曲线的详细内容大家可以查阅相关资料。

           Android提供的贝塞尔曲线绘制接口

           在Android开发中,要实现贝塞尔曲线其实还是很简单的,因为Android已经给我们提供了相关接口,但此接口方法被藏的有点深,藏于Path类中。此方法如下:

           android.graphics.Path.quadTo(float x1, float y1, float x2, float y2)

           Since: API Level 1

           参数说明:

           x1:操作点的x坐标

           y1:操作点的y坐标

           x2:结束点的x坐标

           y2:结束点的y坐标

           从API中看出,贝塞尔曲线从API-1就开始支持了。

           Android贝塞尔曲线的绘制实例

           熟悉方法后,下面就来实现:

           SurfaceView框架不多讲,看过我博客的都应该知道的。

           直接看MySurfaceView类,此类继承SurfaceView,是游戏的主视图。

           这里为了更清晰的讲解:这里部分代码先不贴出来了,最后会整体贴出。

           首先是定义相关的成员变量:

    Java代码
    1. // 贝赛尔曲线成员变量(起始点,控制(操作点),终止点,3点坐标)   
    2. private int startX, startY, controlX, controlY, endX, endY;   
    3. // Path   
    4. private Path path;   
    5. // 为了不影响主画笔,这里绘制贝赛尔曲线单独用一个新画笔   
    6. private Paint paintQ;   
    7. // 随机库(让贝赛尔曲线更明显)   
    8. private Random random;  

           本类构造函数:

    Java代码
    1. /**  
    2.  * SurfaceView初始化函数  
    3.  */  
    4. public MySurfaceView(Context context) {   
    5.     super(context);   
    6.     ...   
    7.         //贝赛尔曲线相关初始化   
    8.         path = new Path();   
    9.         paintQ = new Paint();   
    10.         paintQ.setAntiAlias(true);   
    11.         paintQ.setStyle(Style.STROKE);   
    12.         paintQ.setStrokeWidth(5);   
    13.         paintQ.setColor(Color.WHITE);   
    14.         random = new Random();   
    15.     ...   
    16. }  

           接着我把贝赛尔曲线的绘制封装成一个方法了,函数如下:

    Java代码
    1. /**  
    2.  * 绘制贝赛尔曲线  
    3.  *  
    4.  * @param canvas 主画布  
    5.  */  
    6. public void drawQpath(Canvas canvas) {   
    7.     path.reset();// 重置path   
    8.     // 贝赛尔曲线的起始点   
    9.     path.moveTo(startX, startY);   
    10.     // 设置贝赛尔曲线的操作点以及终止点   
    11.     path.quadTo(controlX, controlY, endX, endY);   
    12.     // 绘制贝赛尔曲线(Path)   
    13.     canvas.drawPath(path, paintQ);   
    14. }  

           最后是用户触屏监听函数以及逻辑函数:

    Java代码
    1. /**  
    2.  * 触屏事件监听  
    3.  */  
    4. @Override  
    5. public boolean onTouchEvent(MotionEvent event) {   
    6.     endX = (int) event.getX();   
    7.     endY = (int) event.getY();   
    8.     return true;   
    9. }   
    10. /**  
    11.  * 游戏逻辑  
    12.  */  
    13. private void logic() {   
    14.     if (endX != 0 && endY != 0) {   
    15.         // 设置操作点为线段x/y的一半   
    16.         controlX = random.nextInt((endX - startX) / 2);   
    17.         controlY = random.nextInt((endY - startY) / 2);   
    18.     }   
    19. }  

           整个代码很easy,主要是贝赛尔函数的参数,尤其是操作点,操作点的各种不同可以实现不同的效果,这里我简单的统一的讲操作点设置成用户触屏点的x、y的一半,呵呵偷懒了~~

           我把贝赛尔的操作点写在了逻辑logic()函数中,不断的执行,并且每次利用nextInt函数得到随机的操作点,主要为了让其曲线不断的变化从而形成一个震动的曲线运动轨迹。

           运行效果截图如下:

    Android游戏开发25:Android中贝塞尔曲线的绘制方法

           这里可能由于图片是静止的,所以效果看起来不是很明显,大家可以运行源码来观察。

           下面贴出整个MySurfaceView的源码:

    Java代码
    1. package com.qpath;   
    2. import java.util.Random;   
    3. import android.content.Context;   
    4. import android.graphics.Canvas;   
    5. import android.graphics.Color;   
    6. import android.graphics.Paint;   
    7. import android.graphics.Paint.Style;   
    8. import android.graphics.Path;   
    9. import android.view.KeyEvent;   
    10. import android.view.MotionEvent;   
    11. import android.view.SurfaceHolder;   
    12. import android.view.SurfaceHolder.Callback;   
    13. import android.view.SurfaceView;   
    14. /**  
    15.  * 赛贝尔曲线  
    16.  * @author Himi  
    17.  *  
    18.  */  
    19. public class MySurfaceView extends SurfaceView implements Callback, Runnable {   
    20.     private SurfaceHolder sfh;   
    21.     private Paint paint;   
    22.     private Thread th;   
    23.     private boolean flag;   
    24.     private Canvas canvas;   
    25.     public static int screenW, screenH;   
    26.     // -----------以上是SurfaceView游戏框架   
    27.     // 贝赛尔曲线成员变量(起始点,控制(操作点),终止点,3点坐标)   
    28.     private int startX, startY, controlX, controlY, endX, endY;   
    29.     // Path   
    30.     private Path path;   
    31.     // 为了不影响主画笔,这里绘制贝赛尔曲线单独用一个新画笔   
    32.     private Paint paintQ;   
    33.     // 随机库(让贝赛尔曲线更明显)   
    34.     private Random random;   
    35.     /**  
    36.      * SurfaceView初始化函数  
    37.      */  
    38.     public MySurfaceView(Context context) {   
    39.         super(context);   
    40.         sfh = this.getHolder();   
    41.         sfh.addCallback(this);   
    42.         paint = new Paint();   
    43.         paint.setColor(Color.WHITE);   
    44.         paint.setAntiAlias(true);   
    45.         setFocusable(true);   
    46.         // -----------以上是SurfaceView游戏框架   
    47.         //贝赛尔曲线相关初始化   
    48.         path = new Path();   
    49.         paintQ = new Paint();   
    50.         paintQ.setAntiAlias(true);   
    51.         paintQ.setStyle(Style.STROKE);   
    52.         paintQ.setStrokeWidth(5);   
    53.         paintQ.setColor(Color.WHITE);   
    54.         random = new Random();   
    55.     }   
    56.     /**  
    57.      * SurfaceView视图创建,响应此函数  
    58.      */  
    59.     public void surfaceCreated(SurfaceHolder holder) {   
    60.         screenW = this.getWidth();   
    61.         screenH = this.getHeight();   
    62.         flag = true;   
    63.         // 实例线程   
    64.         th = new Thread(this);   
    65.         // 启动线程   
    66.         th.start();   
    67.         // -----------以上是SurfaceView游戏框架   
    68.     }   
    69.     /**  
    70.      * 游戏绘图  
    71.      */  
    72.     public void myDraw() {   
    73.         try {   
    74.             canvas = sfh.lockCanvas();   
    75.             if (canvas != null) {   
    76.                 canvas.drawColor(Color.BLACK);   
    77.                 // -----------以上是SurfaceView游戏框架   
    78.                 drawQpath(canvas);   
    79.             }   
    80.         } catch (Exception e) {   
    81.             // TODO: handle exception   
    82.         } finally {   
    83.             if (canvas != null)   
    84.                 sfh.unlockCanvasAndPost(canvas);   
    85.         }   
    86.     }   
    87.     /**  
    88.      * 绘制贝赛尔曲线  
    89.      *  
    90.      * @param canvas 主画布  
    91.      */  
    92.     public void drawQpath(Canvas canvas) {   
    93.         path.reset();// 重置path   
    94.         // 贝赛尔曲线的起始点   
    95.         path.moveTo(startX, startY);   
    96.         // 设置贝赛尔曲线的操作点以及终止点   
    97.         path.quadTo(controlX, controlY, endX, endY);   
    98.         // 绘制贝赛尔曲线(Path)   
    99.         canvas.drawPath(path, paintQ);   
    100.     }   
    101.     /**  
    102.      * 触屏事件监听  
    103.      */  
    104.     @Override  
    105.     public boolean onTouchEvent(MotionEvent event) {   
    106.         endX = (int) event.getX();   
    107.         endY = (int) event.getY();   
    108.         return true;   
    109.     }   
    110.     /**  
    111.      * 游戏逻辑  
    112.      */  
    113.     private void logic() {   
    114.         if (endX != 0 && endY != 0) {   
    115.             // 设置操作点为线段x/y的一半   
    116.             controlX = random.nextInt((endX - startX) / 2);   
    117.             controlY = random.nextInt((endY - startY) / 2);   
    118.         }   
    119.     }   
    120.     /**  
    121.      * 按键事件监听  
    122.      */  
    123.     @Override  
    124.     public boolean onKeyDown(int keyCode, KeyEvent event) {   
    125.         return super.onKeyDown(keyCode, event);   
    126.     }   
    127.     public void run() {   
    128.         while (flag) {   
    129.             long start = System.currentTimeMillis();   
    130.             myDraw();   
    131.             logic();   
    132.             long end = System.currentTimeMillis();   
    133.             try {   
    134.                 if (end - start < 50) {   
    135.                     Thread.sleep(50 - (end - start));   
    136.                 }   
    137.             } catch (InterruptedException e) {   
    138.                 e.printStackTrace();   
    139.             }   
    140.         }   
    141.     }   
    142.     /**  
    143.      * SurfaceView视图状态发生改变,响应此函数  
    144.      */  
    145.     public void surfaceChanged(SurfaceHolder holder, int format, int width,   
    146.             int height) {   
    147.     }   
    148.     /**  
    149.      * SurfaceView视图消亡时,响应此函数  
    150.      */  
    151.     public void surfaceDestroyed(SurfaceHolder holder) {   
    152.         flag = false;   
    153.     }   
    154. }  
  • 相关阅读:
    如何在Infraworks中创建多树种组成的森林
    Autodesk 2013开发者日(DevDays)又要来了 -- 北京(2013年11月7日)和上海(2013年11月11日)
    Mac下的Parallel Windows忘记密码怎么办?
    几个有用的JSON工具
    使用Autodesk OAuth服务在用户认证的示例
    ElasticSearch(九)e代驾使用Elasticsearch流程设计(Yii1版本)
    ElasticSearch(八)Elasticsearch-head 连接不上Elasticsearch的原因和解决方案
    ElasticSearch(七) Elasticsearch在Centos下搭建可视化服务
    Yii1自定义 CGridView 中的操作按钮中 CButtonColumn 选项
    Mysql BLOB、BLOB与TEXT区别及性能影响、将BLOB类型转换成VARCHAR类型
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4749782.html
Copyright © 2020-2023  润新知