Android 基于OpenGL ES2.0 的CircleProgressBar

之前想在播放器上加一个那种卡顿的转转提示:

类似:

https://github.com/lsjwzh/MaterialLoadingProgressBar

这种效果的

由于当时没想到怎么在opengl es上实现,所以就没有做这个效果,后来有时间又去

研究了一下,好像这种效果会有一个加速减速的过程,我这里是匀速的,需要的话也可以再改改

下面上代码:

最主要的绘制代码是这个Ring

  1 package com.example.zhongchangwen.openglescircleprogressbar;
  2 
  3 import android.opengl.GLES20;
  4 
  5 import java.nio.ByteBuffer;
  6 import java.nio.ByteOrder;
  7 import java.nio.FloatBuffer;
  8 
  9 /**
 10  * Created by zhongchangwen on 2017/3/17.
 11  */
 12 
 13 public class Ring {
 14     private FloatBuffer vertexBuffer;
 15 
 16     private final String vertexShaderCode =
 17             "uniform mat4 uMVPMatrix;" +
 18                     "attribute vec4 vPosition;" +
 19                     "void main() {" +
 20                     "  gl_Position =  uMVPMatrix * vPosition;" +
 21                     "}";
 22 
 23     private final String fragmentShaderCode =
 24             "precision mediump float;" +
 25                     "uniform vec4 vColor;" +
 26                     "void main() {" +
 27                     "  gl_FragColor = vColor;" +
 28                     "}";
 29     private final int mProgram;
 30 
 31     private int mPositionHandle;
 32     private int mColorHandle;
 33     private int mMVPMatrixHandle;
 34 
 35     private int vertexCount = 360 * 2;
 36     private float radius = 1.0f;
 37     // Outer vertices of the circle
 38     private int outerVertexCount = vertexCount / 2 - 1;
 39 
 40     static final int COORDS_PER_VERTEX = 3;
 41     private float ringCoords[] = new float[vertexCount * COORDS_PER_VERTEX]; // (x,y,z) for each vertex
 42     private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
 43 
 44     ByteBuffer bb = ByteBuffer.allocateDirect(ringCoords.length * 4);
 45     private float mProgressArc = 1 / 6.0f;
 46     private boolean positive = true;
 47     private float position = 0.0f;
 48     private int mRepeat = 0;//重复次数,六次一个周期
 49     private float mRadius = 0.5f;//半径 
 51     float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 1.0f};
 52 
 53     public Ring() {
 54 
 55         bb.order(ByteOrder.nativeOrder());
 56 
 57         generateVertex(mProgressArc);
 58 
 59         // create empty OpenGL ES Program
 60         mProgram = Utils.initProgram(vertexShaderCode, fragmentShaderCode);
 61     }
 62 
 63     private void generateVertex(float progressArc) {
 64         float center_x = 0.0f;
 65         float center_y = 0.0f;
 66         int idx = 0;
 67 
 68         if (positive) {
 69             for (int i = 0; i < outerVertexCount; ++i) {
 70                 float percent = (i / (float) (outerVertexCount - 1));
 71                 float rad = (float) (percent * Math.PI * progressArc) + position;
 72 
 73                 //Vertex position
 74                 float outer_x = (center_x + radius * (float) Math.cos(rad)) * mRadius;
 75                 float outer_y = (center_y + radius * (float) Math.sin(rad)) * mRadius;
 76 
 77                 ringCoords[idx++] = outer_x;
 78                 ringCoords[idx++] = outer_y;
 79                 ringCoords[idx++] = 3.0f;
 80 
 81                 ringCoords[idx++] = outer_x;
 82                 ringCoords[idx++] = outer_y;
 83                 ringCoords[idx++] = 5.0f;
 84             }
 85         } else {
 86             for (int i = 0; i < outerVertexCount; ++i) {
 87                 float percent = (i / (float) (outerVertexCount - 1));
 88                 float rad = (float) (percent * Math.PI * progressArc) + (11 / 6.0f - mProgressArc) * (float) Math.PI + position;
 89 
 90                 //Vertex position
 91                 float outer_x = (center_x + radius * (float) Math.cos(rad))*0.5f;
 92                 float outer_y = (center_y + radius * (float) Math.sin(rad))*0.5f;
 93 
 94                 ringCoords[idx++] = outer_x;
 95                 ringCoords[idx++] = outer_y;
 96                 ringCoords[idx++] = 3.0f;
 97 
 98                 ringCoords[idx++] = outer_x;
 99                 ringCoords[idx++] = outer_y;
100                 ringCoords[idx++] = 5.0f;
101             }
102 
103         }
104 
105         vertexBuffer = bb.asFloatBuffer();
106         vertexBuffer.put(ringCoords);
107         vertexBuffer.position(0);
108 
109         if (positive) {
110             mProgressArc += 0.01f;
111         } else {
112             mProgressArc -= 0.01f;
113         }
114 
115         if (mProgressArc >= 11 / 6.0f) {
116             positive = false;
117         }
118         if (mProgressArc <= 1 / 6.0f) {
119             positive = true;
120             mRepeat++;
121             position = -1 / 3.0f * (float) Math.PI * mRepeat;
122             if (mRepeat >= 6)
123                 mRepeat = 0;
124         }
125     }
126 
127     public void draw(float[] mvpMatrix) {
128         // Add program to OpenGL ES environment
129         GLES20.glUseProgram(mProgram);
130 
131         // get handle to vertex shader's vPosition member
132         mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
133 
134         // Enable a handle to the triangle vertices
135         GLES20.glEnableVertexAttribArray(mPositionHandle);
136 
137         //refresh the ring's state
138         generateVertex(mProgressArc);
139 
140         // Prepare the triangle coordinate data
141         GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
142                 GLES20.GL_FLOAT, false,
143                 vertexStride, vertexBuffer);
144 
145         // get handle to fragment shader's vColor member
146         mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
147 
148         // Set color for drawing the triangle
149         GLES20.glUniform4fv(mColorHandle, 1, color, 0);
150 
151         // get handle to shape's transformation matrix
152         mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
153 
154         // Pass the projection and view transformation to the shader
155         GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
156 
157         // Draw the ring
158         GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
159 
160         // Disable vertex array
161         GLES20.glDisableVertexAttribArray(mPositionHandle);
162     }
163 }

