Android4.2.2 Gallery2源码分析(6)——GLView.java

来源:互联网 发布:孟加拉语翻译软件 编辑:程序博客网 时间:2024/05/21 17:03
[java] view plaincopyprint?
  1. // GLView is a UI component. It can render to a GLCanvas and accept touch  
  2. // events. A GLView may have zero or more child GLView and they form a tree  
  3. // structure. The rendering and event handling will pass through the tree  
  4. // structure.   
  5. //   
  6. // A GLView tree should be attached to a GLRoot before event dispatching and  
  7. // rendering happens. GLView asks GLRoot to re-render or re-layout the  
  8. // GLView hierarchy using requestRender() and requestLayoutContentPane().  
  9. //   
  10. // The render() method is called in a separate thread. Before calling  
  11. // dispatchTouchEvent() and layout(), GLRoot acquires a lock to avoid the  
  12. // rendering thread running at the same time. If there are other entry points  
  13. // from main thread (like a Handler) in your GLView, you need to call  
  14. // lockRendering() if the rendering thread should not run at the same time.  
  15. //  


 

上面这一段为定义在GLView.java开端的一段注释,它写明了GLView的作用。以往我们定义一个视图,所有视图控件都是从View.java继承来的,而这里,自定义了一个GLView.java实现View的功能。作为视图,它只实现了View的一小部分功能,从这里我们也可以学习到Android系统视图控件是如何实现的。先解释下上面这段注释:

GLView是可以接收触摸事件并通过GLCanvas进行渲染图画的UI组件。GLView可以拥有子控件从而组成一个树状的控件结构。渲染和触摸事件都通过这个树状结构进行传递。

一个GLView树在事件传递和渲染之前必须先附着到一个GLRoot对象中。GLView通过调用requestRender()和requestLayoutContentPane()要求GLRoot对GLView图层的重复渲染(包括画图和位置分配)。

render()方法需要在GLView所在线程之外的线程中调用。在调用dispatchTouchEvent()和layout()方法之前,GLRoot要求锁住线程以避免同时运行渲染线程。如果在主线程之中有其他到达GLView的入口(比如一个Handler),我们需要先调用lockRendering()锁住该线程避免该线程和主线程的同时运行。

接下来,我们从源码中分析,到底GLView是怎么实现注释中所描述的功能。而作为一个视图控件,又是怎么完成“视图”这一角色的。

[java] view plaincopyprint?
  1. <SPAN style="FONT-SIZE: 12px">    public interface OnClickListener {  
  2.         void onClick(GLView v);  
  3.     }</SPAN>  

申明点击事件,这也是GLView唯一拥有的事件(关于事件,回调,监听这些概念,需要了解回调事件,反转调用这些机制)。

[java] view plaincopyprint?
  1. <SPAN style="FONT-SIZE: 12px">    // This should only be called on the content pane (the topmost GLView).  
  2.     public void attachToRoot(GLRoot root) {  
  3.         Utils.assertTrue(mParent == null && mRoot == null);//断言,这是一种调试方法  
  4.         onAttachToRoot(root);  
  5.     }  
  6.   
  7.     // This should only be called on the content pane (the topmost GLView).  
  8.     public void detachFromRoot() {  
  9.         Utils.assertTrue(mParent == null && mRoot != null);  
  10.         onDetachFromRoot();  
  11.     }  
  12.   
  13. ...  
  14.   
  15.     protected void onAttachToRoot(GLRoot root) {  
  16.         mRoot = root;  
  17.         for (int i = 0, n = getComponentCount(); i < n; ++i) {//通过递归对root(某个具体的调用attachToRoot()方法传进来的GLView作为父视图)和所有子视图形成树状结构。  
  18.             getComponent(i).onAttachToRoot(root);  
  19.         }  
  20.     }  
  21.   
  22.     protected void onDetachFromRoot() {  
  23.         for (int i = 0, n = getComponentCount(); i < n; ++i) {  
  24.             getComponent(i).onDetachFromRoot();  
  25.         }  
  26.         mRoot = null;  
  27.     }</SPAN>  

我们关注一下下面的这个方法:

[java] view plaincopyprint?
  1. <SPAN style="FONT-SIZE: 12px">    public void invalidate() {  
  2.         GLRoot root = getGLRoot();  
  3.         if (root != null) root.requestRender();  
  4.     }</SPAN>  

