首先需要实现一个画线工具,代码如下:

package com.project.testOpenGLWithAndroidUI;import android.util.Log;import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;import javax.microedition.khronos.opengles.GL10;/*** Created by cjz on 2018/8/14.*/public class GLLine {/**顶点字节数组**/private ByteBuffer pointByteBuffer;/**顶点RGBA字节数组**/private ByteBuffer colorByteBuffer;/**顶点坐标数组**/private FloatBuffer pointBuffer = null;/**顶点RGBA数组**/private FloatBuffer colorBuffer = null;/**正在写入第几个顶点float**/private int pointBufferPos = 0;/**正在写入第几个颜色float**/private int colorBufferPos = 0;/**初始化时的顶点数目**/private int initVertexCount = 1 * 1024;public void drawLine(float x, float y) {//按初始化大小初始化顶点字节数组和顶点数组if (pointBuffer == null) {pointByteBuffer = ByteBuffer.allocateDirect(initVertexCount * 4);    //顶点数 * sizeof(float)pointByteBuffer.order(ByteOrder.nativeOrder());pointBuffer = pointByteBuffer.asFloatBuffer();pointBuffer.position(0);pointBufferPos = 0;}//按初始化大小初始化RGBA字节数组和RGBA数组if (colorBuffer == null) {colorByteBuffer = ByteBuffer.allocateDirect(initVertexCount * 4);colorByteBuffer.order(ByteOrder.nativeOrder());colorBuffer = colorByteBuffer.asFloatBuffer();colorBuffer.position(0);colorBufferPos = 0;}//写入坐标值x,y,zpointBuffer.put(pointBufferPos++, x);pointBuffer.put(pointBufferPos++, y);pointBuffer.put(pointBufferPos++, 0f);//写入颜色值r,g,b,acolorBuffer.put(colorBufferPos++, 1f);colorBuffer.put(colorBufferPos++, (float) Math.random());colorBuffer.put(colorBufferPos++, 1f);colorBuffer.put(colorBufferPos++, 1f);//如果写入的颜色数超过初始值,将顶点数和颜色数组容量翻倍if (colorBufferPos * 4 >= initVertexCount) {Log.i("GLLines", "扩容点数到:" + initVertexCount);initVertexCount *= 2;ByteBuffer qbb = ByteBuffer.allocateDirect(initVertexCount * 4);    //顶点数 * sizeof(float) ;qbb.order(ByteOrder.nativeOrder());System.arraycopy(pointByteBuffer.array(), 0, qbb.array(), 0, (pointBufferPos) * 4);   //顶点数 * sizeof(float)pointByteBuffer = qbb;pointBuffer = pointByteBuffer.asFloatBuffer();ByteBuffer qbb2 = ByteBuffer.allocateDirect(initVertexCount * 4);    //顶点数 * sizeof(float) ;qbb2.order(ByteOrder.nativeOrder());System.arraycopy(colorByteBuffer.array(), 0, qbb2.array(), 0, (colorBufferPos ) * 4);  //sizeof(R,G,B,Alpha) * sizeof(float)colorByteBuffer = qbb2;colorBuffer = colorByteBuffer.asFloatBuffer();}}public int getVertexCount(){return pointBufferPos / 3;}public void drawTo(GL10 gl) {if (pointBuffer != null && colorBuffer != null) {pointBuffer.position(0);colorBuffer.position(0);gl.glVertexPointer(3, GL10.GL_FLOAT, 0, pointBuffer);gl.glColorPointer(4, GL10.GL_FLOAT,0, colorBuffer);gl.glLineWidth(3f);gl.glDrawArrays(GL10.GL_LINE_STRIP,0, pointBufferPos / 3); //添加的point浮点数/3才是坐标数(因为一个坐标由x,y,z3个float构成,不能直接用), 第三个参数count如果超过实际点数就会不断有指向0的点在最后
//            gl.glDrawElements(GL10.GL_LINE_STRIP,0, pointBufferPos / 3, null);  //第一个参数是点的类型,第二个参数是点的个数,第三个是第四个参数的类型,第四个参数是点的存储绘制顺序。}}
}

其中drawLine函数可以根据传来的浮点值写入到NativeBuffer中,如果线太长就会自动通过System.arraycopy扩容。drawTo是传入gl上下文之后把数组里面的顶点和顶点颜色绘制到OpenGL画布上的。(在里面我留了一个彩蛋,绿色浓度(0f~1f)我用了随机数进行赋值,使得线条呈现出紫色和白色交替的特殊效果,这也是Canvas+path难以实现的一点)

然后是渲染器:

package com.project.testOpenGLWithAndroidUI;import android.content.Context;
import android.opengl.GLSurfaceView.Renderer;
import android.util.Log;
import android.view.MotionEvent;import java.util.ArrayList;
import java.util.List;import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;public class MyGLRenderer implements Renderer {Context context; // Application's contextprivate float x;private float y;private GLLine currentLines = null;  //当前绘制的线private List<GLLine> linesList = new ArrayList<>(); //当前绘制线的表public long frameCount = 0;  //共绘制了多少帧private float ratio;private int width;private int height;public MyGLRenderer(final Context context) {this.context = context;}/**图形引擎回调产生绘图过程,每画完一帧又会调用这个函数画下一帧**/@Overridepublic void onDrawFrame(GL10 gl) {// 清除屏幕和深度缓存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);//绘制模型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) {this.width = width;this.height = 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(-ratio, ratio, -1, 1, 1, 10);//以下两句声明,以后所有的变换都是针对模型(即我们绘制的图形)gl.glMatrixMode(GL10.GL_MODELVIEW);gl.glLoadIdentity();}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {// 设置透明色为清屏gl.glClearColor(0, 0, 0, 0);}/**帧绘制**/public void drawModel(GL10 gl) {gl.glTranslatef(-2f * ratio, 2f, -2f); //必须有,z轴可以用于做缩放,按16比9来做,只要右下角象限synchronized (linesList) {for(GLLine line : linesList) {line.drawTo(gl);}}frameCount++;}public void clearAll() {synchronized (linesList) {linesList.clear();}}public void setPointer(MotionEvent event) {this.x = event.getX();this.y = event.getY();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));float realtiveX = x / height * 4f;  //4个象限float realtiveY = -y / height * 4f;currentLines.drawLine(realtiveX, realtiveY);break;case MotionEvent.ACTION_UP:break;}}}
 

onDrawFrame每16ms间隔就会被调用一次,也就是绘制一帧,然后我们真正绘制线条的地方是drawModel函数,drawModel函数绘制一个List里面保存的GLLine线条对象。setPointer里面每一次接收到down事件就会创建一个GLLine线条,move的时候会纪录线条轨迹顶点。而我们这里是采用比例进行绘图的,也就是假设屏幕为1920*1080,那么(0.1,0.1)这个坐标在实际中代表的就是(192,108)坐标。

然后是我们自定义的GLSurfaceView:

package com.project.testOpenGLWithAndroidUI;import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.view.MotionEvent;/*** Created by cjz on 2018/8/2.*/public class MyGLSurfaceView extends GLSurfaceView{private MyGLRenderer renderer;public MyGLSurfaceView(Context context) {super(context);}public MyGLSurfaceView(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic void setRenderer(Renderer renderer) {super.setRenderer(renderer);this.renderer = (MyGLRenderer) renderer;}@Overridepublic boolean onTouchEvent(MotionEvent event) {renderer.setPointer(event);return true;}
}

其中onTouchEvent的事件将直接传递到我实现的renderer中进行处理。

最后载入到MainActivity中:

package com.project.testOpenGLWithAndroidUI;import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;import com.project.testopengl.R;/*** Created by cjz on 2018/8/2.*/public class MainActivity extends Activity{private FrameLayout ll_container;private TextView tv_frame_rate;private MyGLRenderer myGlRenderer;private Button btn_clear;Handler handler = new Handler();private MyGLSurfaceView myGLSurfaceView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setTheme(android.R.style.Theme_Translucent_NoTitleBar);//强制横屏:setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);setContentView(R.layout.activity_main_3);ll_container = findViewById(R.id.ll_container);myGLSurfaceView = new MyGLSurfaceView(this);myGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);//设置背景透明:myGLSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);myGLSurfaceView.setZOrderOnTop(true);myGlRenderer = new MyGLRenderer(this);myGLSurfaceView.setRenderer(myGlRenderer);
//        myGLSurfaceView.setZOrderMediaOverlay(true);ll_container.addView(myGLSurfaceView);initView();loopGetRate();}/**利用handler+递归轮询帧率**/private void loopGetRate() {handler.postDelayed(new Runnable() {@Overridepublic void run() {tv_frame_rate.setText("FPS:" + myGlRenderer.frameCount);myGlRenderer.frameCount = 0;if(!MainActivity.this.isFinishing()) {loopGetRate();}}}, 1000);}private void initView() {tv_frame_rate = findViewById(R.id.tv_frame_rate); //帧率显示//清屏:btn_clear = findViewById(R.id.btn_clear);btn_clear.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {myGlRenderer.clearAll();}});}@Overrideprotected void onPause() {super.onPause();if (myGLSurfaceView != null) {myGLSurfaceView.onPause();}}@Overrideprotected void onResume() {super.onResume();if (myGLSurfaceView != null) {myGLSurfaceView.onResume();}}
}

我在onCreate处通过一些设置使得GLSurfaceView浮于App的最表面,并使得背景色透明,使得App其他地方可以通过绘制内容呈现。

最后是UI配置文件:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayoutandroid:id="@+id/glView"xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"><FrameLayoutandroid:id="@+id/ll_container"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:background="@drawable/background"></FrameLayout><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/btn_clear"android:layout_width="100px"android:layout_height="100px"android:text="clear"android:layout_alignParentBottom="true"android:layout_alignParentEnd="true" /><TextViewandroid:id="@+id/tv_frame_rate"android:textSize="20dp"android:textColor="#FFFFFF"android:text="null"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_toStartOf="@+id/btn_clear" /></RelativeLayout>
</FrameLayout>

使用效果:

因时间有限暂时没来得及仔细讲解代码的一些细节,呆有空时将会对本文进行更新。

使用GLSurfaceView实现涂鸦画板功能相关推荐

  1. 自定义view实现涂鸦(画板)功能

    自定义view实现涂鸦功能,包括撤销.恢复.重做.保存以及橡皮擦(在风格中实现)功能,小模块包括画笔颜色调整.画笔尺寸调整.画笔类型(包括正常画笔以及橡皮擦功能),之后又陆续实现了画圆.画矩形以及画箭 ...

  2. 在线涂鸦画板小程序源码

    简介: 一款功能简单的在线涂鸦画板小程序源码,可以二次开发 网盘下载地址: http://kekewangLuo.net/VsCTzCLAOVM0 图片:

  3. iOS:quartz2D绘图小项目(涂鸦画板)

    介绍:学了quartz2D的绘图知识后,我根据它的一些功能制作了一个小项目:涂鸦画板. 功能:绘制各种图形,还可以选取相册上的照片做涂鸦,然后保存到相册中.其中,还包括功能有:颜色的选取.线宽的选取. ...

  4. Android涂鸦画板原理详解——从初级到高级(二)

    前言 前面写了<Android涂鸦画板原理详解--从初级到高级(一)>,讲了涂鸦原理初级和中级的应用,现在讲解高级应用.如果没有看过前面一篇文章的同学,建议先去看看哈. 准备 高级涂鸦涉及 ...

  5. android 橡皮擦功能吗,android,安卓开发_Android 图片涂鸦橡皮擦功能,android,安卓开发 - phpStudy...

    Android 图片涂鸦橡皮擦功能 最近在做一个画板功能,大致的不同颜色画笔.不同粗细已经实现. 参照的是该教程:android-drawing-app 现在要做的功能是,从相册或者相机导入图像,然后 ...

  6. 在线绘画,在线画图,在线涂鸦画板

    功能地址 地址:https://tool.toforu.com/f/tuya.html 功能说明 在线绘画,在线画图,在线涂鸦画板. 功能使用 相关知识 在线绘画指的是使用鼠标或键盘等工具在电脑屏幕上 ...

  7. python怎么建立画板_Python基于opencv实现的简单画板功能示例

    本文实例讲述了Python基于opencv实现的简单画板功能.分享给大家供大家参考,具体如下: import cv2 import numpy as np drawing = False # true ...

  8. android画板需求分析,Android编程实现画板功能的方法总结【附源码下载】

    本文实例讲述了Android编程实现画板功能的方法.分享给大家供大家参考,具体如下: Android实现画板主要有2种方式,一种是用自定义View实现,另一种是通过Canvas类实现.当然自定义Vie ...

  9. 《Cocos Creator游戏实战》你画我猜中的画板功能

    你画我猜中的画板功能 创建节点 完成脚本 本节我们来做一个画板,该画板一共有三个小功能: 调节笔刷大小 改变笔刷颜色 橡皮擦 运行效果如下: Cocos Creator版本:2.2.0 后台回复&qu ...

  10. 涂鸦画板,监听touch事件,手机端

    通过监听canvas上的touch事件,在canvas上作图 <!DOCTYPE html> <html><head><meta charset=" ...

最新文章

  1. 我的第一份工作是个小公司
  2. PostgreSQL client's startup packet different between logical and normal stream replication
  3. Codeforces Round #554 (Div. 2) C. Neko does Maths (简单推导)
  4. zedgraph支持游标吗_经典格斗游戏中的隐藏人物,当年在游戏厅你能选出来吗
  5. HDU 4162 Shape Number(最小表示法)
  6. 论文阅读丨神经清洁: 神经网络中的后门攻击识别与缓解
  7. 9.6.1 三维数据可视化之曲面图
  8. ESP32开发 2.添加.c.h并修改CMakeLists,来定制自己的工程
  9. Java序列化机制和原理
  10. 【1】Python 视频文字识别提取 - Mp4转换成Mp3
  11. 计算机考试有python吗_计算机二级考试有python吗
  12. 计算机版的微信的功能是什么,计算机上的微信有没有收藏功能?电脑端如何打开微信收藏?...
  13. html固定按钮相对位置,css固定定位和绝对定位的区别是什么?
  14. 哪些短信平台能发国际短信?
  15. 记一次很坑很坑的报错java.lang.Exception: The class is not public.
  16. dialog沉浸式状态栏android,Dialog全屏,去掉状态栏的方式
  17. 冷冻水和冷却水的区别
  18. 用vmware安装雨林木风虚拟机系统的坑
  19. 嵌入式是什么?(一个电子产品的从0到1)-杂谈
  20. JS数组转字符串传到JAVA后端取出

热门文章

  1. 牛客网 2018校招真题 吉比特 直线上的点
  2. html tooltips效果,html5tooltips.js – 一款轻量级的3D工具提示插件
  3. 5G无线技术基础自学系列 | 时域资源
  4. 万字长文分析递归算法的时间和空间复杂度,从此对递归不再迷茫!
  5. Thinking in java-29  解耦合Decouple
  6. 基于SpringBoot+JSoup+POI+Swagger2实现校园教务系统成绩课程等信息抓取,并提供接口访问的小项目
  7. PLC可编程控制器、变频调速综合实验装置(网络型)
  8. 核心单词Word List 5
  9. ToneChip反馈降噪抑制器的UI设计
  10. 陈强教授《机器学习及R应用》课程 第五章作业