Android opengles 实现触碰屏幕,根据运动轨迹画直线的功能
目录
补一张效果图:

在手机里显示线很清楚的,可能是屏幕录制帧数太低了,显示出来都看不清,实际是没问题的,有机会用另一个手机拍屏幕传一个吧,2333.

宽度加粗了4倍,这下应该能看清楚了hhhh。
引言
由于项目功能需要,要做一个能自定义画直线的功能,网上找了许多文章或项目demo,没找到画直线的,反而是更进一步的实现类似“绘画板”功能的代码和教程较多;但是,这样的功能下,假如使用者想画一个规则的图形——比如直线,那么必须手不能抖笔直的画出来才行,应用到我这个项目里的话实在太反人类了,很不合适,于是就只好自己做了。
由于本人实力有限,也是刚接触opengles这玩意,磨磨蹭蹭拼拼凑凑了好几天总算是勉强实现了画直线的功能。
其实,做着做着就感觉这东西和android开发基本没啥关系了,无非就是android给他做了界面与交互而已,不过后续也要学习Opengl的也算是开个头吧,唠叨有点多了,下面是正文。
按步骤来说,想要实现根据手指运动轨迹画直线主要有以下几个要点:
- 实现一条固定坐标的直线的绘制----->>>>手动绘制一条直线;
- 实现 屏幕坐标向opengles坐标的转换 ;
- 任意绘制多条直线 ;
第一步,先自己学会绘制一条固定坐标的直线
如何绘制一条直线参考官方文档里的三角形案例可以轻松实现,有现成案例,很多博主也是根据这些内容写的教程: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(

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

被折叠的 条评论
为什么被折叠?



