Android opengles 实现触碰屏幕,根据运动轨迹画直线的功能

本文介绍了在Android中使用OpenGL ES实现触碰屏幕画直线的步骤,包括固定坐标直线绘制、屏幕坐标转换、多条直线绘制。通过监听触摸事件动态更新直线坐标,利用坐标转换函数处理屏幕坐标到OpenGL ES坐标的问题。最后讨论了绘制多条直线的实现方式,需要注意清除颜色缓冲可能导致先前线条消失的问题。

目录

补一张效果图:
在这里插入图片描述
在手机里显示线很清楚的,可能是屏幕录制帧数太低了,显示出来都看不清,实际是没问题的,有机会用另一个手机拍屏幕传一个吧,2333.
在这里插入图片描述

宽度加粗了4倍,这下应该能看清楚了hhhh。

引言

由于项目功能需要,要做一个能自定义画直线的功能,网上找了许多文章或项目demo,没找到画直线的,反而是更进一步的实现类似“绘画板”功能的代码和教程较多;但是,这样的功能下,假如使用者想画一个规则的图形——比如直线,那么必须手不能抖笔直的画出来才行,应用到我这个项目里的话实在太反人类了,很不合适,于是就只好自己做了。

由于本人实力有限,也是刚接触opengles这玩意,磨磨蹭蹭拼拼凑凑了好几天总算是勉强实现了画直线的功能。

其实,做着做着就感觉这东西和android开发基本没啥关系了,无非就是android给他做了界面与交互而已,不过后续也要学习Opengl的也算是开个头吧,唠叨有点多了,下面是正文。

按步骤来说,想要实现根据手指运动轨迹画直线主要有以下几个要点:

  1. 实现一条固定坐标的直线的绘制----->>>>手动绘制一条直线
  2. 实现 屏幕坐标向opengles坐标的转换
  3. 任意绘制多条直线

第一步,先自己学会绘制一条固定坐标的直线

如何绘制一条直线参考官方文档里的三角形案例可以轻松实现,有现成案例,很多博主也是根据这些内容写的教程:https://developer.android.com/training/graphics/opengl/environment
本文是也是基于官方文档的三角形绘制案例改写的,具体如何绘制固定直线。
由于我没有中途保存过绘制直线的代码,下面提供的是已经实现自定义画线功能后的代码了,建议自己实现绘制一条固定坐标直线后再看后面的内容。

第二步,动态的绘制一条直线

仅仅实现这个功能其实也很简单,在renderer或者glsurfaceView里设置一个触摸事件,动态的更新直线类中的起点与终点的坐标即可。相关事件参考代码如下:(重点是事件内容)
Activity中设置时间的oncreate函数如下:

@Override
    protected void onCreate(Bundle savedInstanceState) {
   
   
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        glSurfaceView = new OneGlSurfaceView(this);
        mRenderer = new OneGlRenderer();
        glSurfaceView.setRenderer(mRenderer);
        setContentView(glSurfaceView);
        glSurfaceView.setOnTouchListener(this);//给view监听触摸事件
    }

之后是重写的Touch事件

@Override
    public boolean onTouch(View v, MotionEvent event) {
   
   


        switch (event.getAction()) {
   
   
            /**
             * 点击的开始位置
             */
            case MotionEvent.ACTION_DOWN:
                float[] out = mRenderer.handleTouch(event.getX(),event.getY(),mRenderer.straightLine);
                currentLines = new StraightLine(new float[] {
   
   0, 0, 0});
                currentLines.setX_start(out[0]);//设置起点x坐标
                currentLines.setY_start(out[1]);//设置直线起点y坐标 取按下的坐标为起点
                                           
                break;
            /**
             * 触屏实时位置
             */
            case MotionEvent.ACTION_MOVE:
                //out数组保存坐标转换后的xyz坐标
                out = mRenderer.handleTouch(event.getX(),event.getY(),mRenderer.straightLine);
                Log.i("setPointer", String.format("x: %f, y: %f", event.getX(), event.getY()));
                Log.i("setPointer", String.format("x-start: %f, y-start: %f, z-start:%f", mRenderer.straightLine.getX_start(), mRenderer.straightLine.getY_start(),mRenderer.straightLine.getZ_start()));
                Log.i("setPointer", String.format("x-end: %f, y-end: %f, z-end:%f", out[0], out[1],out[2]));
                Log.i("setPointer", String.format("POS: %d", mRenderer.straightLine.pointBufferPos));
                

                currentLines.setX_end(out[0]);//设置直线终点x坐标 
                currentLines.setY_end(out[1]);//设置直线终点y坐标 随着移动不断更新
                // out[2]中存着z坐标,但是平面用不上,所以z坐标不变,依旧是固定值0              
                break;
            /**
             * 离开屏幕的位置
             */
            case MotionEvent.ACTION_UP:
                
                
                break;
            default:
                break;
        }
        /**
         * 注意返回值
         * true:view继续响应Touch操作;
         * false:view不再响应Touch操作,故此处若为false,只能显示起始位置,不能显示实时位置和结束位置
         */

        return true;

    }

