-
OpenGLES两种方式画立方体
2012-10-22 15:49:00其实两种方法都是画12个三角形画出来的,在OpenGL ES中,只支持三角形,所以任何复杂多边形都是由三角形画出来的。 第一种:顶点法 : 把一个四边形当成一个面,而一个面由两个三角形组成。一个三角形是不是有3个...OpenGlES这东西真TM蛋疼,研究了几天了,终于能把这东西画出来
,新手学开发就是不容易阿
其实两种方法都是画12个三角形画出来的,在OpenGL ES中,只支持三角形,所以任何复杂多边形都是由三角形画出来的。
第一种:顶点法
:
把一个四边形当成一个面,而一个面由两个三角形组成。一个三角形是不是有3个顶点?,所以一个面就有了3+3个顶点,一个立方体有6个面,6*6个顶点
此立方体的颜色也是根据顶点所渲染,正如定义这个立方体的顶点一样,不过它的参数可不是和定义顶点的一样哦,它的参数类型是:R,G,B,A,代表的是颜色值
public class OpenGlEsDemoActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GLSurfaceView glview = new GLSurfaceView(this); glview.setRenderer(new OpenGlRender()); setContentView(glview); } }
public class OpenGlRender implements GLSurfaceView.Renderer{ //每一个面画两个三角形,立方体有6个面 private float[] vertices={ -1.0f,1.0f,1f, // top left -1.0f,-1.0f,1f, // bottom left 1.0f,-1.0f,1f, //top right -1.0f,1.0f,1f, //bottom left 1.0f,-1.0f,1f, //bottom right 1.0f,1.0f,1f, //top right //前面 1.0f,1.0f,1f, 1.0f,-1.0f,1f, 1.0f,-1.0f,-1f, 1.0f,1.0f,1f, 1.0f,-1.0f,-1.0f, 1.0f,1.0f,-1f, //右面 -1.0f,1.0f,-1.0f, -1.0f,-1.0f,-1.0f, -1.0f,1.0f,1.0f, -1.0f,-1.0f,-1.0f, -1.0f,-1.0f,1.0f, -1.0f,1.0f,1.0f, //左面 1.0f,1.0f,-1.0f, 1.0f,-1.0f,-1.0f, -1.0f,-1.0f,-1.0f, 1.0f,1.0f,-1.0f, -1.0f,-1.0f,-1.0f, -1.0f,1.0f,-1.0f, //后面 -1.0f,1.0f,-1.0f, // top left -1.0f,1.0f,1.0f, //bottom left 1.0f,1.0f,-1.0f, //top right -1.0f,1.0f,1.0f, //bottom left 1.0f,1.0f,1.0f, //top right 1.0f,1.0f,-1.0f, // -top right上面 -1.0f,-1.0f,1.0f, -1.0f,-1.0f,-1.0f, 1.0f,-1.0f,-1.0f, -1.0f,-1.0f,1.0f, 1.0f,-1.0f,-1.0f, 1.0f,-1.0f,1.0f, //下面 }; //立方体的顶点颜色 private float[] colors={ 1f,0f,0f,1f, 1f,0f,0f,1f, 1f,0f,0f,1f, 1f,0f,0f,1f, 1f,0f,0f,1f, 1f,0f,0f,1f, 1f,0f,1f,1f, 1f,0f,1f,1f, 1f,0f,1f,1f, 1f,0f,1f,1f, 1f,0f,1f,1f, 1f,0f,1f,1f, 0f,1f,0f,1f, 0f,1f,0f,1f, 0f,1f,0f,1f, 0f,1f,0f,1f, 0f,1f,0f,1f, 0f,1f,0f,1f, 0f,0f,1f,1f, 0f,0f,1f,1f, 0f,0f,1f,1f, 0f,0f,1f,1f, 0f,0f,1f,1f, 0f,0f,1f,1f, 0.5f,0f,1f,1f, 0.5f,0f,1f,1f, 0.5f,0f,1f,1f, 0.5f,0f,1f,1f, 0.5f,0f,1f,1f, 0.5f,0f,1f,1f, 1f,0f,0.5f,1f, 1f,0f,0.5f,1f, 1f,0f,0.5f,1f, 1f,0f,0.5f,1f, 1f,0f,0.5f,1f, 1f,0f,0.5f,1f, }; FloatBuffer vertBuffer; //顶点缓冲 FloatBuffer colorBuffer; //颜色缓冲 float rx=-70f; //旋转角度 public OpenGlRender(){ ByteBuffer vbb=ByteBuffer.allocateDirect(vertices.length*4); vbb.order(ByteOrder.nativeOrder()); vertBuffer=vbb.asFloatBuffer(); vertBuffer.put(vertices); vertBuffer.position(0); ByteBuffer cbb= ByteBuffer.allocateDirect(colors.length*4); cbb.order(ByteOrder.nativeOrder()); colorBuffer = cbb.asFloatBuffer(); colorBuffer.put(colors); colorBuffer.position(0); } public void onSurfaceCreated(GL10 gl, EGLConfig config) { //启用深度测试 gl.glEnable(GL10.GL_DEPTH_TEST); // 所做深度测试的类型 gl.glDepthFunc(GL10.GL_DITHER); //黑色背景 gl.glClearColor(0f, 0f, 0f, 0.5f); //启用阴影平滑 gl.glShadeModel(GL10.GL_SMOOTH); //清除深度缓存 gl.glClearDepthf(1.0f); gl.glEnable(GL10.GL_TEXTURE_2D); //告诉系统对透视进行修正 gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); } public void draw(GL10 gl){ gl.glFrontFace(GL10.GL_CCW); gl.glEnable(GL10.GL_CULL_FACE); gl.glCullFace(GL10.GL_BACK); //开启顶点和纹理缓冲 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertBuffer); gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer); gl.glLoadIdentity(); gl.glTranslatef(0, 0, -5); gl.glRotatef(45f, 0f, 1f, 0f); //往右边(y轴)倾斜45度C gl.glRotatef(rx,1f, 0f, 0f); //往上面倾斜(x轴)倾斜,根据每次得到的角度 gl.glDrawArrays(GL10.GL_TRIANGLES, 0,vertices.length); gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); gl.glDisableClientState(GL10.GL_COLOR_ARRAY); gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glDisable(GL10.GL_CULL_FACE); rx--; //旋转角度减1 } public void onDrawFrame(GL10 gl) { // 清除深度和颜色缓存 gl.glClearColor(0f, 0f, 0f, 0.5f); gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glMatrixMode(GL10.GL_MODELVIEW); //设置矩阵模式 draw(gl); } public void onSurfaceChanged(GL10 gl, int width, int height) { gl.glViewport(0, 0, width, height); gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); GLU.gluPerspective(gl, 45.0f, (float)width/(float)height, 0.1f, 100.f); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); }
第二种:索引法
画出来的效果与 上面是一样的,不过它是通过索引所画出来的
还是分成6个面来画,一个面画2个三角形。
这里画出来的是这个立方体的最前面,对应的还是x,y,z轴。-1f,1f,1f, //0 -1f,-1f,1f, //1 1f,-1f,1f, //2 1f,1f,1f, //3
只不过的是在这个indices中指定了它的索引位置,所以它才会乖乖听话把四边形画出来private short[] indices={ 0,1,2, 0,2,3,}
0是左上角的坐标,1是左下角,2是右下角,3是右上角
public class Demo extends Activity{ @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); GLSurfaceView v = new GLSurfaceView(this); v.setRenderer(new OpenGlRender2(null)); setContentView(v); } }
public class OpenGlRender2 implements Renderer{ //顶点坐标x,y,z private float[] vertices= { -1f,1f,1f, -1f,-1f,1f, 1f,-1f,1f, 1f,1f,1f, //前面 1f,1f,1f, 1f,-1f,1f, 1f,-1f,-1f, 1f,1f,-1f, //右面 1f,1f,-1f, 1f,-1f,-1f, -1f,-1f,-1f, -1f,1f,-1f, //后面 -1f,1f,-1f, -1f,-1f,-1f, -1f,-1f,1f, -1f,1f,1f, //左面 -1f,1f,-1f, -1f,1f,1f, 1f,1f,1f, 1f,1f,-1f, //上面 -1f,-1f,1f, -1f,-1f,-1f, 1f,-1f,-1f, 1f,-1f,1f, //下面 }; //顶点颜色,R,G,B,A private float[] colors={ 0.2f,0f,0.7f,1f, 0f,0.4f,0.3f,1f, 0.8f,0.1f,0.1f,1f, 1f,1f,1f,1f, //前面 0f,1f,0f,1f, 0f,1f,0f,1f, 0f,1f,0f,1f, 0f,1f,0f,1f, //后面 0f,0f,1f,1f, 0f,0f,1f,1f, 0f,0f,1f,1f, 0f,0f,1f,1f, //左面 0.3f,0.5f,1f,1f, 0.3f,0.5f,1f,1f, 0.3f,0.5f,1f,1f, 0.3f,0.5f,1f,1f, //上面 0f,0.2f,0.3f,1f, 0f,0.4f,0.3f,1f, 0f,0.3f,0.3f,1f, 0f,0.4f,0.3f,1f, //下面 }; private short[] indices={ 0,1,2, 0,2,3, 4,5,6, 4,6,7, 8,9,10, 8,10,11, 12,13,14, 12,14,15, 16,17,18, 16,18,19, 20,21,22, 20,22,23, }; private FloatBuffer mVertexBuffer,mColorBuffer; private ShortBuffer mIndixBuffer; private float rx=45.0f; public OpenGlRender2(Bitmap bitmap){ ByteBuffer vbb=ByteBuffer.allocateDirect(vertices.length*4); vbb.order(ByteOrder.nativeOrder()); mVertexBuffer=vbb.asFloatBuffer(); mVertexBuffer.put(vertices); mVertexBuffer.position(0); ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length*2); ibb.order(ByteOrder.nativeOrder()); mIndixBuffer=ibb.asShortBuffer(); mIndixBuffer.put(indices); mIndixBuffer.position(0); ByteBuffer cbb=ByteBuffer.allocateDirect(colors.length*4); cbb.order(ByteOrder.nativeOrder()); mColorBuffer=cbb.asFloatBuffer(); mColorBuffer.put(colors); mColorBuffer.position(0); } public void onDrawFrame(GL10 gl) { gl.glClearColor(0f, 0f, 0f, 0.5f); gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glFrontFace(GL10.GL_CCW); gl.glEnable(GL10.GL_CULL_FACE); gl.glCullFace(GL10.GL_BACK); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glVertexPointer(3,GL10.GL_FLOAT, 0, mVertexBuffer); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); gl.glColorPointer(4, GL10.GL_FLOAT, 0, mColorBuffer); gl.glLoadIdentity(); // gl.glColor4f(1f, 0f, 0f, 1f); gl.glTranslatef(0f, 0f, -5f); gl.glRotatef(-45f, 0f, 1f, 0f); gl.glRotatef(rx, 1f, 0f, 0f); gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_SHORT, mIndixBuffer); gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); gl.glDisable(GL10.GL_CULL_FACE); rx++; } public void onSurfaceChanged(GL10 gl, int width, int height) { gl.glViewport(0, 0, width, height); gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); GLU.gluPerspective(gl, 45.0f, (float)width/(float)height, 0.1f, 100.f); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); } public void onSurfaceCreated(GL10 gl, EGLConfig config) { //启用深度测试 gl.glEnable(GL10.GL_DEPTH_TEST); // 所做深度测试的类型 gl.glDepthFunc(GL10.GL_DITHER); //黑色背景 gl.glClearColor(1f, 0f, 0f, 1f); //启用阴影平滑 gl.glShadeModel(GL10.GL_SMOOTH); //清除深度缓存 gl.glClearDepthf(1.0f); }
-
中国传统婚礼要准备的东西.doc
2021-01-18 14:19:14双喜字由两个“喜”字组成,代表喜事加倍,不同一般的高兴和喜庆,也表示给新人带来好运气和幸福生活。 中式婚礼服装 传统的中国婚礼上,新娘子要穿非常漂亮的汉服,汉服是具有中国特色的传统服装,具有历史意义和... -
Java23种设计模式(六)装饰者设计模式
2020-09-07 09:43:05甚至称他们两为相同的模式,但是仔细理解以后,他们两个还是有着本质上的区别,首先他们作用方面都是一样的,都是将同一个东西的两个共同组成属性抽出来,然后需要新添加东西的时候只需添加对应的属性就行了。...这是本人学习Java23种设计模式期间总结和记录,主要听的课程是尚硅谷——韩顺平老师的课程,这个课程可谓是非常详细了,推荐学习。
这里讲到装饰者设计模式,装饰者模式在新手初学的时候总觉得和桥接模式没啥区别,甚至称他们两为相同的模式,但是仔细理解以后,他们两个还是有着本质上的区别,首先他们作用方面都是一样的,都是将同一个东西的两个共同组成属性抽出来,然后需要新添加东西的时候只需添加对应的属性就行了。
但是桥接模式对应的是很严格的匹对,某样东西对应某种属性,某些东西对应不了这个属性。就用手机举例,很多手机品牌都对应着刘海屏的属性,很多品牌都对应的是安卓系统;但是,苹果手机虽然对应着刘海屏,但是对应的不是安卓系统,其他手机品牌也无法对应苹果系统,这样就不是桥接模式了,因为你不能随心所欲的添加属性或者其他,因为有着严格对应关系,而装饰者模式就不一样了,比如一份饮料,他什么配料都可以加,可以加珍珠,可以加糖,不论你是添加配料也好,还是添加新的饮料也好,他们都可以组合在一起,装视起来。举例咖啡饮品,代码如下:
@Setter @Getter public abstract class Drink {//不论是咖啡还是配料,最终搭配好的都是饮品 public String des; private float price; public abstract float cost(); } public class Coffee extends Drink {//咖啡的对象 @Override public float cost() { return super.getPrice(); } } public class Decorator extends Drink{//配料的对象 private Drink drink; public Decorator(Drink drink) { this.drink = drink; } @Override public float cost() { return this.getPrice() + drink.cost(); } @Override public String getDes(){ return this.des + " " + this.getPrice() + "&&" + drink.getDes(); } } public class Latte extends Coffee{//具体的拿铁咖啡 public Latte() { setDes("拿铁咖啡"); setPrice(20.0f); } } public class Milk extends Decorator {//具体的牛奶配料 public Milk(Drink drink) { super(drink); setDes("加牛奶"); setPrice(2.0f); } } public class CoffeeBar {//制作咖啡,将任意的配料加入咖啡 public static void main(String[] args) { Drink latte = new Latte(); System.out.println(latte.getDes() + latte.getPrice()); latte = new Milk(latte); System.out.println(latte.getDes() + latte.cost()); latte = new Milk(latte); System.out.println(latte.getDes() + latte.cost() ); } }
装饰者模式定义
- 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
- 这里提到的动态的将新功能附加到对象和 ocp 原则,在后面的应用实例上会以代码的形式体现,请同学们注意体会。
装饰设计模式咖啡例子示意图:
-
C++中的4种类型转换方式
2021-01-20 03:43:04把一个指向基类对象的指针转换成指向一个派生类对象的指针,这两种转换之间的差别是巨大的,但是传统的c语言风格的类型转换没有区分这些。还有一个缺点是,c风格的转换不容易查找,他由一个括号加上一个标识符组成,... -
四种类型转换:static_cast,const_cast,reinterpret_cast,dynamic_cast
2020-11-12 09:04:35可以在任意类型之间转换,不能进行错误检查,容易出错(比如你可以把一个指向const对象的指针转换成指向非const对象的指针,把一个指向基类对象的指针转换成指向一个派生类对象的指针,这两种转换之间的差别是巨大的...一、为什么C++中不使用C中的强制类型转换而增加这四种类型转换?
- C中的强制类型转换看似很强大,可以在任意类型之间转换,不能进行错误检查,容易出错(比如你可以把一个指向const对象的指针转换成指向非const对象的指针,把一个指向基类对象的指针转换成指向一个派生类对象的指针,这两种转换之间的差别是巨大的)。
- C风格的转换不容易查找,它由一个括号加上一个标识符组成,这样的东西在C++程序里一大堆。
所以C++为了克服这些缺点,引进了四种新的类型转换操作符。
二、四种类型转换的使用场合
1.静态类型转换:static_cast <目标类型> (标识符)
(1)什么时候使用static_cast?
- 任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。-
- 可以完成C++中基础数据类型转换。
- 在一个方向上可以作隐式转换,在另外一个方向上就可以作静态转换。
注:顶层const:表示指针本身是个常量。如:int *const p;
底层const:表示指针所指的对象是一个常量。如:int const *p;
C++中的基础数据类型有:整型,浮点型,字符型,布尔型(2)何时发生隐式类型转换?
- 在大多数表达式中,比int类型小的整型值首先提升为较大的整数类型。
- 在条件中,非布尔值转换为布尔类型。
- 初始化过程中,初始值转换成变量的类型:在赋值语句中,右侧运算对象转换成左侧运算对象的类型。
- 如果算术运算或关系运算的运算对象有多种类型,需要转换成同一种类型。
- 函数调用时也会发生类型转换。
(3)主要用法:
(1)用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。
进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
(2)用于基本数据类型之间的转换,如把int转换成char,这种转换的安全也要开发人员来保证.。
(3)把void指针转换成目标类型的指针(不安全)。
(4)把任何类型的表达式转换成void类型。2.常类型转换:const_cast <目标类型> (标识符)
(1)何时使用const_cast?
- 用来移除对象的常量性,即去除const,但只能改变运算对象的底层const。
注:只有const_cast能改变表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量属性都将引发编译器错误。
- 目的:使用const_cast去除const限定的目的不是为了修改它的内容,通常是为了函数能够接受这个实际参数。
注意:const_cast只能用于指针或引用,不能用于变量。
(2)脱掉const后的引用或指针可以改吗?
可以改变const自定义类的成员变量,但是对于内置数据类型,却表现未定义行为。
总之,不要试图去改变const修饰的对象!3.重解释类型转换:reinterpret_cast <目标类型> (标识符)
(1)何时使用reinterpret_cast?
- 通常为操作数的位模式提供较低层的重新解释,也就是说将数据以二进制存在形式的重新解释,在双方向上都不可以隐式类型转换的,则需要重解释类型转换。
- 可以用于任意类型的指针之间的转换,对转换的结果不做任何保证,尽量少用。
4.动态类型转换:dynamic_cast <目标类型> (标识符)
(1)何时使用dynamic_cast?
与以上三种类型在编译时就进行类型转换不同,此类型转换发生在程序运行期间。
- 用于多态中的父子类之间的强制转化。
- 用于将基类的指针或引用安全地转换成派生类的指针或引用。
- 只能用于有虚函数的类。(为什么?)
(2)为什么需要有虚函数 dynamic_cast和static_cast在类层次间转换的区别
因为该种类型转换发生在运行时,运行时需要知道继承关系的话则只有虚函数表才能提供,通过虚函数表可以知道该类对象的父类,从而进行父子类之间的转换。
注:如果一条dynamic_cast语句的转换目标是指针类型并且失败了,则结果为0;如果转换目标是引用类型并且失败了,则dynamic_cast运算符将抛出一个bad_cast异常。
(3)dynamic_cast和static_cast在类层次间转换的区别
在类的转换时,在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
2020.11.12
-
来自一个新手自学C#对“自定义事件”的理解
2020-10-13 14:23:13偶然间,在公司调试机器看代码时,发现自动动作代码中定义了许多自定义的事件,这才猛然醒悟,事件这东西原来是这么的重要;于是,这两天就在研究自定义事件是如何使用的,现在把这两天研究到的结果记录下来。 学习...前言
偶然间,在公司调试机器看代码时,发现自动动作代码中定义了许多自定义的事件,这才猛然醒悟,事件这东西原来是这么的重要;于是,这两天就在研究自定义事件是如何使用的,现在把这两天研究到的结果记录下来。
学习事件,我主要是通过B站刘铁猛老师的视频和网上的博客进行学习,其中,下面所展示的代码都是在刘铁猛老师的课堂中敲打的。
事件的组成部分
- 事件拥有者;
- 事件;
- 事件响应者;
- 事件处理器;
- 事件订阅.
事件是什么?
参考百度:
事件是可以被控件识别的操作,如按下确定按钮,选择某个单选按钮或者复选框。每一种控件有自己可以识别的事件,如窗体的加载、单击、双击等事件,编辑框(文本框)的文本改变事件,等等。
触发事件的对象称为事件发送者;接收事件的对象称为事件接收者。
C#中事件机制的工作过程如下:
(1)将实际应用中需通过事件机制解决的问题对象注册到相应的事件处理程序上,表示今后当该对象的状态发生变化时,该对象有权使用它注册的事件处理程序。
(2)当事件发生时,触发事件的对象就会调用该对象所有已注册的事件处理程序。
自定义事件
对于自定义事件,本人还是很懵懂的,对该知识点的认值并不是很熟悉,下面就写下本人定义自定义事件的全过程。
以一个例子展开:
假如你去饭店吃饭,坐下凳子后,你就要开始想你要吃什么,想了五秒钟,想好了,你就得叫服务员给你下单,这时候服务员就来订阅你点菜的这个事件了,当你离开时还得付账。-
首先定义用于传递事件消息的类,也叫事件参数
事件参数一般我们命名习惯都要以
EventArgs
作为结尾。DishName 你点的菜名
Size 分量大小// 用于传递事件消息(事件参数) public class OrderEventAgrs : EventArgs { public string DishName { get; set; } public string Size { get; set; } }
注意:这里需要继承自EventArgs,不用管他为什么要继承EventArgs,就把它当作一种写代码的习惯就好。
-
定义事件拥有者类
刚开始可以先定义一个空类public class Customer { }
-
声明一个定义事件的委托
注意:如果该委托仅仅只是用来定义事件的,那么一般都要以
EventHandler
结尾。public delegate void OrderEventHandler(Customer customer, OrderEventAgrs e); // 参数一:拥有事件的类(事件源) 参数二:传递事件消息的类(事件参数)
此委托用于定义事件与在时间内部订阅方法
-
可以在事件拥有者类中定义事件了
事件命名也得要以
EventHandler
结尾/* 一、事件拥有者 */ public class Customer { // 声明一个委托类型字段 private OrderEventHandler orderEventHandler; /* 二、事件 */ public event OrderEventHandler Order { // 定义事件 // 事件内部在使用委托取订阅方法 add { this.orderEventHandler += value; } remove { this.orderEventHandler -= value; } } //public event OrderEventHandler Order; // 简单定义事件的方法 }
可以看到,先定义了一个委托类型的字段,然后在定义事件的内部中添加add和remove方法,分别用于委托订阅方法和删减方法,间接就是事件订阅方法了。
这点很重要,这是区别 - 简单定义事件 - 的门栏。
也顺便把下面的方法都加到Customer类中吧:
public double Bill { get; set; } public void PayTheBill() { Console.WriteLine("I will pay ${0}", this.Bill); } public void Walkln() { Console.WriteLine("Walk into the restaurant."); } public void SitDown() { Console.WriteLine("Sit down"); }
-
定义 被事件订阅的方法类
// 被事件订阅的方法类 public class Waiter { /* 四、事件处理器 */ public void Action(Customer customer, OrderEventAgrs e) { Console.WriteLine("I will serve your the dish - {0}.", e.DishName); double price = 10; // 假设所有东西都十元钱 switch (e.Size) { case "small": // 小份价格是原价*0.5 price = price * 0.5; break; case "large": // 大份价格是原价*1.5 price = price * 1.5; break; default: price = 0; break; } // 统计价格 customer.Bill += price; } }
该类中的Action方法,是用来被事件订阅的,也叫事件处理器。
也就是当事件促发时,所要处理的事情。注意函数的参数和返回值类型必须要和委托定义的一样。
当然也可以如下图,使用系统自动成成函数的方法,自动生成,这样比较保险。
-
接着在Customer类中添加 促发事件的方法
// 促发事件 public void Think() { for (int i = 0; i < 5; i++) { Console.WriteLine("Let me think..."); Thread.Sleep(1000); } if (this.orderEventHandler != null) { OrderEventAgrs e = new OrderEventAgrs(); e.DishName = "egg"; // 鸡蛋 e.Size = "large"; // 大份 this.orderEventHandler.Invoke(this, e); // 促发 } } public void Action() { Console.ReadLine(); this.Walkln(); this.SitDown(); this.Think(); }
需要特别注意,得判断委托是否为空,才进行下面的操作,避免不必要的报错。
-
在main函数中进行事件的订阅和应用
static void Main(string[] args) { Customer customer = new Customer(); /* 三、事件响应者 */ Waiter waiter = new Waiter(); /* 五、事件订阅 */ customer.Order += waiter.Action; customer.Action(); // 执行事件响应后的方法 customer.PayTheBill(); // 付钱 Console.ReadKey(); }
到了这里,代码也就写完了,可以运行看看效果:
效果也就是这样了。
自定义事件也就码完了,下面是全部代码:using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; namespace 复习自定义事件 { class Program { static void Main(string[] args) { Customer customer = new Customer(); /* 三、事件响应者 */ Waiter waiter = new Waiter(); /* 五、事件订阅 */ customer.Order += waiter.Action; customer.Action(); customer.PayTheBill(); Console.ReadKey(); } } // 用于传递事件消息(事件参数) public class OrderEventAgrs : EventArgs { public string DishName { get; set; } public string Size { get; set; } } // 声明一个定义事件的委托 public delegate void OrderEventHandler(Customer customer, OrderEventAgrs e); // 参数一:拥有事件的类(事件源) 参数二:传递事件消息的类(事件参数) /* 一、事件拥有者 */ public class Customer { // 声明一个委托类型字段 private OrderEventHandler orderEventHandler; /* 二、事件 */ public event OrderEventHandler Order { // 事件内部在使用委托取订阅方法 add { this.orderEventHandler += value; } remove { this.orderEventHandler -= value; } } //public event OrderEventHandler Order; // 简单定义事件的方法 public double Bill { get; set; } public void PayTheBill() { Console.WriteLine("I will pay ${0}", this.Bill); } public void Walkln() { Console.WriteLine("Walk into the restaurant."); } public void SitDown() { Console.WriteLine("Sit down"); } // 促发事件 public void Think() { for (int i = 0; i < 5; i++) { Console.WriteLine("Let me think..."); Thread.Sleep(1000); } if (this.orderEventHandler != null) { OrderEventAgrs e = new OrderEventAgrs(); e.DishName = "egg"; // 鸡蛋 e.Size = "large"; // 大份 this.orderEventHandler.Invoke(this, e); // 促发 } } public void Action() { Console.ReadLine(); this.Walkln(); this.SitDown(); this.Think(); } } // 被事件订阅的方法类 public class Waiter { /* 四、事件处理器 */ public void Action(Customer customer, OrderEventAgrs e) { Console.WriteLine("I will serve your the dish - {0}.", e.DishName); double price = 10; switch (e.Size) { case "small": price = price * 0.5; break; case "large": price = price * 1.5; break; default: price = 0; break; } customer.Bill += price; } } }
下面是一些系统自带的事件代码积累,有兴趣的可以看看。
系统自带事件
Timer事件
例子一:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Timers; namespace 例子一 { class Program { static void Main(string[] args) { // 1. 事件拥有者 Timer timer = new Timer(); timer.Interval = 1000; // 3. 事件的响应者 Box box = new Box(); Girl girl = new Girl(); // 2. 事件 5. 事件订阅 timer.Elapsed += box.Action; timer.Elapsed += girl.Action; timer.Start(); Console.ReadKey(); } } class Box { static int time = 0; // 4. 事件的处理器 internal void Action(object sender, ElapsedEventArgs e) { Console.WriteLine("第" + time + "盒子"); time += 2; } } class Girl { static int time = 1; internal void Action(object sender, ElapsedEventArgs e) { Console.WriteLine("第" + time + "女孩"); time += 2; } } }
他会无限的一直这样输出下去,直到你按下任意键。
事件的拥有者和事件的响应者是分开的不同的两个类
例子二:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; // 事件的拥有者和事件的响应者是分开的不同的两个类 namespace 例子二 { class Program { static void Main(string[] args) { // 1. 事件拥有者 Form form = new Form(); // 3. 事件响应者 Controls controls = new Controls(form); form.ShowDialog(); } class Controls { private Form form; public Controls(Form _form) { if (_form != null) { this.form = _form; // 2. 事件 5. 事件订阅 this.form.Click += this.FormClicked; // 窗体的点击事件 } } // 4. 事件处理器 private void FormClicked(object sender, EventArgs e) { this.form.Text = DateTime.Now.ToString(); // 获取当前系统事件更新窗体标题 } } } }
当鼠标点击窗体里的任意地方都会更新标题
事件的拥有者同时也是事件的响应者
例子三:using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; // 事件的拥有者同时也是事件的响应者 namespace 例子三 { class Program { static void Main(string[] args) { // 1. 事件拥有者 3. 事件响应者 MyForm myForm = new MyForm(); // 2. 事件 5. 事件订阅 myForm.Click += myForm.FormClicked; myForm.ShowDialog(); } } class MyForm : Form { // 4. 事件处理器 internal void FormClicked(object sender, EventArgs e) { this.Text = DateTime.Now.ToString(); } } }
还是一样,鼠标点击窗体后更新标题时间。自定义类继承自系统的类,就可以添加自己的方法去实现了。
事件响应者也是事件拥有者
例子四:using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace 例子四 { class Program { static void Main(string[] args) { // 3. 事件响应者 MyForm myForm = new MyForm(); myForm.ShowDialog(); } } class MyForm : Form { private TextBox textBox; private Button button; public MyForm() { textBox = new TextBox(); // 1. 事件拥有者 button = new Button(); this.Controls.Add(textBox); this.Controls.Add(button); // 2. 事件 5. 事件订阅 button.Click += this.ButtonClicked; this.button.Text = "Say Hello"; this.button.Top = 30; } // 4. 事件处理器 private void ButtonClicked(object sender, EventArgs e) { this.textBox.Text = "Hello Wrold"; } } }
总结:
事件本人懂的也就这些了,具体事件有什么用,我还真是不知道,既然有这样一个东西,肯定有他的用处的;日后对事件有更进一步的理解后再过来更新更新这篇博客吧。 -
新版Android开发教程.rar
2010-12-14 15:49:11Android Android Android Android 手机新概念 操作系统的选择 -------- 定制和长尾 � 重构 � MVC 和 Web APP 架构 Android Android Android Android 开发背景 � 计算技术、无线接入技术的发展,使嵌入式系统逐渐... -
最好的asp CMS系统科讯CMSV7.0全功能SQL商业版,KesionCMS V7.0最新商业全能版-免费下载
2013-03-09 22:57:2234、两种编辑器选择,可视化编辑器,类似word的所件即所得的在线内容编辑功能,支持表格、图片、FLASH、多种格式播放器插入,支持图片和附件上传。 35、字符过滤,自动过滤敏感字符,避免损害网站形象、避免网站... -
本周总结
2019-08-02 14:02:50本周主要写了上传和下载这两个东西,然后熟悉了一种新的结构。 这个项目的结构也是我之前没有见过的,它是mapper,provider,respository一起组成了我之前熟悉的结构dao和mapper sql语句要么在mapper中使用注解的... -
固态和机械硬盘组raid_还在纠结机械硬盘和固态硬盘选哪个?不妨试试混合固态硬盘...
2020-12-12 11:52:03一年一度的618来了,又到了各位装机小能手们选新装备的时候,目前正在选购硬盘的小...电脑的储存设备是由价格更高、速度更快的固态硬盘和容量更大,读写更慢的机械硬盘这截然相反的两种选择组成,所以很多装机新手们... -
使用c#开发mapserver之layerObj
2009-11-22 20:01:00layerObj就是mapserver中的图层对象,...layerObj的创建有两种方法,一种是从mapObj对象中获取,是由mapfile中配置的Layer标签中的内容组成,在mapserver打开mapfile时自动生成的。还有一种方法就是创建一个新的layerO -
POJ 3254-Corn Fields【基础状压DP】
2019-09-20 16:52:47农夫John已经购买组成一个新茂盛矩形牧场中号由Ñ(1≤ m≤12; 1≤ñ≤12)平方包裹。他想在一些广场上为奶牛种一些美味的玉米。遗憾的是,一些广场是不育的,不能种植。Canny FJ知道奶牛不喜欢彼此吃东西,所以当... -
IOS autolayout自动布局实例(swift)
2015-08-18 17:35:10对于一个新手来说,刚接触自动布局这东西,会被一种叫约束的东西搞得晕头转向,不是少添加了,就是多添加了,造成约束冲突,不过弄明白之后,也就那回事了。 首先先上个最终的效果图上来,一步步做出这个效果,如果... -
java面向对象编程.pptx
2020-03-08 14:28:09本章内容面向对象的思想对象和类类的定义对象的创建与销毁方法的重载构造函数对象的初始化设计模式之单例模式面向对象的思想面向对象Object Oriented指世界是由对象(Object )所组成的每个对象主要具有两个特征 1行为... -
objective-c自学总结(三)---面向对象的封装,继承与多态
2015-05-22 12:28:00然后以新的完整形式呈现出来 例如,两种或多种化学药品组成一个胶囊 将方法和属性一起包装到一个单元中,单元以类的形式实现 对象同时具有属性和方法两项特性 对象的属性和方法通常被封装在一起,共同体现事物的... -
我的-源码
2021-02-18 06:08:36地雷和合成物现在是两种不同的东西。 在1.0版中,每个地雷都有自己的组成部分,您可以在其中定义用于重置地雷的材料。 现在,地雷和组成物是两回事。 地雷基本上控制着它的几何形状,重置时间,如何执行重置,全息... -
jvm内存使用率计算方式_计算机内存的工作方式究竟是什么样的?
2021-01-10 23:33:46在许多方面来说,记忆决定了我们是什么样的人,让我们不忘往事。学习并记住新本领以及为未来做规划,像计算机常常...每个二进制数被存放于存储元件中,在两种可能值间自如转换,0和1,由数百万计的二进制数组成的... -
反射内存 延时_计算机内存的工作方式究竟是什么样的?
2020-12-22 02:15:19在许多方面来说,记忆决定了我们是什么样的人,让我们不忘往事。学习并记住新本领以及为未来做规划,像计算机常常...每个二进制数被存放于存储元件中,在两种可能值间自如转换,0和1,由数百万计的二进制数组成的... -
ROS学习日记2
2019-12-24 20:51:29解析:ament是一种元编译系统,用来构建组成应用程序的多个独立功能包,它并不是一个全新的东西,而是catkin编译系统进一步演化的版本,这两个单词也是近义词。ament主要分为两个部分: [1]编译系统:配置、编译、... -
动态添加点击事件_动态面板使用说明及案例详解
2021-01-12 21:41:07什么是动态面板动态面板是Axure的高级交互元件,由不同的状态面板组成,是我们制作交互过程中运用频率最高的元件,很多交互效果需要...添加动态面板在Axure中我们有两种方法添加动态面板。方法一:从Default元件库... -
xcode动态改变窗口大小_动态面板使用说明及案例详解
2020-12-08 02:15:15什么是动态面板动态面板是Axure的高级交互元件,由不同的状态面板组成,是我们制作交互过程中运用频率最高的元件,很多交互效果需要...添加动态面板在Axure中我们有两种方法添加动态面板。方法一:从Default元件库... -
sklearn之样本生成
2018-01-30 10:27:50因此,这里有两个东西是我们比较关心的,一个是模型,另外一个样本数据。现在很多文章都在讨论各种算法,大都可以归为模型的建立过程,今天,本博文讲另外一个重要组成部分,样本数据获取方法(以sklearn为工具)。 ... -
结对编程心得
2016-03-22 11:12:00结对编程是我第一次听说到的一个编程方式,结对编程就是两个人组成一个小队伍进行编程,是一种新的编程思想。结对编程的优点有以下这几点第一点,就是思考问题的角度,一个人思考问题肯定没有两个人思考问题来的明白... -
玩游戏中体会到的平衡
2008-05-22 22:56:00游戏是消除屏幕上横平坚直堆着的许多小球,它们由五种颜色组成,每两个相同颜色在一起就可以消掉,相同颜色的越多消除时得分越高,每消完一列后就会从右边加入新的一列。游戏设计得并不复杂,但是最... -
sklearn之样本生成(1)
2016-12-14 17:47:28因此,这里有两个东西是我们比较关心的,一个是模型,另外一个样本数据。现在很多文章都在讨论各种算法,大都可以归为模型的建立过程,今天,本博文讲另外一个重要组成部分,样本数据获取方法(以sklearn为工具)。 ... -
党员会学习的文章内容.doc
2021-01-18 15:07:50这同其他事物一样,是矛盾的结构,是矛盾的统一体,即两个相反的东西结合在一起,成为一个新的东西而存在。自然科学告诉我们,世界上每一物体,都是这样结合成的。世界上已经发现有九十四种元素,每种元素的原子都... -
前端商城系统
2016-09-29 14:19:00H5有很多新的API,这些API可以让前端做一些以前只能在后台完成的东西。本文利用webstorage,用前端做了一个商城系统。除了购买扣款这个环节外,其他的功能都有。...这些页面间的传值主要有两种情况: 1.跳转... -
Unix文件与目录简介
2016-04-15 13:44:52Unix文件系统是目录和文件的一种层次结构,所有东西的起点是称为根的目录,此目录的名称是一个字符"/" 文件名 创建新目录时会自动创建了两个文件名:.(点)和..(点点),点指向当前目录,点点指向父目录 路径名 由...
-
Recipe_Project:在此项目中,我们可以搜索配方中的给定成分-源码
-
SocketTool.zip
-
王梓航:3.2比特币能否突破50000大关重回巅峰
-
Leafage 诞生记(一)
-
一步一步搭建Flutter开发架子-国际化,路由,本地化,响应式
-
【爱码农】C#制作MDI文本编辑器
-
C语言零基础入门(详细讲解)
-
「纪念碑谷」你不知道的 8 个故事
-
关于comsol技术剖析的超强干货
-
插入排序实验室直流网络职业生涯040119-源码
-
项目经理成长之路
-
使用vue搭建微信H5公众号项目
-
戴尔5370 8250u UHD620 clover引导EFI.zip
-
mysql 索引 原理和知识点
-
day7-事务隔离以及事务锁
-
自动化测试Python3+Selenium3+Unittest
-
教示灯:另一个系统-源码
-
FT232驱动.zip
-
fatal: Cannot get https://gerrit.googlesource.com/git-repo/clone.bundle
-
祝水水生日快乐.rar