主要就是计算好环上面的顶点,然后按照画三角形的方法绘制

原理不难,主要是重复对顶点的计算。

mProgressArc * PI 是环的弧度,这里取1/6*PI 到 11/6*PI

positive 表示绘制方向,true表示顺时针绘制,false表示逆时针绘制

然后:

在渲染类里面处理一下投影的效果:

 1 public void onDrawFrame(GL10 unused){
 2         // Redraw background color
 3         GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
 4 
 5         // Set the camera position (View matrix)
 6         Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
 7 
 8         // Calculate the projection and view transformation
 9         Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
10 
11         float[] scratch = new float[16];
12         //set the animation cycle time to 1 second; 1000ms * 0.360 == 360 degree
13         long time = SystemClock.uptimeMillis() % 1000L;
14         //positive is anticlockwise; negative is clockwise
15         float angle = -0.360f * ((int) time);
16 
17         Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, -1.0f);
18         Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
19 
20         // Draw shape
21         mRing.draw(scratch);
22     }
mRing.draw(scratch);表示图像会随着时间而进行旋转


旋转的速度可以通过
long time = SystemClock.uptimeMillis() % 1000L;
float angle = -0.360f * ((int) time);

控制这个表示1秒旋转360度

long time = SystemClock.uptimeMillis() % 2000L;
float angle = -0.180f ((int)time);

表示两秒转360度


mRing.draw(mMVPMatrix);

表示不加旋转的原本动画


附上github地址:
https://github.com/george-cw/OpenGLESCircleProgressBar

原文地址:https://www.cnblogs.com/george-cw/p/6586983.html