Android 基于OpenGL ES2.0 的CircleProgressBar

时间:2021-05-16 03:58:17

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

类似:

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);表示图像会随着时间而进行旋转
Android 基于OpenGL ES2.0 的CircleProgressBar

旋转的速度可以通过
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);

表示不加旋转的原本动画

Android 基于OpenGL ES2.0 的CircleProgressBar


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