invalidate()在View.java中表示刷新View的意思,这里表明了GLView的刷新是通过requestRender()完成的。而GLRoot是一个接口,requestRender()还没有定义,需要在具体应用中具体实现如何刷新。这就是接口的反转调用,根据具体情况进行灵活的实现。这是可以学习的地方。

同理的是重新分配子视图位置的方法:

[java] view plaincopyprint?
  1. <SPAN style="FONT-SIZE: 12px">    public void requestLayout() {  
  2.         mViewFlags |= FLAG_LAYOUT_REQUESTED;  
  3.         mLastHeightSpec = -1;  
  4.         mLastWidthSpec = -1;  
  5.         if (mParent != null) {  
  6.             mParent.requestLayout();  
  7.         } else {  
  8.             // Is this a content pane ?   
  9.             GLRoot root = getGLRoot();  
  10.             if (root != null) root.requestLayoutContentPane();  
  11.         }  
  12.     }</SPAN>  

接下来是重点分析的render()函数,它等同于View.java中的onDraw()函数。即该子视图显示什么样的内容由这里实现。

[java] view plaincopyprint?
  1. <SPAN style="FONT-SIZE: 12px">    protected void render(GLCanvas canvas) {  
  2.         boolean transitionActive = false;  
  3.         if (mTransition != null && mTransition.calculate(AnimationTime.get())) {  
  4.             invalidate();  
  5.             transitionActive = mTransition.isActive();  
  6.         }  
  7.         renderBackground(canvas);  
  8.         canvas.save();  
  9.         if (transitionActive) {  
  10.             mTransition.applyContentTransform(this, canvas);  
  11.         }  
  12.         for (int i = 0, n = getComponentCount(); i < n; ++i) {  
  13.             renderChild(canvas, getComponent(i));  
  14.         }  
  15.         canvas.restore();  
  16.         if (transitionActive) {  
  17.             mTransition.applyOverlay(this, canvas);  
  18.         }  
  19.     }</SPAN>  


看看renderBackground(canvas)

[java] view plaincopyprint?
  1. <SPAN style="FONT-SIZE: 12px">    protected void renderBackground(GLCanvas view) {  
  2.         if (mBackgroundColor != null) {  
  3.             view.clearBuffer(mBackgroundColor);  
  4.         }  
  5.         if (mTransition != null && mTransition.isActive()) {  
  6.             mTransition.applyBackground(this, view);  
  7.             return;  
  8.         }  
  9.     }</SPAN>  

显然这里需要关注的是applyBackground()这个方法,这是定义在StateTransitionAnimation.java这一动画类中的方法。

[java] view plaincopyprint?
  1. <SPAN style="FONT-SIZE: 12px">    public void applyBackground(GLView view, GLCanvas canvas) {  
  2.         if (mCurrentBackgroundAlpha > 0f) {  
  3.             applyOldTexture(view, canvas, mCurrentBackgroundAlpha, mCurrentBackgroundScale, true);  
  4.         }  
  5.     }  
  6.   
  7. ...  
  8.   
  9.     private void applyOldTexture(GLView view, GLCanvas canvas, float alpha, float scale, boolean clear) {  
  10.         if (mOldScreenTexture == null)  
  11.             return;  
  12.         if (clear) canvas.clearBuffer(view.getBackgroundColor());  
  13.         canvas.save();  
  14.         canvas.setAlpha(alpha);  
  15.         int xOffset = view.getWidth() / 2;  
  16.         int yOffset = view.getHeight() / 2;  
  17.         canvas.translate(xOffset, yOffset);  
  18.         canvas.scale(scale, scale, 1);  
  19.         mOldScreenTexture.draw(canvas, -xOffset, -yOffset);  
  20.         canvas.restore();  
  21.     }</SPAN>  

mOldScreenTexture在StateTransitionAnimation.java的构造参数中申明,因此我们终于搞明白了GLView首先渲染的是调用StateTransitionAnimation.java这一动画的视图时传进来的材质(材质的知识是OpenGL中关于材质渲染需要了解的),至于具体是什么材质,1可以从GLView.java中看对StateTransitionAnimation的调用是否为空,如果不为空,说明这是GLView的固有属性,所有子视图都会拥有这一属性。2为空则看某个子视图不为空的调用,则是该子视图的扩展属性。

在GLView.java中,mTransition的值为:

[java] view plaincopyprint?
  1. <SPAN style="FONT-SIZE: 12px">    public void setIntroAnimation(StateTransitionAnimation intro) {  
  2.         mTransition = intro;  
  3.         if (mTransition != null) mTransition.start();  
  4.     }</SPAN>  

这个方法是启动这个动画的地方,也是render()中首先渲染的材质传进来的地方。因此说明renderBackground()和该动画联系密切。可以说renderBackground()是专为有动画时服务的。

上面的内容说明GLView拥有一个与StateTransitionAnimation动画相关的性质,这个性质的具体内容则要看在StateTransitonAnimation中如何实现的,此处不关注。回到render()函数,接下来重点关注的是renderChild()这一函数。

[java] view plaincopyprint?
  1. <SPAN style="FONT-SIZE: 12px">    protected void renderChild(GLCanvas canvas, GLView component) {  
  2.         if (component.getVisibility() != GLView.VISIBLE  
  3.                 && component.mAnimation == nullreturn;  
  4.   
  5.         int xoffset = component.mBounds.left - mScrollX;  
  6.         int yoffset = component.mBounds.top - mScrollY;  
  7.   
  8.         canvas.translate(xoffset, yoffset);  
  9.   
  10.         CanvasAnimation anim = component.mAnimation;  
  11.         if (anim != null) {  
  12.             canvas.save(anim.getCanvasSaveFlags());  
  13.             if (anim.calculate(AnimationTime.get())) {  
  14.                 invalidate();  
  15.             } else {  
  16.                 component.mAnimation = null;  
  17.             }  
  18.             anim.apply(canvas);  
  19.         }  
  20.         component.render(canvas);  
  21.         if (anim != null) canvas.restore();  
  22.         canvas.translate(-xoffset, -yoffset);  
  23.     }</SPAN>  


上面这段代码中,对Canvas作了平移操作,然后调用component.render(canvas)渲染子视图,因此实现了递归渲染所有子视图到画布上。

这里的这个GLCanvas画布是个什么东西,由Canvas的调用堆栈这一节我们知道它是一个GLCanvasImpl对象,查看GLCanvasImpl构造函数知道这是一块缓冲区,没有进行任何绘制操作。因此GLView中的render()函数只是对画布进行了一些平移操作,没有进行任何绘制操作。而具体子视图则需要看子视图中是如何进行绘制的。


GLView.java的内容暂时分析到这里,具体的视图与图形外貌的联系还需要我们查看具体子视图代码中是如何进行绘制的,GLView只是所有子视图一个共有的属性和动作的集合,但是子视图也可能会更改这些属性和动作。




 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 魅族手机玩王者荣耀卡怎么办 魅族手机导航gps信号弱怎么办 魅族手机4g信号差怎么办 魅族手机下面一排键失灵怎么办 魅族手机冲不进去电怎么办 苹果账号付款了又让付款怎么办 魅蓝3s开机定屏怎么办 魅族手机未找到固件怎么办不用电脑 魅族手机触屏局部失灵怎么办 更新了魅蓝的新系统掉帧怎么办 手机后盖摔了一下凹了一个洞怎么办 魅族手机充电需要重启怎么办 网上预约好了但就诊卡丢了怎么办 如果魅族手机锁屏密码忘记了怎么办 魅族手机应用加密忘记密码了怎么办 小米电视盒子3s死机黑屏怎么办 小米note充电充不进去电怎么办 乐视手机otg功能用不了怎么办 小米4c手机玩王者荣耀卡怎么办 捡的小米6被锁了怎么办 小米5s手机玩穿越火线卡怎么办 苹果平板更新完系统登录不上怎么办 谷歌商店一直卡在核对信息怎么办 贴吧邮箱忘了无法登陆怎么办 华为手机绑定了账号忘了怎么办 淘宝的手机多次注册无法登录怎么办 电脑看不到U盘里面的文件怎么办 c盘访问权限被锁了怎么办 xp系统电脑开机密码忘记了怎么办 华为荣耀9用久了卡怎么办 华为p9相机模糊敲打就正常怎么办 华为p9蓝频按什么都没反映怎么办 华为手机p9开机密码忘了怎么办 华为P9手机开机一直闪屏怎么办 荣耀畅玩7x用户数据锁定怎么办 华为手机p9的指纹解锁坏了怎么办 华为P9青春版外放音量小怎么办 华为麦芒5的开关键失灵怎么办 小米手机进水黑屏但是有声音怎么办 小米5手机突然黑屏没电怎么办 小米4开不了机怎么办充电没反应