效果图:
[img]
[/img]
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/textviewll" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="#000000" android:textSize="18dip" android:background="#00FF00" android:text="链接手机/网页/邮件" android:gravity="center_vertical|center_horizontal" /> <TextView android:id="@+id/textView0" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="#FF0000" android:textSize="18dip" android:background="#FFFFFF" android:text="拨打手机:13888888888" android:gravity="center_vertical|center_horizontal" android:autoLink="phone" /> <TextView android:id="@+id/textView1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="#FF0000" android:textSize="18dip" android:background="#00FF00" android:text="雨松MOMO的博客:http://blog.csdn.net/xys289187120" android:gravity="center_vertical|center_horizontal" android:autoLink="web" /> <TextView android:id="@+id/textView2" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="#FF0000" android:textSize="18dip" android:background="#FFFF00" android:text="发送邮件:xuanyusong@gmail.com" android:gravity="center_vertical|center_horizontal" android:autoLink="email" /> </LinearLayout>
[img]
[/img]
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/textviewll" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/TextView01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="在图片下方" android:textColor="#FF0000" android:drawableBottom="@drawable/jay" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" > </TextView> <TextView android:id="@+id/TextView01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="在图片上方" android:textColor="#FF0000" android:drawableTop="@drawable/jay" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" > </TextView> <TextView android:id="@+id/TextView01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="在图片左边" android:textColor="#FF0000" android:drawableLeft="@drawable/jay" android:layout_alignParentLeft="true" android:layout_centerVertical="true" > </TextView> <TextView android:id="@+id/TextView01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="在图片右边" android:textColor="#FF0000" android:drawableRight="@drawable/jay" android:layout_alignParentRight="true" android:layout_centerVertical="true" > </TextView> </RelativeLayout >
[img]
[/img]
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="#000000" android:textSize="18dip" android:background="#00FF00" android:text="文本内容" android:gravity="center_vertical|center_horizontal" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="18dip" android:background="#FFFFFF" android:textColor="#FF0000" android:text="设置字符串显示为*" android:gravity="center_vertical|center_horizontal" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="18dip" android:background="#FFFFFF" android:textColor="#FF0000" android:text="设置字符串显示为*" android:password="true" android:gravity="center_vertical|center_horizontal" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="18dip" android:background="#FF0000" android:textColor="#FFFFFF" android:text="设置字符串阴影颜色" android:shadowColor="#000000" android:shadowRadius="3.0" android:gravity="center_vertical|center_horizontal" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="18dip" android:background="#FFFFFF" android:textColor="#FF0000" android:singleLine="true" android:text="只显示一行字符串超出屏幕为'...'dsfusdiofjdsiofjsdiofjoisdjfiosdjfoisdjfoisdf" android:gravity="center_vertical|center_horizontal" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="18dip" android:background="#FF0000" android:textColor="#FFFFFF" android:text="设置显示文字的间隔为0.5" android:textScaleX="0.5" android:gravity="center_vertical|center_horizontal" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="18dip" android:background="#FF0000" android:textColor="#FFFFFF" android:text="设置显示文字的间隔为2.0" android:textScaleX="2.0" android:gravity="center_vertical|center_horizontal" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="18dip" android:background="#FFFFFF" android:textColor="#FF0000" android:text="设置文字外形为 bold" android:text android:gravity="center_vertical|center_horizontal" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="18dip" android:background="#FFFFFF" android:textColor="#FF0000" android:text="设置文字外形为 normal" android:text android:gravity="center_vertical|center_horizontal" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="18dip" android:background="#FFFFFF" android:textColor="#FF0000" android:text="设置文字外形为 italic" android:text android:gravity="center_vertical|center_horizontal" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="#FFFFFF" android:textColor="#FF0000" android:text="设置文字大小 为10" android:textSize="10dip" android:gravity="center_vertical|center_horizontal" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="#FFFFFF" android:textColor="#FF0000" android:text="设置文字大小 为15" android:textSize="15dip" android:gravity="center_vertical|center_horizontal" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="#FFFFFF" android:textColor="#FF0000" android:text="设置文字大小 为20" android:textSize="20dip" android:gravity="center_vertical|center_horizontal" /> <TextView android:layout_width="200px" android:layout_height="wrap_content" android:textSize="18dip" android:ellipsize="marquee" android:focusable="true" android:marqueeRepeatLimit="marquee_forever" android:focusableInTouchMode="true" android:scrollHorizontally="true" android:text="文字滚屏文字跑马灯效果加长加长加长加长加长加长加长加长加长加长加长加长" android:background="#FF0000" android:textColor="#FFFFFF" /> </LinearLayout>
Technote 2230提出了很多用OpenGL ES来提升iphone程序性能的建议。我们现在远远不能深刻理解OpenGL ES所以你需要学习以下内容。不信?是真的,试试看,我等着你的读后感。
好,就这样定了?副标题为“优化顶点数据”,这里有一些算法上的建议用来"submit strip-ordered indexed triangles with per vertex data interleaved"。当苹果给出这些建议的时候,他们通常有一些非常好的理由,让我们看看如何使用它。
首先让我们了解它的意思。让我们把这句话分解开来:
Strip Ordered:换句话说,如果你的模型有相邻的三角形,组成一个三角形带提交而不是分别提交每一个三角形。我们早期教程讲到过三角形带,你应该多少知道一点该怎么做。对于大多数物体来说你都可以使用这个方法,但不是所有时候都要用它。你什么时候能够使用?当你确定使用三角形带可以有效减少推入OpenGL ES每帧的顶点数据的时候。
indexed:这里仍然没有什么新内容。我们使用顶点索引有一段时间了。我们仅仅用12个顶点来创建旋转的20面体。glDrawElements()是基于索引绘制而不是基于顶点。
见鬼,我们目前已经做的很棒了,不是吗?我们眼下一直在做着正确的事情。让我们来看最后一个建议,然而:
with per vertex data interleaved:好的,恩。。。。它到底是什么意思?
好的,是时候考验你的记忆力了。你还记得在过去几个部分,也就是当我们讨论glVertexPointer(),glNormalPointer(),glColorPointer(),glTexCoordPointer()的时候吗?在前面的部分里,我告诉你不用关注参数stride并把它设置为0。
但是现在你要关注它(stride)了,因为它是用来交叉存取每个顶点数据的关键。
你也许想知道“per vertex data”是什么,并且想知道如何交叉存取它。
你肯定知道,在OpenGL ES里我们用顶点数组来表示几何图形,每一个数组包含3个定义顶点的GLfloat变量,来创造我们的物体。除了这些,我们有时也使用其它数据。举例来说,如果我们用光效就需要顶点法线,我们不得不在法线数组里定义每一个顶点的法线。如果我们使用纹理坐标,我们不得不在纹理数组里定义每一个顶点的纹理坐标。如果我们用颜色数组,我们不得不指定每个点的颜色,你是否注意到我总是强调“per vertex data”?这些数据类型就是苹果公司在它们技术说明里提到的"per vertex data"。这就是你所有在OpenGL ES 中放入数组的任意一种适用于顶点的顶点数组。
这个系列到目前为止,我们已经创造一个数组来存放顶点数据,并把它们与法线数据、颜色数据、纹理坐标分开存在其他独立数组中,像这样:
我们将要学习如何把这所有的数据放在一起作为一个整体数据来存储:
如果你读不懂上图解里面的代码请不要担心。 当那个变得重要的时候,我们会再次列出来讲解的,这个给出的代码列表仅仅是举例说明在一个独立的内存单元中我们可以存入所有的顶点数据。 我们所要做的只是把所有描述一个单一点的数据放在内存的同一个地方。
这样做能够使OpenGL快速的获得读取到每个顶点的信息。 在今天的例子里面,我们要将交叉存储顶点,法线,颜色(vertices, normals, color data),同样的方法可以使用在纹理坐标中,或者仅交叉存储顶点和法线。事实上在一个Xcode工程里面,会附带着一些数据结构定义这三个交叉存储的情况。
为了让这个能够工作起来,我们需要一个新的数据结构。下面的数据结构能够让我们把上面提到的顶点, 法线,颜色 交叉存储到一起,
Vertex3D vertex;
Vector3D normal;
Color3D color;
} ColoredVertexData3D;
啊哈哈,漂亮而简洁不是吗?你只用一个数据结构就包含了我们需要的一个顶点的所有属性。
下一步,当然了,线面我们需要填充顶点数据,所以我们需要把三个static const数组合并成一个。这里是相同的二十面体数据,指定使用了我们自己定义的新数据类型:
static const ColoredVertexData3D vertexData[] = { { {0, -0.525731, 0.850651}, // Vertex | {0.000000, -0.417775, 0.675974}, // Normal | Vertex 0 {1.0, 0.0, 0.0, 1.0} // Color | }, { {0.850651, 0, 0.525731}, // Vertex | {0.675973, 0.000000, 0.417775}, // Normal | Vertex 1 {1.0, 0.5, 0.0, 1.0} // Color | }, { {0.850651, 0, -0.525731}, // Vertex | {0.675973, -0.000000, -0.417775}, // Normal | Vertex 2 {1.0, 1.0, 0.0, 1.0} // Color | }, { {-0.850651, 0, -0.525731}, // Vertex | {-0.675973, 0.000000, -0.417775}, // Normal | Vertex 3 {0.5, 1.0, 0.0, 1.0} // Color | }, { {-0.850651, 0, 0.525731}, // Vertex | {-0.675973, -0.000000, 0.417775}, // Normal | Vertex 4 {0.0, 1.0, 0.0, 1.0} // Color | }, { {-0.525731, 0.850651, 0}, // Vertex | {-0.417775, 0.675974, 0.000000}, // Normal | Vertex 5 {0.0, 1.0, 0.5, 1.0} // Color | }, { {0.525731, 0.850651, 0}, // Vertex | {0.417775, 0.675973, -0.000000}, // Normal | Vertex 6 {0.0, 1.0, 1.0, 1.0} // Color | }, { {0.525731, -0.850651, 0}, // Vertex | {0.417775, -0.675974, 0.000000}, // Normal | Vertex 7 {0.0, 0.5, 1.0, 1.0} // Color | }, { {-0.525731, -0.850651, 0}, // Vertex | {-0.417775, -0.675974, 0.000000}, // Normal | Vertex 8 {0.0, 0.0, 1.0, 1.0}, // Color | }, { {0, -0.525731, -0.850651}, // Vertex | {0.000000, -0.417775, -0.675973}, // Normal | Vertex 9 {0.5, 0.0, 1.0, 1.0} // Color | }, { {0, 0.525731, -0.850651}, // Vertex | {0.000000, 0.417775, -0.675974}, // Normal | Vertex 10 {1.0, 0.0, 1.0, 1.0} // Color | }, { {0, 0.525731, 0.850651}, // Vertex | {0.000000, 0.417775, 0.675973}, // Normal | Vertex 11 {1.0, 0.0, 0.5, 1.0} // Color | } };
下面是我们如何传递信息到OpenGL。我们传递在这个数组里面的每个数据成员的第一个顶点的地址,并且提供所传递的数据的长度大小作为步长参数,而不是传递指针到合适的数组。
glColorPointer(4, GL_FLOAT, sizeof(ColoredVertexData3D), &vertexData[0].color);
glNormalPointer(GL_FLOAT, sizeof(ColoredVertexData3D), &vertexData[0].normal);
以上几个方法中,最后一个参数调用了数据里的第一个顶点,例如,&vertexData[0].color指向第一个顶点的颜色信息。stride(跨度)参数表明了在我们读取下一个相同类型的数据时,我们要跳过多少比特的数据。如果你看了下面的图解你也许就有那么一点点感觉了(不好意思它有点宽,你需要适当的调整你的浏览器以便于看到这个图解的所有部分)
变简单了不是吗?如果你一点也不喜欢打字,那么你可以下载这个十二面的交叉存取版本spinning icosahedron。我也会使用新的数据类型更新我的OpenGL ES Xcode Template。
我们现在依然没有使用triangle strips,但是混合三角形到triangle strips是下一步的教程,现在我要去参加WWDC了。
原文:iphonedevelopment,OpenGL ES from the Ground Up Part 8: Interleaving Vertex Data
iTyran翻译讨论地址:http://ityran.com/forum-36-1.html
我在从零开始学习OpenGL ES之四 – 光效 一文中使用了一个普通GLfloat数组。由于它没有使用任何非OpenGL定义的数据结构,所以是最为普通和方便的方式。
但在此我使用在第一部分中定义的Vertex3D, Vector3D和 Color3D数据结构重写了 setupView:方法。并不是这种方法“更好”,但是它是一种不同的方式。当我第一次学习OpenGL时,我发现使用顶点,颜色和三角形的术语比可变长度浮点数组更容易理解。如果你和我一样,那么你会发现这个版本更容易理解。
除了使用自定义数据结构外,我还减少了环境光元素的数量并将光源向右移动了一点。然后使用Vector3DMakeWithStartAndEndPoints()将移动的光源指向二十面体。这样做使得光效更为生动一点。
-(void)setupView:(GLView*)view { const GLfloat zNear = 0.01, zFar = 1000.0, fieldOfView = 45.0; GLfloat size; glEnable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0); CGRect rect = view.bounds; glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size / (rect.size.width / rect.size.height), zNear, zFar); glViewport(0, 0, rect.size.width, rect.size.height); glMatrixMode(GL_MODELVIEW); // Enable lighting glEnable(GL_LIGHTING); // Turn the first light on glEnable(GL_LIGHT0); // Define the ambient component of the first light static const Color3D light0Ambient[] = {{0.05, 0.05, 0.05, 1.0}}; glLightfv(GL_LIGHT0, GL_AMBIENT, (const GLfloat *)light0Ambient); // Define the diffuse component of the first light static const Color3D light0Diffuse[] = {{0.4, 0.4, 0.4, 1.0}}; glLightfv(GL_LIGHT0, GL_DIFFUSE, (const GLfloat *)light0Diffuse); // Define the specular component and shininess of the first light static const Color3D light0Specular[] = {{0.7, 0.7, 0.7, 1.0}}; glLightfv(GL_LIGHT0, GL_SPECULAR, (const GLfloat *)light0Specular); glLightf(GL_LIGHT0, GL_SHININESS, 0.4); // Define the position of the first light // const GLfloat light0Position[] = {10.0, 10.0, 10.0}; static const Vertex3D light0Position[] = {{10.0, 10.0, 10.0}}; glLightfv(GL_LIGHT0, GL_POSITION, (const GLfloat *)light0Position); // Calculate light vector so it points at the object static const Vertex3D objectPoint[] = {{0.0, 0.0, -3.0}}; const Vertex3D lightVector = Vector3DMakeWithStartAndEndPoints(light0Position[0], objectPoint[0]); glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, (GLfloat *)&lightVector); // Define a cutoff angle. This defines a 50° field of vision, since the cutoff // is number of degrees to each side of an imaginary line drawn from the light's // position along the vector supplied in GL_SPOT_DIRECTION above glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 25.0); glLoadIdentity(); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); }
你可以随意调整光的属性,增加额外的光源或二十面体,体验一下这些调整会为场景带来什么样的变化。这些东西是很难体验出来的,所以不要指望一晚上就理解了所有东西。