简单解释一下,首先众所周知,Renderer有如下三个默认存在的方法:
onSurfaceCreated() - 调用一次以设置视图的 OpenGL ES 环境。
onDrawFrame() - 每次重新绘制视图时调用。
onSurfaceChanged() - 当视图的几何图形发生变化(例如当设备的屏幕方向发生变化)时调用。
于是,由于每次我们触摸移动,更新坐标以后,图形就发生了变化,需要重新绘制图形,所以会重新调用renderer类当中的onSurfaceChanged()onDrawFrame()
一般的直线类的绘制函数draw()会放在**onDrawFrame()**里。

第三步,坐标转换

opengl内部的坐标范围是(-1,1),而我们通过触摸事件获取到的是屏幕坐标,想要直接把屏幕坐标作为点的坐标传值肯定是不行的。
有一点需要特别注意的是,GLES20中对于Matrix.frustumM的左右边缘上下界一定要设置为(-1,1)否则会出现坐标转换的分布无法覆盖整个屏幕,从而出现坐标转换的差异。(有不少教程是直选用了部分象限,因而左右的上下界设置为了Ratio的值)
上述代码中,我用了一个out的数组存储坐标转换后的坐标,转换函数我写在了我的Renderer类里,主要是调用了GLU库里的 GLU.gluUnProject()函数,在对得到的坐标,用变换矩阵相乘,由于笔者还没学习图形学,所以具体原理并不是很懂,但是网上有不少教程有对此做说明。
(PS:虽然我看了以后还是一知半解,估计等我学了图形学后就明白了,大概)。

public float[] handleTouch(float rx, float ry,StraightLine line) {
   
   
        float[] near_xyz = line.unProject(rx, ry, 0, mVMatrix, mProjMatrix, viewWidth, viewHeight);
        return near_xyz;
    }

UnProject()函数如下.

public float[] unProject(float xTouch, float yTouch, float winz,
                              float[] viewMatrix,
                              float[] projMatrix,
                              int width, int height) {
   
   
        int[] viewport = {
   
   0, 0, width, height};

        float[] out = new float[3];
        float[] temp = new float[4];
        float[] temp2 = new float[4];
        // get the near and far ords for the click

        float winx = xTouch, winy = (float) viewport[3] - yTouch;

        int result = GLU.gluUnProject(winx, winy, winz, viewMatrix, 0, projMatrix, 0, viewport, 0, temp, 0);

        Matrix.multiplyMV(temp2, 0, viewMatrix, 0, temp, 0);
        if (result == 1) {
   
   
            out[0] = temp2[0] / temp2[3];
            out[1] = temp2[1] / temp2[3];
            out[2] = temp2[2] / temp2[3];
        }
        return out;
    }

第四步,绘制多条直线

简单说明一下原理,定义一个直线类的ArrayList,不断的存储直线,每次遍历ArrayList将里面所有的直线类都绘制一遍,没想到吧,你以为是你在原先的画面上一条一条线的加上去的,实际上是每次所有的线都给你重新绘制了一边哒!!!
由于计算机处理速度很快,就给了你一种是一条线一条线加进去的错觉,hhh。
下面放代码,有两种,一种写在Activity里的:

public class StraightLineActivity extends AppCompatActivity implements View.OnTouchListener {
   
   
    private OneGlSurfaceView glSurfaceView;
    private OneGlRenderer mRenderer;
    public StraightLine currentLines = null;  //当前绘制的线
    public List<StraightLine> linesList = new ArrayList<>(); //当前绘制线的表

    public long frameCount = 0;  //共绘制了多少帧
    private float ratio;
    private int viewWidth;
    private int viewHeight;

    @Override
    protected void onCreate(
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值