Android opengles 实现触碰屏幕,根据运动轨迹画直线的功能
Android opengles 实现触碰屏幕,根据运动轨迹画直线的功能
- 目录
- 引言
- 第一步,先自己学会绘制一条固定坐标的直线
- 第二步,动态的绘制一条直线
- 第三步,坐标转换
- 第四步,绘制多条直线
- 代码
目录
补一张效果图:
在手机里显示线很清楚的,可能是屏幕录制帧数太低了,显示出来都看不清,实际是没问题的,有机会用另一个手机拍屏幕传一个吧,2333.
宽度加粗了4倍,这下应该能看清楚了hhhh。
引言
由于项目功能需要,要做一个能自定义画直线的功能,网上找了许多文章或项目demo,没找到画直线的,反而是更进一步的实现类似“绘画板”功能的代码和教程较多;但是,这样的功能下,假如使用者想画一个规则的图形——比如直线,那么必须手不能抖笔直的画出来才行,应用到我这个项目里的话实在太反人类了,很不合适,于是就只好自己做了。
由于本人实力有限,也是刚接触opengles这玩意,磨磨蹭蹭拼拼凑凑了好几天总算是勉强实现了画直线的功能。
其实,做着做着就感觉这东西和android开发基本没啥关系了,无非就是android给他做了界面与交互而已,不过后续也要学习Opengl的也算是开个头吧,唠叨有点多了,下面是正文。
按步骤来说,想要实现根据手指运动轨迹画直线主要有以下几个要点:
- 实现一条固定坐标的直线的绘制----->>>>手动绘制一条直线;
- 实现 屏幕坐标向opengles坐标的转换 ;
- 任意绘制多条直线 ;
第一步,先自己学会绘制一条固定坐标的直线
如何绘制一条直线参考官方文档里的三角形案例可以轻松实现,有现成案例,很多博主也是根据这些内容写的教程:https://developer.android.com/training/graphics/opengl/environment
本文是也是基于官方文档的三角形绘制案例改写的,具体如何绘制固定直线。
由于我没有中途保存过绘制直线的代码,下面提供的是已经实现自定义画线功能后的代码了,建议自己实现绘制一条固定坐标直线后再看后面的内容。
第二步,动态的绘制一条直线
仅仅实现这个功能其实也很简单,在renderer或者glsurfaceView里设置一个触摸事件,动态的更新直线类中的起点与终点的坐标即可。相关事件参考代码如下:(重点是事件内容)
Activity中设置时间的oncreate函数如下:
@Overrideprotected 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事件
@Overridepublic 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 clickfloat 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;@Overrideprotected 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);}@Overrideprotected void onPause() {super.onPause();if (glSurfaceView != null) {glSurfaceView.onPause();}}@Overrideprotected void onResume() {super.onResume();if (glSurfaceView != null) {glSurfaceView.onResume();}}@Overridepublic 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]);currentLines.setY_start(out[1]);synchronized (linesList) {linesList.add(currentLines);}break;/*** 触屏实时位置*/case MotionEvent.ACTION_MOVE: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]);currentLines.setY_end(out[1]);currentLines.draw(mRenderer.mProjMatrix,mRenderer.mVMatrix);break;/*** 离开屏幕的位置*/case MotionEvent.ACTION_UP:synchronized (linesList) {linesList.add(currentLines);}break;default:break;}/*** 注意返回值* true:view继续响应Touch操作;* false:view不再响应Touch操作,故此处若为false,只能显示起始位置,不能显示实时位置和结束位置*/return true;}
}
另一种,写render里的这种方法得现在SurfaceView里重写onTouchEvent
@Overridepublic boolean onTouchEvent(MotionEvent event) {renderer.setPointer(event);return true;}
然后,renderer里写**setPointer()**方法
public void setPointer(MotionEvent event) {this.x = event.getX();this.y = event.getY();this.z = 0f;switch (event.getAction()) {case MotionEvent.ACTION_DOWN:currentLines = new GLLine();synchronized (linesList) {linesList.add(currentLines);}break;case MotionEvent.ACTION_MOVE:Log.i("setPointer", String.format("x: %f, y: %f", x, y,z));this.x = x / height * 4f;this.y = -y / height * 4f;this.z = 0;currentLines.drawLine(x, y,z);break;case MotionEvent.ACTION_UP:break;}}
以上两种方法都可以。
到这里位置,理论上来说肯定是可以实现画多条线了,但是笔者当时到这一步以后发现还是只能绘制一条直线,排查了半天终于发现了问题所在。
由于我是参考的官方文档的draw()方法,里面有一句:
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
没注释掉这句的话,每次绘制他都会吧之前的清空掉,说是清空,其实就是全变成透明色了,前面的线重新绘制的时候就没了,每次就只剩下最后一次绘制的线了。
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;@Overrideprotected 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);}@Overrideprotected void onPause() {super.onPause();if (glSurfaceView != null) {glSurfaceView.onPause();}}@Overrideprotected void onResume() {super.onResume();if (glSurfaceView != null) {glSurfaceView.onResume();}}
}
代码
由于本身也是参考了很多网上开源的资源做出来的,所以会各种相似,希望大家不要介意。
(1)在XML文件里面申明Opengles的使用
<!-- Tell the system this app requires OpenGL ES 3.0. --><uses-feature android:glEsVersion="0x00030000" android:required="true" />
(2)GLSurfaceView
这个很简单,我的理解是基本就是给图形一个显示的空间,本身并不复杂,无需过多关注。
public class OneGlSurfaceView extends GLSurfaceView {private OneGlRenderer mRenderer;public OneGlSurfaceView(Context context) {super(context);// Create an OpenGL ES 2.0 contextsetEGLContextClientVersion(2);// Set the Renderer for drawing on the GLSurfaceView//setRenderer((Renderer) mRenderer);}@Overridepublic boolean onTouchEvent(MotionEvent event) {mRenderer.setPointer(event);return true;}@Overridepublic void setRenderer(Renderer renderer) {super.setRenderer(renderer);this.mRenderer = (OneGlRenderer) renderer;}
}
(3)Renderer类
public class OneGlRenderer implements Renderer, com.example.ty.openglndk.GLSurfaceView.Renderer {int viewWidth, viewHeight;float[] mVMatrix = new float[16];float[] mProjMatrix = new float[16];private int mwidth,mheight;float ratio;StraightLine straightLine;private StraightLine currentLines = null; //当前绘制的线private List<StraightLine> linesList = new ArrayList<>(); //当前绘制线的表@Overridepublic void onSurfaceCreated(GL10 gl, javax.microedition.khronos.egl.EGLConfig config) {// Set the background frame colorGLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);}@Overridepublic void onDrawFrame(GL10 gl) {// Redraw background color//GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);// 清除屏幕和深度缓存//gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); //不加这个可以产生残影(模拟器可以)// 重置当前的模型观察矩阵gl.glLoadIdentity();// 允许设置顶点//GL10.GL_VERTEX_ARRAY顶点数组gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);// 允许设置颜色//GL10.GL_COLOR_ARRAY颜色数组gl.glEnableClientState(GL10.GL_COLOR_ARRAY);//反走样gl.glEnable(GL10.GL_BLEND);//线条抗锯齿gl.glEnable(GL10.GL_LINE_SMOOTH);/***************绘制模型**************///StraightLine straightLine = new StraightLine();currentLines = new StraightLine(new float[] {0, 0, 0});currentLines.draw(mProjMatrix,mVMatrix);drawModel(gl);/************************************/// 取消颜色设置gl.glDisableClientState(GL10.GL_COLOR_ARRAY);// 取消顶点设置gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);//绘制结束gl.glFinish();}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {GLES20.glViewport(0, 0, width, height);viewWidth = width;viewHeight = height;// 设置透明色为清屏//gl.glClearColor(0, 0, 0, 0);mwidth = width;mheight = height;ratio = (float) width / height;// 设置OpenGL场景的大小,(0,0)表示窗口内部视口的左下角,(w,h)指定了视口的大小gl.glViewport(0, 0, width, height);// 设置投影矩阵gl.glMatrixMode(GL10.GL_PROJECTION);// 重置投影矩阵gl.glLoadIdentity();// 设置视口的大小gl.glFrustumf(-1, 1, -1, 1, 1, 10);//以下两句声明,以后所有的变换都是针对模型(即我们绘制的图形)gl.glMatrixMode(GL10.GL_MODELVIEW);gl.glLoadIdentity();Matrix.frustumM(mProjMatrix, 0, -1, 1, -1, 1, 3, 10);Matrix.setLookAtM(mVMatrix,0, 2,2,9, 0f,0f,0f, 0f,1.0f,0.0f);}public static int loadShader(int type, String shaderCode){// 创造顶点着色器类型(GLES20.GL_VERTEX_SHADER)// 或者是片段着色器类型 (GLES20.GL_FRAGMENT_SHADER)int shader = GLES20.glCreateShader(type);// 添加上面编写的着色器代码并编译它GLES20.glShaderSource(shader, shaderCode);GLES20.glCompileShader(shader);return shader;}public float[] handleTouch(float rx, float ry,StraightLine line) {float[] near_xyz = line.unProject(rx, ry, 0, mVMatrix, mProjMatrix, viewWidth, viewHeight);float[] far_xyz = line.unProject(rx, ry, 1, mVMatrix, mProjMatrix, viewWidth, viewHeight);return near_xyz;}/**帧绘制**/public void drawModel(GL10 gl) {int count = 0;synchronized (linesList) {for(StraightLine line : linesList) {count++;Log.i("drawModel", String.format("调用次数: %d",count));line.draw(mProjMatrix,mVMatrix);}}}public void setPointer(MotionEvent event) {switch (event.getAction()) {/*** 点击的开始位置*/case MotionEvent.ACTION_DOWN:float[] out = handleTouch(event.getX(),event.getY(),currentLines);currentLines = new StraightLine(new float[] {0, 0, 0});currentLines.setX_start(out[0]);currentLines.setY_start(out[1]);//mRenderer.straightLine.x_start=out[0];//mRenderer.straightLine.y_start=out[1];//mRenderer.straightLine.touch(out[0],out[1],0);//mRenderer.straightLine.z_start=out[2];//tvTouchShowStart.setText("起始位置:(" + event.getX() + "," + event.getY());break;/*** 触屏实时位置*/case MotionEvent.ACTION_MOVE://tvTouchShow.setText("实时位置:(" + event.getX() + "," + event.getY());out = handleTouch(event.getX(),event.getY(),currentLines);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", currentLines.getX_start(), currentLines.getY_start(),currentLines.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", currentLines.pointBufferPos));/*int pos = mRenderer.straightLine.pointBufferPos;if(mRenderer.straightLine.pointBufferPos/3%2!=0){//假如此时顶点数组内的点坐标数量不为偶数mRenderer.straightLine.touch(out[0],out[1],0,pos);mRenderer.straightLine.vertexCount++;}*/currentLines.setX_end(out[0]);currentLines.setY_end(out[1]);//currentLines.draw(mProjMatrix,mVMatrix);//mRenderer.straightLine.x_end=out[0];//mRenderer.straightLine.y_end=out[1];//mRenderer.straightLine.touch(out[0],out[1],0,pos);//mRenderer.straightLine.z_end=out[2];break;/*** 离开屏幕的位置*/case MotionEvent.ACTION_UP://tvTouchShow.setText("结束位置:(" + event.getX() + "," + event.getY());//straightLine.y_end=event.getY()/1000;out = handleTouch(event.getX(),event.getY(),currentLines);//mRenderer.straightLine.x_end=out[0];//mRenderer.straightLine.y_end=out[1];//mRenderer.straightLine.z_end=out[2];//mRenderer.straightLine.touch(out[0],out[1],0);synchronized (linesList) {linesList.add(currentLines);}currentLines.setX_end(out[0]);currentLines.setY_end(out[1]);Log.i("setPointer", String.format("List中直线个数:%d",linesList.size()));Log.i("setPointer", String.format("List中直线1:x1:%f y1:%f z1:%f",linesList.get(0).getX_start(),linesList.get(0).getY_start(),linesList.get(0).getZ_start()));Log.i("setPointer", String.format("List中直线1:x2:%f y2:%f z2:%f",linesList.get(0).getX_end(),linesList.get(0).getY_end(),linesList.get(0).getZ_end()));break;default:break;}/*** 注意返回值* true:view继续响应Touch操作;* false:view不再响应Touch操作,故此处若为false,只能显示起始位置,不能显示实时位置和结束位置*/}
}
(4)StraightLine 直线类
opengles中直线的绘制需要起始两个点的坐标,并且点的坐标都是3维的,比较关键或者看不懂的内容我都在注释里标注了。
官方文档中只提供了画三角形的实例,但是画三角形的原理与画直线是一样的,所以就根据三角形的案例直接修改了,下面是代码。
public class StraightLine {//顶点着色程序 - 用于渲染形状的顶点的 OpenGL ES 图形代码,官网里直接有提供现成的。private final String vertexShaderCode ="attribute vec4 vPosition;" +"void main() {" +" gl_Position = vPosition;" +"}";
//片段着色程序 - 用于使用颜色或纹理渲染形状面的 OpenGL ES 代码,同上官网提供。private final String fragmentShaderCode ="precision mediump float;" +"uniform vec4 vColor;" +"void main() {" +" gl_FragColor = vColor;" +"}";private FloatBuffer vertexBuffer;//节点缓冲区private boolean updateVertex = false;public static float x_start=0.0f;//初始起点坐标public static float y_start=0.5f;public static float z_start=0.0f;public static float x_end=0.5f;//初始终点坐标public static float y_end=-0.5f;public static float z_end=0.0f;// number of coordinates per vertex in this arraystatic final int COORDS_PER_VERTEX = 3;//每3个节点一个坐标float triangleCoords[] = { // in counterclockwise order:x_start, y_start, z_start, // tx_end, y_end, z_end};int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;private int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertexfloat[] mMVPMatrix = new float[16];//投影矩阵float[] mMMatrix = new float[16];//变换矩阵,用来做乘法的private float[] mMVMatrix = new float[16];//视角矩阵private float[] position;//位置public StraightLine(float[] position) {this.position = position;// 初始化ByteBuffer,长度为arr数组的长度*4,因为一个float占4个字节ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * 4);// 数组排列用nativeOrderbb.order(ByteOrder.nativeOrder());// 从ByteBuffer创建一个浮点缓冲区vertexBuffer = bb.asFloatBuffer();// 将坐标添加到FloatBuffervertexBuffer.put(triangleCoords);// 设置缓冲区来读取第一个坐标vertexBuffer.position(0);pointBufferPos = 0;//记录当前数组位置int vertexShader = OneGlRenderer.loadShader(GLES20.GL_VERTEX_SHADER,vertexShaderCode);int fragmentShader = OneGlRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,fragmentShaderCode);// 创建空的OpenGL ES程序mProgram = GLES20.glCreateProgram();// 添加顶点着色器到程序中GLES20.glAttachShader(mProgram, vertexShader);// 添加片段着色器到程序中GLES20.glAttachShader(mProgram, fragmentShader);// 创建OpenGL ES程序可执行文件GLES20.glLinkProgram(mProgram);}public void draw(float[] projMatrix, float[] viewMatrix) {//GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);// 将程序添加到OpenGL ES环境GLES20.glUseProgram(mProgram);// 获取顶点着色器的位置的句柄mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");// 启用三角形顶点位置的句柄GLES20.glEnableVertexAttribArray(mPositionHandle);//准备三角形坐标数据GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,GLES20.GL_FLOAT, false,vertexStride, vertexBuffer);// 获取片段着色器的颜色的句柄mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");/**实现屏幕坐标向坐标转换的核心代码**/Matrix.setIdentityM(mMMatrix, 0);Matrix.translateM(mMMatrix, 0, position[0], position[1], position[2]);Matrix.multiplyMM(mMVMatrix, 0, viewMatrix, 0, mMMatrix, 0);Matrix.multiplyMM(mMVPMatrix, 0, projMatrix, 0, mMVMatrix, 0);// 设置绘制三角形的颜色GLES20.glUniform4fv(mColorHandle, 1, color, 0);// 绘制三角形GLES20.glDrawArrays(GLES20.GL_LINE_STRIP, 0, vertexCount);// 禁用顶点数组GLES20.glDisableVertexAttribArray(mPositionHandle);}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 clickfloat 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;}
}
(5)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;@Overrideprotected 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);}@Overrideprotected void onPause() {super.onPause();if (glSurfaceView != null) {glSurfaceView.onPause();}}@Overrideprotected void onResume() {super.onResume();if (glSurfaceView != null) {glSurfaceView.onResume();}}
}
美中不足的是,为了能够实时显示直线的轨迹,其实每次最后一条线绘制了两次,按下去拖动的时候显示的是,draw的线条,手指离开时drawmodel才会把它的直线画出来,此时draw的直线也完成了绘制。
(PS:通过加粗线条以后,基本看不出来了,哈哈哈)
/***************绘制模型**************///StraightLine straightLine = new StraightLine();currentLines = new StraightLine(new float[] {0, 0, 0});currentLines.draw(mProjMatrix,mVMatrix);drawModel(gl);/************************************/
之所以这么做是因为,引入同步机制以后,发现绘制轨迹的时候,由于只取起点与终点,没有中间的轨迹,虽然画的很准,但是在手指起来之前是看不到自己画的线的。
我知道我这么描述你们估计听不懂,想看看啥样子的可以把draw函数注释了自己看看,哈哈哈。如果有大佬知道怎么解决这个问题的欢迎评论。
补个图,注释以后是这样的
参考文献如下:
[1]: https://blog.csdn.net/soely/article/details/79183525
[2]: https://blog.csdn.net/cjzjolly/article/details/81667087?utm_medium=distribute.pc_relevant_right.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param_right&depth_1-utm_source=distribute.pc_relevant_right.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param_right
Android opengles 实现触碰屏幕,根据运动轨迹画直线的功能相关推荐
- android imageview 多点触碰(MultiTouch)实现图片拖拽移动缩放
刚用android手机 发现手机自带的图片浏览器挺酷 可以用手指移动 缩放 还有动画效果 Intent intent = new Intent(Intent.ACTION_VIEW); ...
- 我们手指触碰屏幕都做了什么?
1:首先我自定义一个LinearLayout,和TextView,重写它们的onInterceptTouchEvent和onTouchEvent方法,观察它们都返回值. 前者onInterceptTo ...
- Android源码解析触碰机制
分发 dispatchTouchEvent 触碰屏幕时会触发的view方法,原理需要看更深层次的源码,这里可以理解为入口 拦截 onInterceptTouchEvent 消费 onTouch ...
- android 处理多点触控手势
编写:Andrwyw - 原文:http://developer.android.com/training/gestures/multi.html 多点触控手势是指在同一时间有多点(手指)触碰屏幕.本 ...
- iOS--触碰响应UIResponder UIGestureRecognizer
疯狂iOS讲义总结 一.在iOS中,触碰的响应是以响应者链的形式进行的.也就是说,当用户和某个控件交互时,该控件会成为第一响应者(First Responder),第一响应者作为响应者链的开始,交互交 ...
- android 防触碰功能,类似打电话时屏幕熄灭
防触碰功能用到的是android手机的距离感应器 1.申请距离感应器管理者 <span style="white-space:pre"> </span>Se ...
- Android基础新手教程——3.4 TouchListener PK OnTouchEvent + 多点触碰
Android基础新手教程--3.4 TouchListener PK OnTouchEvent + 多点触碰 标签(空格分隔): Android基础新手教程 本节引言: 如题,本节给大家带来的是To ...
- android nfc标签类型,Android NFC标签 开发深度解析 触碰的艺术
原标题:Android NFC标签 开发深度解析 触碰的艺术 本文来自于CSDN博客,作者:郭朝,已获授权,版权归原作者所有,未经作者同意,请勿转载. 欢迎同有博客好文章的作者加微信(ID:tm_fo ...
- 【Android 】零基础到飞升 | TouchListener PK OnTouchEvent + 多点触碰
3.4 TouchListener PK OnTouchEvent + 多点触碰 分类 Android 基础入门教程 本节引言: 如题,本节给大家带来的是TouchListener与OnTouchEv ...
最新文章
- db2top详细使用方法_Py之PIL:Python的PIL库的简介、安装、使用方法详细攻略
- 优秀的服务器托管服务商的必备要素
- rz安装 xshell_利用XShell上传、下载文件(使用sz与rz命令)
- springboot 直接转发调用_springboot-过滤器的页面跳转【重定向与请求转发】-异常报错...
- if java_Java 条件语句
- UCMap移动GIS 时空地图GIS
- php cpu mac,PHP 获得计算机的唯一标识[CPU,网卡 MAC地址]
- Find a girl friend
- 华为产品技术学习笔记之路由原理(一)
- jQuery load() 中文乱码
- c++ 编写函数返回两个值最小值_结合实例来分析SQL的窗口函数
- 蓝桥杯第六届省赛JAVA真题----打印菱形
- 培养用户习惯才是软件的唯一出路!
- 数据结构—二叉排序树
- 塞雷三分钟漫画中国史1
- MatrixOne混沌测试之道
- 关于网站项目计划书的写法
- Dest0g3 520迎新赛WP
- 数学建模基本算法模型
- “硬核”刘强东是怎么炼成的?
热门文章
- 报表在linux下部署后中文变成小方块
- HTMLCSS学习笔记(二十四)——利用border属性制作太极图与哆啦A梦
- Module containing this breakpoint has not yet loaded or the breakpoint adress could not be obtained.
- H5 百度高德地图导航
- word2vec的应用:gensim相似度检测(附代码)
- 第四章课后习题-用Python实现羊车门问题,最大公约数计算,猜字游戏,统计不同字符个数。
- Go语言IDE GoLand的BUG
- 欧拉函数(求与n互质的数的个数)
- 保研历程(经验分享、保研流程介绍)
- java中任何变量都可以被赋值为null,java中当给一个对象赋值为null时发生了什么...