cc>真*复习专用
文章目录
多媒体应用开发
MediaPlayer的状态图和生命周期及相关方法
以下是MediaPlayer的生命周期以及他们之间进行转换的时候调用的方法的图表。
IDlE状态
当使用的关键字new
实例化了一个MediaPlayer对象或者是调用了方法reset()
之后会进入此状态。
Initialized状态
调用方法setDataSource()
设置要播放的媒体文件之后进入这个状态。
Preparing状态
调用方法prepareAsync()
后从Initialized状态进入此状态,此时代表正在加载资源,是一种异步加载方法。
Prepared状态
此状态代表资源已经加载完毕,资源没有任何问题,进入预播放状态。
有两种进入此状态的途径,一种是调用方法prepare()
同步加载资源,在加载完成后进入此状态;另一种是从Preparing状态在异步加载完数据之后进入此状态。
Started状态
正在进行媒体播放,此时可以使用seekTo()
方法指定媒体播放的位置。
使用start()
方法进入此状态。
Paused状态
暂停状态。
暂停之后可以再次调用start()
方法进入播放状态。
Stopped状态
调用stop()
之后进入此状态。
在此状态下不能直接调用start()
方法再次进行播放,必须调用prepare()
或者是preprareAsync()
方法后重进进入Prepared状态后才能调用start()
方法播放。
PlaybackCompleted状态
播放完毕之后进入此状态,此时用户可以重新调用start()
方法再次进行播放,也可以使用seekTo()
方法重新定位播放的位置。也可以使用stop()
方法进入Stopped状态。
Error状态
当用户播放操作之中出现了某些错误操作之后进入此状态,在此状态调用reset()
方法使播放器重新变为Idle状态。
在这个时候播放器的生命周期已经结束了。
End状态
在调用了release()
方法后进入这个状态,这个时候会释放播放器所占用的所有软硬件资源,并且不会再进入其他的状态。
MediaPlayer音视频播放的基本实现
直接new一个播放器,这个时候需要调用方法setDataSource()
方法设置播放的文件,并在设置完播放的文件之后需要调用prepare()
或者是prepareAsync()
方法,使播放器进入预播放状态。
MediaPlayer mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource("");
mediaPlayer.prepareAsync();
// mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
使用create()
方法创建,使用此方法在创建之后直接进入预播放状态,无需也不能调用prepare()
或者是prepareAsync()
方法。
MediaPlayer mediaPlayer = MediaPlayer.create(this, Uri.parse(""));
其中setDataSource()
方法可以去设置来自SDCard或是assets、row以及网络的媒体资源。
create()
可以设置来自网络和row的媒体资源。
Android的音效框架AudioEffect的基本应用;
声音特效VolumeShaper的基本应用
如何使用SoundPool播放音频以及与MediaPlayer的区别
VideoView播放视频的操作方法
MediaRecorder框架的基本知识和应用方法
MediaRecorder
录制的音频文件是经过压缩后的,需要设置编码器,并且录制的音频文件可以用系统自带的Music播放器播放。MediaRecorder已经集成了录音、编码、压缩等,并支持少量的录音音频格式,但是这也是他的缺点,支持的格式过少并且无法实时处理音频数据。
AudioRecord
主要实现对音频实时处理以及边录边播功能,相对MediaRecorder比较专业,在声音录制过程中,可以处理采集的声音数据,如降噪、合成等。过程为一段一段进行录制然后得到数据分别进行处理。录制的是PCM格式的音频文件,如果保存成音频文件,是不能够被播放器播放的,需要用AudioTrack来播放,所以必须先写代码实现数据编码以及压缩。
相关类:
- MediaRecorder.AudioSource
- MediaRecorder.VideoSource
- MediaRecorder.OutputFormat
- MediaRecorder.AudioEncoder
- MediaRecorder.VideoEncoder
所需权限
<!-- 授予程序录制声音的权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 授予程序使用摄像头的权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 授予使用外部存储器的权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Camera2的基本架构和应用
MediaProjection框架的基本知识、特点和应用
由于使用了媒体的映射技术手段,故截取的屏幕并不是真正的设备屏幕,而是截取的通过映射出来的“虚拟屏幕”。
1、获取MediaProjectionManager
该对象是一个系统服务,可通getSystemService(Context.MEDIA_PROJECTION_SERVICE)获取
2、通过MediaProjectionManager.createScreenCaptureIntent()获取Intent
弹出dialog询问用户是否授权应用捕捉屏幕,同时覆写onActivityResult()获取授权结果。
3、通过startActivityForResult传入Intent, 如果授权成功,在onActivityResult中通过MediaProjectionManager.getMediaProjection(resultCode,data)获取MediaProjection
4、创建ImageReader,构建VirtualDisplay
5、最后就是通过ImageReader截图,就可以从ImageReader里获得Image对象。
6、将Image对象转换成bitmap
OpenGL ES 3D应用开发
OpenGL的基本知识和坐标系
OpenGL ES(OpenGL for Embedded Systems) 是 OpenGL 的子集,针对手机、PDA 和游戏主机等嵌
入式设备而设计。该规范也是由 Khronos Group 开发维护。
OpenGL ES 去除了四边形(GL_QUADS)、多边形(GL_POLYGONS) 等复杂图元,以及许多非绝对必要的特性,剩下最核心有用的部分。可以理解成是一个在移动平台上能够支持 OpenGL 最基本功能的精简规范。
右手坐标系
伸开右手,大拇指指向X轴正方向,食指指向Y轴正方向,其他三个手指指向Z轴正方向。
在右手坐标系中,确定旋转轴后,右手握成拳头,拇指指向旋转轴的正方向,其余手指的弯曲方向即为旋转的正方向,跟手指弯曲方向一致的旋转记为正向,相反则为负向。
OpenGL ES采用坐标系
OpenGL ES采用的是右手坐标,选取屏幕中心为原点,从原点到屏幕边缘默认长度为1,也就是说默认情况下,从原点到(1,0,0)的距离和到(0,1,0)的距离在屏幕上展示的并不相同。即向右为X正轴方向,向左为X负轴方向,向上为Y轴正轴方向,向下为Y轴负轴方向,屏幕面垂直向上为Z轴正轴方向,垂直向下为Z轴负轴方向。
其中,屏幕中心是坐标原点。
3D坐标转换为2D坐标
OpenGL 要求输入的顶点坐标都是标准化设备坐标,即每个顶点的 x、y、z 都在 -1 到 1 之间,由标准化设备坐标转换为屏幕坐标的过程中会经历变换多个坐标系统,在这些特定的坐标系中,一些操作和计算可以更加方便。
物体顶点的起始坐标再局部空间(Local Space),这里称它为局部坐标(Local Coordinate),它在之后会变成世界坐标(world Coordinate),观测坐标(View Coordinate),裁剪坐标(Clip Coordinate),并最后以屏幕坐标(Screen Corrdinate)的形式结束
正交投影和透视投影
正交投影(Orthographic projection), 需要指定一个正方形/长方形的视景体,在视锥体以外的任何物体都不会被绘制,所以实际大小相同的物体在屏幕上都具有相同的大小。物体不会随距离观测点的位置而大小发生变化。正交投影比较适合平面图形/2D图形渲染时使用
透视投影(perspective projection),对于肉眼直观的感受是,近大远小的,这种视觉效果称之为透视。透视投影要模仿肉眼的这种效果,是使用透视投影矩阵来完成的;在3D开发中更为常见,同样需要指定视景体的,而这个视景体并不是类似于正方体,看起来像平截体。透视投影一般会使用于3D图像渲染,因为它会更加逼真,距离观测点越远,物体越小,距离观测点越近,物体越大。
渲染管线
图形渲染管线可以被划分为两个主要部分:第一部分把3D坐标转换为2D坐标,第二部分是把2D坐标转变为实际的有颜色的像素。
OpenGL 渲染管线流程为:顶点数据 -> 顶点着色器 -> 图元装配 -> 几何着色器 -> 光栅化 -> 片段着色器 -> 逐片段处理 -> 帧缓冲
- 顶点属性
主要由顶点坐标和颜色值组成。 - 顶点着色器
顶点着色器主要的目的是把3D坐标转为另一种3D坐标(屏幕2D坐标),同时顶点着色器允许我们对顶点属性进行一些基本处理。
在这个阶段每个顶点是分别单独处理的,这个阶段所接受的是每个顶点的属性特征,输出的则是变换后的顶点数据。 - 图元装配
将顶点着色器输出的所有顶点作为输入,并所有的点装配成指定图元的形状。这里的图元指的是:点、线和三角。
需要注意的是,所有复杂的图形都是由三角形组成的。 - 几何着色器
几何着色器把图元形式的一系列顶点的集合作为输入,它可以通过产生新顶点构造出新的(或是其它的)图元来生成其他形状。 - 光栅化
几何着色器的输出会被传入光栅化阶段,这里它会把图元映射为最终屏幕上相应的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)。在片段着色器运行之前会执行裁切(Clipping)。裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。 - 片段着色器
主要目的是计算一个像素的最终颜色,这也是所有OpenGL高级效果产生的地方。通常,片段着色器包含3D场景的数据(比如光照、阴影、光的颜色等等),这些数据可以被用来计算最终像素的颜色。 - 逐片段处理
进行剪切、Alpha 测试、 模版测试、深度测试、混合等处理,这些操作将会最后影响其在帧缓冲区的颜色值。 - 帧缓冲
此阶段主要执行帧缓冲的写入操作,也是渲染管线的最后一步,负责将最终的像素点写到帧缓冲区。
OpenGL ES API创建和操作图形两个关键类:GLSurfaceView和 GLSurfaceView.Renderer
GLSurfaceView
继承自SurfaceView,实现了SurfaceHolder.Callback2接口。
主要是在SurfaceView的基础上它加入了EGL的管理,并自带了一个GLThread绘制线程(EGLContext创建GL环境所在线程即为GL线程),绘制的工作直接通过OpenGL在绘制线程进行,不会阻塞主线程,绘制的结果输出到SurfaceView所提供的Surface上,这使得GLSurfaceView也拥有了OpenGL ES所提供的图形处理能力。
GLSurfaceView.Renderer接口
GLSurfaceView.Renderer 接口定义了使用 OpenGL 绘图时所需的方法。该接口通过GLSurfaceView.setRenderer() 与 GLSurfaceView 关联在一起。
其中有以下三个接口函数:
- onSurfaceCreated(GL10 gl, EGLConfig config)
在GLSurfaceView创建之后,该方法被调用一次,通常在该方法中做一些无需改变的初始化操作。 - onDrawFrame(GL10 gl)
GLSurfaceView对象每一次重绘时系统都会调用该方法,由该方法执行具体的绘图工作。 - onSurfaceChanged(GL10 gl, int width, int height)
当GLSurfaceView对象的几何外形被改变时,该方法被调用。
调用GL10绘制2D/3D图形的基本步骤和主要方法
基本步骤
- 调用方法设置顶点矩阵
- 调用方法绘制图像
重要方法说明
glVertexPointer(int size,int type,int stride,Buffer pointer)
:定义一个顶点坐标矩阵。如果顶点矩阵功能启用,当调用glDrawArrays方法或glDrawElements方法时会使用。想要启用或禁止顶点矩阵,使用glEnableClientState或glDisableClientState方法,并以GL_VERTEX_ARRAY为参数。顶点矩阵初始为禁止,调用glDrawArrays方法或glDrawElements方法时无效。
- size 每个顶点的坐标维数,必须是2, 3或者4,初始值是4。
- type 指明每个顶点坐标的数据类型,允许的符号常量有GL_BYTE, GL_SHORT, GL_FIXED和GL_FLOAT,初始值为GL_FLOAT。
- stride 指明连续顶点间的位偏移,也就是连续顶点间的字节排列方式,即<从一个数据的开始到下一个数据的开始所相隔的字节数>,为0表示数组中的顶点被认为按照紧凑方式排列,初始值为0。
- pointer 指定了数组中第一个顶点的首地址(通常是数组名)。
glDrawArrays(int mode,int first,int count)
:由矩阵数据渲染图元。可以设置独立的顶点、法线、颜色矩阵,以及纹理坐标,并仅需调用glDrawArrays就可以通过它们构建一系列图元。
- mode 指明渲染哪一种图元。
- GL_POINTS
按照输入顺序绘制顶点。 - GL_LINES
按照输入顺序,两两一组绘制线段。 - GL_LINE_STRIP
按照输入顺序,依次连接每个顶点,绘出n-1条线段。 - GL_LINE_LOOP
按照输入顺序,依次连接每个顶点,最后将第n个和第1个顶点相连,得到一个由n条线段围成的闭合图形。 - GL_TRIANGLES
按照输入顺序,每三个一组,绘制三角形。 - GL_TRIANGLE_STRIP
按照输入顺序,按照n,n-1,n-2的组合绘制三角形。 - GL_TRIANGLE_FAN
按照输入顺序,以0,i,j的格式绘制三角形。
- GL_POINTS
- first 指明在允许访问的矩阵中的起始索引。
- count 指明要渲染的索引的数量。
输入矩阵中的数据不一定会所有的都用到。
glDrawElements(int mode,int count,int type,Buffer indices)
:由矩阵数据渲染图元。glDrawElements 方法还是需要传递顶点数据,但只需要传递物体实际上的顶点数据,也就是最少的,不重复的顶点数据。然后再向渲染管线传递要绘制的顶点数据的索引,根据索引从顶点数据中取出对应的顶点,然后再按照指定的方式进行绘制。当glDrawElements被调用,它会使用有序索引来查询可用矩阵中的元素,并以此创建序列几何图元,如果GL_VERTEX_ARRAY被禁用,则不会创建。
- mode 指明被渲染的是哪种图元
- count 指明被渲染的元素个数
- type 指明索引指的类型,不是GL_UNSIGNED_BYTE就是GL_UNSIGNED_SHORT。
- indices 指明存储索引的位置指针。
glColor4f(float red,float green,float blue,float alpha)
:设置当前颜色。
- red为当前颜色指明一个新的红色值,初始值为1。
- green 为当前颜色指明一个新的绿色值,初始值为1。
- blue 为当前颜色指明一个新的蓝色值,初始值为1。
- alpha 为当前颜色指明一个新的alpha值,初始值为1。
glColorPointer(int size,int type,int stride,Bufferpointer)
:定义一个颜色矩阵。
- size 指明每个颜色的元素数量,必须为4。
- type指明每个矩阵中颜色元素的数据类型,允许的符号常量有GL_UNSIGNED_BYTE, GL_FIXED和GL_FLOAT,初始值为GL_FLOAT。
- stride指明连续的点之间的位偏移,如果stride为0时,颜色被紧密挤入矩阵,初始值为0。
- pointer指明包含颜色的缓冲区,如果pointer为null,则为设置缓冲区。
其他方法说明
glEnable(int cap)
:启用服务器端GL功能。glEnable方法和glDisable方法可以启用和禁止各种功能,各种功能(除了GL_DITHER和GL_MULTISAMPLE)的初始值为GL_FALSE。
glDisable(int cap)
:禁用服务器端GL功能。例如,要关闭抗抖动功能,可以使用gl.glDisable(GL10.GL_DITHER);
例如:
GL_ALPHA_TEST 如果启用,将进行alpha 测试
GL_DEPTH_TEST 如果启用,将进行深度测试;就是让OpenGL ES负责跟踪每个物体在Z轴上的深度,这样就可避免后面的物体遮挡前面的物体。
GL_BLEND 如果启用,将引入的值与颜色缓冲区中的值混合
glHint (int target, int mode)
:该方法用于对OpenGL ES某方法进行修正。
glClearColor(float red, float green, float blue, float alpha)
:用于指定清除屏幕时使用的颜色,4个参数分别用于设置红、绿、蓝和透明度的值,值的范围是0.0f~1.0f。例如设置gl.glClearColor(0 , 0 , 0 , 0):就是用黑色“清屏”。
glShadeMode(int mode)
:该方法用于设置OpenGL ES的着色。
mode 指明使用哪种着色技术,可以取值GL_FLAT和GL_SMOOTH。默认取值是GL_SMOOTH。
在使用顶点数据绘制几何图形时,如果为每个顶点指定了顶点颜色,此时若使用GL_SMOOTH,每个顶点使用对应的顶点颜色来着色,而顶点之间的片元颜色则使用差值的方式来计算获得,结果就是渐变色;而若使用GL_FLAT,假设几何图形由n个三角形构成,则只会使用顶点颜色数组中最后n个颜色进行着色。
glViewport(int x , int y , int width , int height)
:设置3D视窗的位置与大小。其中前两个参数指定该视窗的位置,后两个参数指定该视窗的宽、高。
glMatrixMode(int mode)
:设置视图的矩阵模型。通常可接受GL10.GL_PROJECTION、GL10.GL_MODEVIEW两个常量值。
当调用glMatrixMode(GL10.GL_PROJECTION):代码后,指定将屏幕视为透视图(要想看到逼真的三维物体,这是必要的),这意味着越远的东西看起来越小;当调用glMatrixMode(GL10.GL_MODEVIEW):代码后,即将当前矩阵模式设为模型视图矩阵,这意味着任何新的变换都会影响该矩阵中的所有物体。
glLoadIdentity()
:相当于reset()方法,用于初始化单位矩阵。
glFrustumf (float left , float right , float bottom , float top , float zNear , float zFar)
:用于设置透视投影的空间大小。前路两个参数用于设置X轴上的最小坐标值、最大坐标值;中间两个参数用于设置Y轴上的最小坐标值、最大坐标值;后面两个参数用于设置Z轴上所能绘制的场景的深度的最小值、最大值。
glClear(int mask)
:清理缓冲区,并设置为预设值。glClear可以使参数为多个值按位与后的结果,以表明那个缓冲区需要清理。有如下值:
- GL_COLOR_BUFFER_BIT:表明颜色缓冲区。
- GL_DEPTH_BUFFER_BIT:表明深度缓冲区。
- GL_STENCIL_BUFFER_BIT:表明模型缓冲区。
glEnableClientState(int array)
:启用客户端的某项功能。glEnableClientState和glDisableClientState启用或禁用客户端的单个功能。默认的,所有客户端功能禁用。array可以是下列符号常量:
- GL_COLOR_ARRAY 颜色矩阵。
- GL_NORMAL_ARRAY 法线矩阵。
- GL_TEXTURE_COORD_ARRAY 纹理坐标矩阵。
- GL_VERTEX_ARRAY 顶点矩阵。
- GL_POINT_SIZE_ARRAY_OES(OES_point_size_arrayextension) 点大小矩阵。
代码
// 清除屏幕
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// 允许设置顶点 // GL10.GL_VERTEX_ARRAY顶点数组
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 设置顶点
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mBuffer);
//设置点的颜色为绿色
gl.glColor4f(0f, 1f, 0f, 0f);
//设置点的大小
gl.glPointSize(80f);
// 绘制点
gl.glDrawArrays(GL10.GL_POINTS, 0, 1);
gl.glFinish();
// 禁止顶点设置
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
// 清除屏幕缓存和深度缓存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 启用顶点坐标数据
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 启用顶点颜色数据
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
// 设置当前矩阵模式为模型视图。
gl.glMatrixMode(GL10.GL_MODELVIEW);
// 重置当前的模型视图矩阵
gl.glLoadIdentity();
gl.glTranslatef(-0.6f, 0.0f, -1.5f);
/*// 沿着Y轴旋转
gl.glRotatef(rotate, 0f, 0.2f, 0f);*/
// 设置顶点的位置数据
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, taperVerticesBuffer);
// 设置顶点的颜色数据
gl.glColorPointer(4, GL10.GL_FIXED, 0, taperColorsBuffer);
// 按taperFacetsBuffer指定的面绘制三角形
gl.glDrawElements(GL10.GL_TRIANGLE_STRIP
, taperFacetsBuffer.remaining(),
GL10.GL_UNSIGNED_BYTE, taperFacetsBuffer);
// 绘制结束
gl.glFinish();
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
纹理坐标和坐标映射
以下针对2D贴图
纹理坐标
Android使用的opengl es的纹理坐标系跟官方的opengl 纹理坐标系统不一样; 左上角(0,0),右下角(1,1);向右为S轴,向下为T轴。
也就是纹理坐标是相对于图片的坐标,和顶点坐标不一样。
坐标映射
以纹理坐标系选取一系列的顶点,按照输入顺序同我们绘图的顶点坐标一 一对应,以此确定我们要选取的区域以及如何对应到实际绘出的图像上。
纹理贴图的基本步骤
启用纹理映射功能
使用方法gl.glEnable()
启用相应的功能。
有以下几个参数可供选择
- GL_TEXTURE_1D
- GL_TEXTURE_2D
- GL_TEXTURE_3D
- GL_TEXTURE_CUBE_MAP
常用的为GL_TEXTURE_2D
创建纹理对象
gl.glGenTextures(int n,int[] textures,int offset)
- n:需要生成的纹理对象个数;
- textures:保存生成所有纹理对象的数组;
- offset:纹理对象数组的偏移(生成的纹理从数组什么位置开始赋值)
gl.glGenTextures(int n,java.nio.IntBuffer textures)
创建 n 个纹理,并将纹理ID 放在 textures 中;
设置纹理属性
通过glTexParameteri(int target, int pname, int param)
方法进行各种属性设置;常见的设置有放大、缩小时的纹理过滤以及纹理环绕。
三个参数的含义如下:
- target:纹理单元目标类型,表示我们激活的纹理单元对应了什么样的操作类型,比如GL_TEXTURE_1D、GL_TEXTURE_2D等。
- pname:属性名称;
- param:属性值;
指定纹理
对于2D贴图,使用方法glTexImage2D(int target, int level, int internalformat,int width,int height,int border,int format,int type,java.nio.Buffer pixels)
参数含义如下:
- target:活动纹理单元的目标类型,参考glBindTexture第一个参数;
- level:缩略图的图像级别,用于图像缩小时图像质量改善;
- internalformat:纹理单元中数据格式,GL_ALPHA,GL_LUMINANCE,GL_LUMINANCE_ALPHA,GL_RGB,GL_RGBA其中之一;
- width:纹理图像的宽度;
- height:纹理图像的高度;
- border:指定边框的宽度,这里必须为0;
- format:纹理数据的格式,必须匹配internalformat;
- type:纹理数据的数据类型,GL_UNSIGNED_BYTE等值;
- pixels:指向内存中的图像数据;
还有一个更加简便的方法:texImage2D (int target, int level, Bitmap bitmap, int border)
- target:操作的目标类型,设为 GL_TEXTURE_2D 即可
- level:纹理的级别,表示多级分辨率的纹理图象的级数。若只有一种分辨率,level为0。
- bitmap:图像
- border:边框,一般设为0
纹理映射
glTexCoordPointer(int size, int type, int stride, Buffer pointer)
设置顶点数组为纹理坐标缓存
- size:纹理顶点坐标的分量个数
- type:纹理坐标的数据类型,short, int, float, double都可以
- stride:位图的宽度,可以理解为相邻的两个纹理之间跨多少个字节,一般为0,因为一般不会在纹理中再添加其他的信息
- pointer:存放纹理坐标的数组
绑定纹理
glBindTexture (int target, int texture)
第一个参数是纹理类型,我们使用 2D 纹理,参数设为 GL_TEXTURE_2D, 第二个参数是纹理对象的 ID。
旋转、平移和缩放操作
平移
使用方法glTranslatef(float x, float y, float z)
平移就是将原位置加上平移向量,得到目标位置。
旋转
旋转使用方法glRotatef (float angle, float x, float y, float z)
完成
其中,参数angle是要旋转的角度,其余三个参数是对于x、y、z轴的选项,1为顺时针旋转,0为不旋转,-1为逆时针旋转。
要注意的是,旋转针对是整个坐标系;如果进行了多次的旋转,其每次所依据的旋转目标都是当前新的坐标系,并不是之前的。
缩放
缩放使用函数glScalef(float x, float y, float z)
将原坐标乘以参数x、y、z得到的就是新的坐标。
注意,如果是先进行的缩放操作,再进行了平移,那么实际平移的距离=设置的平移参数*缩放的倍数。
光照
网络应用开发
Socket通信(包括TCP和UDP)
TCP
服务端
创建ServerSocket
并将其绑定到指定的端口进行监听。
ServerSocket提供了多个构造方法,供我们以不同的方式,去创建服务。
- ServerSocket()
- ServerSocket (int port)
- ServerSocket (int port, int backlog)
- ServerSocket (int port, int backlog, InetAddress localAddress)
各个参数意思如下:
- port:绑定的端口号,如果值为0由系统自动分配。
- backlog:等待队列的长度,如果值为0由系统默认分配,同时这个值如果大于系统规定的最大长度,则为系统最大长度。一般系统规定的最大长度为50。
- localAddress:绑定的地址,如果系统有多个IP,只对指定的IP的请求进行相应;如果值为NULL,则可以使用任意的地址。
说明
1、无参构造方法的作用
因为在绑定端口之后就不能对Socket进行各种操作了,所有我们可以使用无参的构造方法创建对象,然后进行各种选项的设置,最后调用方法bind()
绑定端口。
2、等待队列
在服务器运行时可能会监听多个客户端的连接请求,当连接请求到达了上限的时候,服务器会拒绝新的连接请求。
常用设置
1、超时时间
setSoTimeout(int timeout)
2、是否允许重用服务器所绑定的地址
setResuseAddress(boolean on)
3、设置接收数据的缓冲区的大小
setReceiveBufferSize(int size)
4、设定连接时间、延迟和带宽
setPerformancePreferences (int connectionTime,int latency,int bandwidth)
响应过程
1、创建ServerSocket绑定指定的端口
2、调用accept()方法相应连接请求。如果收到客户端的请求,那就返回socket,建立连接。
3、调用方法读取\写入数据,相应请求。
4、通信结束,关闭套接字。
客户端
创建Socket,连接至服务器。
同样,Socket也提供了多个构造函数。
- Socket()
- Socket(InetAddress/String remoteAddress,int port)
- Socket(InetAddress/String remoteAddress,int port,InetAddress localAddr,int localPort)
以下是对各种参数的解释:
- remoteAddress:服务器的地址
- port:服务器的端口
- localAddr:本机IP
- localPort:本机端口
常用方法
- getInetAddress():获取远程服务端的IP地址
- getPort():获取远程服务端的端口
- getLocalAddress():获取本地客户端的IP地址
- getLocalPort():获取本地客户端的端口
- getInputStream():获得输入流,同时返回一个InputStream对象实例。
- getOutStream():获得输出流,同时返回一个OutputStream对象实例。
- isClose():是否已经关闭。
- isConnect():是否曾经连接过。
- isBound():是否已经与一个本地端口绑定。
- shutdownOutput():关闭输出流,这个时候还是可以进行数据接收操作的。
- close():关闭连接。
多线程操作
在服务端为每一个accept()返回的Socket创建一个服务线程,由这个线程处理与服务端的通信。
在服务端进行如此操作是为了同时对多个客户端的请求进行相。
在客户端,同样建立一个单独的线程同服务器进行通信,其和UI线程之间使用Handler进行通信。
之所以会这样进行操作是因为在进行通信的时候会阻塞当前的进程,如果是直接在UI线程中进行通信,那就会造成节目的卡顿。
UDP
1、创建DatagramSocket对象
2、创建一个byte数组用于接收/发送数据
3、创建DatagramPackage用来包装数组
以上不管是服务端还是客户端都要进行的操作
在服务端使用receive(package)
方法接收数据
在客户端使用方法send(package)
发送数据
URLConnection和HttpURLConnection进行网络通信
URLConnection
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
public class GetPostUtil
{
/**
* 向指定URL发送GET方法的请求
* @param url 发送请求的URL
* @param params 请求参数,请求参数应该是name1=value1&name2=value2的形式。
* @return URL所代表远程资源的响应
*/
public static String sendGet(String url, String params)
{
StringBuilder result = new StringBuilder();
BufferedReader in = null;
try
{
String urlName = url + "?" + params;
URL realUrl = new URL(urlName);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
// 建立实际的连接
conn.connect(); // ①
// 获取所有响应头字段
Map<String, List<String>> map = conn.getHeaderFields();
// 遍历所有的响应头字段
for (String key : map.keySet())
{
System.out.println(key + "--->" + map.get(key));
}
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null)
{
result.append(line).append("\n");
}
}
catch (Exception e)
{
System.out.println("发送GET请求出现异常!" + e);
e.printStackTrace();
}
// 使用finally块来关闭输入流
finally
{
try
{
if (in != null)
{
in.close();
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
return result.toString();
}
/**
* 向指定URL发送POST方法的请求
* @param url 发送请求的URL
* @param params 请求参数,请求参数应该是name1=value1&name2=value2的形式。
* @return URL所代表远程资源的响应
*/
public static String sendPost(String url, String params)
{
PrintWriter out = null;
BufferedReader in = null;
StringBuilder result = new StringBuilder();
try
{
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(params); // ②
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null)
{
result.append(line).append("\n");
}
}
catch (Exception e)
{
System.out.println("发送POST请求出现异常!" + e);
e.printStackTrace();
}
// 使用finally块来关闭输出流、输入流
finally
{
try
{
if (out != null)
{
out.close();
}
if (in != null)
{
in.close();
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
return result.toString();
}
}
HttpURLConnection
同URLConnection大同小异,在开启的 时候使用强制类型转换将URLConnection转换为HttpURLConnection。
在Android中使用OkHttp发送请求,接收相应的步骤
创建OkHttpClient对象
final OkHttpClient client = new OkHttpClient();
OkHttpClient client = new OkHttpClient.Builder().
readTimeout(30, TimeUnit.SECONDS).
cache(cache).
proxy(proxy).
authenticator(authenticator).
build();
发送接受请求:
private void getRequest() {
final Request request=new Request.Builder()
.get()
.tag(this)
.url("https://www.baidu.com")
.build();
new Thread(new Runnable() {
@Override
public void run() {
Response response = null;
try {
response = client.newCall(request).execute();
if (response.isSuccessful()) {
tv_show.setText(response.body().string());
} else {
throw new IOException("Unexpected code " + response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
private void postRequest() {
RequestBody formBody = new FormBody.Builder()
.add("id", msg).
.build();
final Request request = new Request.Builder()
.url("http://www.baidu.com")
.post(formBody)
.build();
new Thread(new Runnable() {
@Override
public void run() {
Response response = null;
try {
//同步方法
response = client.newCall(request).execute();
tv_show.setText(response.body().string());
//异步方法
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() // ⑤
{
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e)
{
e.printStackTrace();
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException
{
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
WebView的基本知识
WebView加载WEB页面的操作方法
WebView与 JS 的交互
桌面组件管理
静态壁纸和动态壁纸设置
App shortcuts和Pinned shortcuts两类快捷方式的创建
AppWidget创建的基本步骤。
- 在 AndroidManifest 中声明 App Widget
- 在 xml 目录定义 App Widget 的初始化 xml 文件
- 实现 Widget 具体布局的 Layout xml。
- 继承 AppWidgetProvider 类,实现具体的 Widget 业务逻辑。
传感器应用开发
Android传感器框架;
android SDK提供的与传感器相关的类有(位于android.hardware包):
Sensor:表示传感器的类。它保存有传感器名称,厂商,版本号。准确度等信息;
SensorEvent:表示传感器事件。它能够保存传感器的值,传感器类型,时间戳等信息;
SensorEventListener:用于接收传感器来自SensorManager的通知,当传感器发生变化时,它包括两个回调函数。
SensorManager:SensorManager让你能够访问设备(手机)的所有传感器。
manager=(SensorManager) getSystemService(SENSOR_SERVICE);
相关方法
– public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) 为指定的传感器注册监听器
– public void unregisterListener(SensorEventListener listener) 为所有传感器解除已注册的监听器
– public void unregisterListener(SensorEventListener listener, Sensor sensor) 为指定的传感器解除已注册的监听器
– public ListgetSensorList(int type) 获得可用传感器列表
– public Sensor getDefaultSensor(int type) 获得给定类型的默认传感器
地理定位
Android地理定位的基本框架
支持GPS的核心API
邻近警告应用
临近警告就是固定一个点,当手机与该点的距离少于指定范围时,可以触发对应的处理
调用 LocationManager的addProximityAlert() 方法添加临近警告
addProximityAlert(double latitude,double longitude,float radius,long expiration,PendingIntent intent)
latitude 指定固定点的经度
longitude 指定固定点的纬度
radius 指定半径长度
expiration 指定经过多少毫秒后该临近警告就会过期失效,-1 表示永不过期
intent 该参数指定临近该固定点时触发该 intent 对应的组件