当前位置: 首页 > news >正文

做3d动画的斑马网站自媒体平台哪个好

做3d动画的斑马网站,自媒体平台哪个好,网站做网站广告,网站空间格式asp转自#xff1a;http://blog.csdn.net/guolin_blog/article/details/16330267 在上一篇文章中#xff0c;我带着大家一起剖析了一下LayoutInflater的工作原理#xff0c;可以算是对View进行深入了解的第一步吧。那么本篇文章中#xff0c;我们将继续对View进行深入探究http://blog.csdn.net/guolin_blog/article/details/16330267 在上一篇文章中我带着大家一起剖析了一下LayoutInflater的工作原理可以算是对View进行深入了解的第一步吧。那么本篇文章中我们将继续对View进行深入探究看一看它的绘制流程到底是什么样的。如果你还没有看过我的上一篇文章可以先去阅读 Android LayoutInflater原理分析带你一步步深入了解View(一)  。 相 信每个Android程序员都知道我们每天的开发工作当中都在不停地跟View打交道Android中的任何一个布局、任何一个控件其实都是直接或间 接继承自View的如TextView、Button、ImageView、ListView等。这些控件虽然是Android系统本身就提供好的我 们只需要拿过来使用就可以了但你知道它们是怎样被绘制到屏幕上的吗多知道一些总是没有坏处的那么我们赶快进入到本篇文章的正题内容吧。 要知道任何一个视图都不可能凭空突然出现在屏幕上它们都是要经过非常科学的绘制流程后才能显示出来的。每一个视图的绘制过程都必须经历三个最主要的阶段即onMeasure()、onLayout()和onDraw()下面我们逐个对这三个阶段展开进行探讨。 一. onMeasure() measure 是测量的意思那么onMeasure()方法顾名思义就是用于测量视图的大小的。View系统的绘制流程会从ViewRoot的 performTraversals()方法中开始在其内部调用View的measure()方法。measure()方法接收两个参 数widthMeasureSpec和heightMeasureSpec这两个值分别用于确定视图的宽度和高度的规格和大小。 MeasureSpec的值由specSize和specMode共同组成的其中specSize记录的是大小specMode记录的是规格。specMode一共有三种类型如下所示 1. EXACTLY 表示父视图希望子视图的大小应该是由specSize的值来决定的系统默认会按照这个规则来设置子视图的大小开发人员当然也可以按照自己的意愿设置成任意的大小。 2. AT_MOST 表示子视图最多只能是specSize中指定的大小开发人员应该尽可能小得去设置这个视图并且保证不会超过specSize。系统默认会按照这个规则来设置子视图的大小开发人员当然也可以按照自己的意愿设置成任意的大小。 3. UNSPECIFIED 表示开发人员可以将视图按照自己的意愿设置成任意的大小没有任何限制。这种情况比较少见不太会用到。 那 么你可能会有疑问了widthMeasureSpec和heightMeasureSpec这两个值又是从哪里得到的呢通常情况下这两个值都是由父 视图经过计算后传递给子视图的说明父视图会在一定程度上决定子视图的大小。但是最外层的根视图它的widthMeasureSpec和 heightMeasureSpec又是从哪里得到的呢这就需要去分析ViewRoot中的源码了观察performTraversals()方法可 以发现如下代码 [java] view plaincopy childWidthMeasureSpec  getRootMeasureSpec(desiredWindowWidth, lp.width);  childHeightMeasureSpec  getRootMeasureSpec(desiredWindowHeight, lp.height);   可 以看到这里调用了getRootMeasureSpec()方法去获取widthMeasureSpec和heightMeasureSpec的值注 意方法中传入的参数其中lp.width和lp.height在创建ViewGroup实例的时候就被赋值了它们都等于MATCH_PARENT。然 后看下getRootMeasureSpec()方法中的代码如下所示 [java] view plaincopy private int getRootMeasureSpec(int windowSize, int rootDimension) {      int measureSpec;      switch (rootDimension) {      case ViewGroup.LayoutParams.MATCH_PARENT:          measureSpec  MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);          break;      case ViewGroup.LayoutParams.WRAP_CONTENT:          measureSpec  MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);          break;      default:          measureSpec  MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);          break;      }      return measureSpec;  }   可 以看到这里使用了MeasureSpec.makeMeasureSpec()方法来组装一个MeasureSpec当rootDimension参 数等于MATCH_PARENT的时候MeasureSpec的specMode就等于EXACTLY当rootDimension等于 WRAP_CONTENT的时候MeasureSpec的specMode就等于AT_MOST。并且MATCH_PARENT和 WRAP_CONTENT时的specSize都是等于windowSize的也就意味着根视图总是会充满全屏的。 介绍了这么多MeasureSpec相关的内容接下来我们看下View的measure()方法里面的代码吧如下所示 [java] view plaincopy public final void measure(int widthMeasureSpec, int heightMeasureSpec) {      if ((mPrivateFlags  FORCE_LAYOUT)  FORCE_LAYOUT ||              widthMeasureSpec ! mOldWidthMeasureSpec ||              heightMeasureSpec ! mOldHeightMeasureSpec) {          mPrivateFlags  ~MEASURED_DIMENSION_SET;          if (ViewDebug.TRACE_HIERARCHY) {              ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);          }          onMeasure(widthMeasureSpec, heightMeasureSpec);          if ((mPrivateFlags  MEASURED_DIMENSION_SET) ! MEASURED_DIMENSION_SET) {              throw new IllegalStateException(onMeasure() did not set the                        measured dimension by calling                        setMeasuredDimension());          }          mPrivateFlags | LAYOUT_REQUIRED;      }      mOldWidthMeasureSpec  widthMeasureSpec;      mOldHeightMeasureSpec  heightMeasureSpec;  }   注 意观察measure()这个方法是final的因此我们无法在子类中去重写这个方法说明Android是不允许我们改变View的measure 框架的。然后在第9行调用了onMeasure()方法这里才是真正去测量并设置View大小的地方默认会调用getDefaultSize()方法 来获取视图的大小如下所示 [java] view plaincopy public static int getDefaultSize(int size, int measureSpec) {      int result  size;      int specMode  MeasureSpec.getMode(measureSpec);      int specSize  MeasureSpec.getSize(measureSpec);      switch (specMode) {      case MeasureSpec.UNSPECIFIED:          result  size;          break;      case MeasureSpec.AT_MOST:      case MeasureSpec.EXACTLY:          result  specSize;          break;      }      return result;  }   这 里传入的measureSpec是一直从measure()方法中传递过来的。然后调用MeasureSpec.getMode()方法可以解析出 specMode调用MeasureSpec.getSize()方法可以解析出specSize。接下来进行判断如果specMode等于 AT_MOST或EXACTLY就返回specSize这也是系统默认的行为。之后会在onMeasure()方法中调用 setMeasuredDimension()方法来设定测量出的大小这样一次measure过程就结束了。 当然一个界面的展示可能会涉及到很多次的measure因为一个布局中一般都会包含多个子视图每个视图都需要经历一次measure过程。ViewGroup中定义了一个measureChildren()方法来去测量子视图的大小如下所示 [java] view plaincopy protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {      final int size  mChildrenCount;      final View[] children  mChildren;      for (int i  0; i  size; i) {          final View child  children[i];          if ((child.mViewFlags  VISIBILITY_MASK) ! GONE) {              measureChild(child, widthMeasureSpec, heightMeasureSpec);          }      }  }   这里首先会去遍历当前布局下的所有子视图然后逐个调用measureChild()方法来测量相应子视图的大小如下所示 [java] view plaincopy protected void measureChild(View child, int parentWidthMeasureSpec,          int parentHeightMeasureSpec) {      final LayoutParams lp  child.getLayoutParams();      final int childWidthMeasureSpec  getChildMeasureSpec(parentWidthMeasureSpec,              mPaddingLeft  mPaddingRight, lp.width);      final int childHeightMeasureSpec  getChildMeasureSpec(parentHeightMeasureSpec,              mPaddingTop  mPaddingBottom, lp.height);      child.measure(childWidthMeasureSpec, childHeightMeasureSpec);  }   可 以看到在第4行和第6行分别调用了getChildMeasureSpec()方法来去计算子视图的MeasureSpec计算的依据就是布局文件中 定义的MATCH_PARENT、WRAP_CONTENT等值这个方法的内部细节就不再贴出。然后在第8行调用子视图的measure()方法并把 计算出的MeasureSpec传递进去之后的流程就和前面所介绍的一样了。 当然onMeasure()方法是可以重写的也就是说如果你不想使用系统默认的测量方式可以按照自己的意愿进行定制比如 [java] view plaincopy public class MyView extends View {        ......            Override      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {          setMeasuredDimension(200, 200);      }    }   这样的话就把View默认的测量流程覆盖掉了不管在布局文件中定义MyView这个视图的大小是多少最终在界面上显示的大小都将会是200*200。 需要注意的是在setMeasuredDimension()方法调用之后我们才能使用getMeasuredWidth()和getMeasuredHeight()来获取视图测量出的宽高以此之前调用这两个方法得到的值都会是0。 由此可见视图大小的控制是由父视图、布局文件、以及视图本身共同完成的父视图会提供给子视图参考的大小而开发人员可以在XML文件中指定视图的大小然后视图本身会对最终的大小进行拍板。 到此为止我们就把视图绘制流程的第一阶段分析完了。 二. onLayout() measure 过程结束后视图的大小就已经测量好了接下来就是layout的过程了。正如其名字所描述的一样这个方法是用于给视图进行布局的也就是确定视图的位 置。ViewRoot的performTraversals()方法会在measure结束后继续执行并调用View的layout()方法来执行此过 程如下所示 [java] view plaincopy host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);   layout()方法接收四个参数分别代表着左、上、右、下的坐标当然这个坐标是相对于当前视图的父视图而言的。可以看到这里还把刚才测量出的宽度和高度传到了layout()方法中。那么我们来看下layout()方法中的代码是什么样的吧如下所示 [java] view plaincopy public void layout(int l, int t, int r, int b) {      int oldL  mLeft;      int oldT  mTop;      int oldB  mBottom;      int oldR  mRight;      boolean changed  setFrame(l, t, r, b);      if (changed || (mPrivateFlags  LAYOUT_REQUIRED)  LAYOUT_REQUIRED) {          if (ViewDebug.TRACE_HIERARCHY) {              ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);          }          onLayout(changed, l, t, r, b);          mPrivateFlags  ~LAYOUT_REQUIRED;          if (mOnLayoutChangeListeners ! null) {              ArrayListOnLayoutChangeListener listenersCopy                       (ArrayListOnLayoutChangeListener) mOnLayoutChangeListeners.clone();              int numListeners  listenersCopy.size();              for (int i  0; i  numListeners; i) {                  listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);              }          }      }      mPrivateFlags  ~FORCE_LAYOUT;  }   在 layout()方法中首先会调用setFrame()方法来判断视图的大小是否发生过变化以确定有没有必要对当前的视图进行重绘同时还会在这里把 传递过来的四个参数分别赋值给mLeft、mTop、mRight和mBottom这几个变量。接下来会在第11行调用onLayout()方法正如 onMeasure()方法中的默认行为一样也许你已经迫不及待地想知道onLayout()方法中的默认行为是什么样的了。进入onLayout() 方法咦怎么这是个空方法一行代码都没有 没错View中的onLayout()方法就是一个空方法因为onLayout()过 程是为了确定视图在布局中所在的位置而这个操作应该是由布局来完成的即父视图决定子视图的显示位置。既然如此我们来看下ViewGroup中的 onLayout()方法是怎么写的吧代码如下 [java] view plaincopy Override  protected abstract void onLayout(boolean changed, int l, int t, int r, int b);   可 以看到ViewGroup中的onLayout()方法竟然是一个抽象方法这就意味着所有ViewGroup的子类都必须重写这个方法。没错像 LinearLayout、RelativeLayout等布局都是重写了这个方法然后在内部按照各自的规则对子视图进行布局的。由于 LinearLayout和RelativeLayout的布局规则都比较复杂就不单独拿出来进行分析了这里我们尝试自定义一个布局借此来更深刻地 理解onLayout()的过程。 自定义的这个布局目标很简单只要能够包含一个子视图并且让子视图正常显示出来就可以了。那么就给这个布局起名叫做SimpleLayout吧代码如下所示 [java] view plaincopy public class SimpleLayout extends ViewGroup {        public SimpleLayout(Context context, AttributeSet attrs) {          super(context, attrs);      }        Override      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {          super.onMeasure(widthMeasureSpec, heightMeasureSpec);          if (getChildCount()  0) {              View childView  getChildAt(0);              measureChild(childView, widthMeasureSpec, heightMeasureSpec);          }      }        Override      protected void onLayout(boolean changed, int l, int t, int r, int b) {          if (getChildCount()  0) {              View childView  getChildAt(0);              childView.layout(0, 0, childView.getMeasuredWidth(), childView.getMeasuredHeight());          }      }    }   代 码非常的简单我们来看下具体的逻辑吧。你已经知道onMeasure()方法会在onLayout()方法之前调用因此这里在 onMeasure()方法中判断SimpleLayout中是否有包含一个子视图如果有的话就调用measureChild()方法来测量出子视图的 大小。 接着在onLayout()方法中同样判断SimpleLayout是否有包含一个子视图然后调用这个子视图的 layout()方法来确定它在SimpleLayout布局中的位置这里传入的四个参数依次是0、0、 childView.getMeasuredWidth()和childView.getMeasuredHeight()分别代表着子视图在 SimpleLayout中左上右下四个点的坐标。其中调用childView.getMeasuredWidth()和 childView.getMeasuredHeight()方法得到的值就是在onMeasure()方法中测量出的宽和高。 这样就已经把SimpleLayout这个布局定义好了下面就是在XML文件中使用它了如下所示 [html] view plaincopy com.example.viewtest.SimpleLayout xmlns:androidhttp://schemas.android.com/apk/res/android      android:layout_widthmatch_parent      android:layout_heightmatch_parent             ImageView           android:layout_widthwrap_content          android:layout_heightwrap_content          android:srcdrawable/ic_launcher          /        /com.example.viewtest.SimpleLayout   可 以看到我们能够像使用普通的布局文件一样使用SimpleLayout只是注意它只能包含一个子视图多余的子视图会被舍弃掉。这里 SimpleLayout中包含了一个ImageView并且ImageView的宽高都是wrap_content。现在运行一下程序结果如下图所 示                                 OKImageView成功已经显示出来了并且显示的位置也正是我们所期望的。如果你想改变ImageView显示的位置只需要改变childView.layout()方法的四个参数就行了。 在 onLayout()过程结束后我们就可以调用getWidth()方法和getHeight()方法来获取视图的宽高了。说到这里我相信很多朋友长 久以来都会有一个疑问getWidth()方法和getMeasureWidth()方法到底有什么区别呢它们的值好像永远都是相同的。其实它们的值 之所以会相同基本都是因为布局设计者的编码习惯非常好实际上它们之间的差别还是挺大的。 首先getMeasureWidth()方法在 measure()过程结束后就可以获取到了而getWidth()方法要在layout()过程结束后才能获取到。另 外getMeasureWidth()方法中的值是通过setMeasuredDimension()方法来进行设置的而getWidth()方法中 的值则是通过视图右边的坐标减去左边的坐标计算出来的。 观察SimpleLayout中onLayout()方法的代码这里给子视图的 layout()方法传入的四个参数分别是0、0、childView.getMeasuredWidth()和 childView.getMeasuredHeight()因此getWidth()方法得到的值就是 childView.getMeasuredWidth() - 0  childView.getMeasuredWidth() 所以此时getWidth()方法和getMeasuredWidth() 得到的值就是相同的但如果你将onLayout()方法中的代码进行如下修改 [java] view plaincopy Override  protected void onLayout(boolean changed, int l, int t, int r, int b) {      if (getChildCount()  0) {          View childView  getChildAt(0);          childView.layout(0, 0, 200, 200);      }  }   这 样getWidth()方法得到的值就是200 - 0 200不会再和getMeasuredWidth()的值相同了。当然这种做法充分不尊重measure()过程计算出的结果通常情况下是不推荐这么 写的。getHeight()与getMeasureHeight()方法之间的关系同上就不再重复分析了。 到此为止我们把视图绘制流程的第二阶段也分析完了。 三. onDraw() measure 和layout的过程都结束后接下来就进入到draw的过程了。同样根据名字你就能够判断出在这里才真正地开始对视图进行绘制。ViewRoot中 的代码会继续执行并创建出一个Canvas对象然后调用View的draw()方法来执行具体的绘制工作。draw()方法内部的绘制过程总共可以分为 六步其中第二步和第五步在一般情况下很少用到因此这里我们只分析简化后的绘制过程。代码如下所示 [java] view plaincopy public void draw(Canvas canvas) {      if (ViewDebug.TRACE_HIERARCHY) {          ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);      }      final int privateFlags  mPrivateFlags;      final boolean dirtyOpaque  (privateFlags  DIRTY_MASK)  DIRTY_OPAQUE               (mAttachInfo  null || !mAttachInfo.mIgnoreDirtyState);      mPrivateFlags  (privateFlags  ~DIRTY_MASK) | DRAWN;      // Step 1, draw the background, if needed      int saveCount;      if (!dirtyOpaque) {          final Drawable background  mBGDrawable;          if (background ! null) {              final int scrollX  mScrollX;              final int scrollY  mScrollY;              if (mBackgroundSizeChanged) {                  background.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);                  mBackgroundSizeChanged  false;              }              if ((scrollX | scrollY)  0) {                  background.draw(canvas);              } else {                  canvas.translate(scrollX, scrollY);                  background.draw(canvas);                  canvas.translate(-scrollX, -scrollY);              }          }      }      final int viewFlags  mViewFlags;      boolean horizontalEdges  (viewFlags  FADING_EDGE_HORIZONTAL) ! 0;      boolean verticalEdges  (viewFlags  FADING_EDGE_VERTICAL) ! 0;      if (!verticalEdges  !horizontalEdges) {          // Step 3, draw the content          if (!dirtyOpaque) onDraw(canvas);          // Step 4, draw the children          dispatchDraw(canvas);          // Step 6, draw decorations (scrollbars)          onDrawScrollBars(canvas);          // were done...          return;      }  }   可 以看到第一步是从第9行代码开始的这一步的作用是对视图的背景进行绘制。这里会先得到一个mBGDrawable对象然后根据layout过程确定 的视图位置来设置背景的绘制区域之后再调用Drawable的draw()方法来完成背景的绘制工作。那么这个mBGDrawable对象是从哪里来的 呢其实就是在XML中通过android:background属性设置的图片或颜色。当然你也可以在代码中通过 setBackgroundColor()、setBackgroundResource()等方法进行赋值。 接下来的第三步是在第 34行执行的这一步的作用是对视图的内容进行绘制。可以看到这里去调用了一下onDraw()方法那么onDraw()方法里又写了什么代码呢进 去一看你会发现原来又是个空方法啊。其实也可以理解因为每个视图的内容部分肯定都是各不相同的这部分的功能交给子类来去实现也是理所当然的。 第 三步完成之后紧接着会执行第四步这一步的作用是对当前视图的所有子视图进行绘制。但如果当前的视图没有子视图那么也就不需要进行绘制了。因此你会发现 View中的dispatchDraw()方法又是一个空方法而ViewGroup的dispatchDraw()方法中就会有具体的绘制代码。 以 上都执行完后就会进入到第六步也是最后一步这一步的作用是对视图的滚动条进行绘制。那么你可能会奇怪当前的视图又不一定是ListView或者 ScrollView为什么要绘制滚动条呢其实不管是Button也好TextView也好任何一个视图都是有滚动条的只是一般情况下我们都没 有让它显示出来而已。绘制滚动条的代码逻辑也比较复杂这里就不再贴出来了因为我们的重点是第三步过程。 通过以上流程分析相信大家已经 知道View是不会帮我们绘制内容部分的因此需要每个视图根据想要展示的内容来自行绘制。如果你去观察TextView、ImageView等类的源 码你会发现它们都有重写onDraw()这个方法并且在里面执行了相当不少的绘制逻辑。绘制的方式主要是借助Canvas这个类它会作为参数传入到 onDraw()方法中供给每个视图使用。Canvas这个类的用法非常丰富基本可以把它当成一块画布在上面绘制任意的东西那么我们就来尝试一下 吧。 这里简单起见我只是创建一个非常简单的视图并且用Canvas随便绘制了一点东西代码如下所示 [java] view plaincopy public class MyView extends View {        private Paint mPaint;        public MyView(Context context, AttributeSet attrs) {          super(context, attrs);          mPaint  new Paint(Paint.ANTI_ALIAS_FLAG);      }        Override      protected void onDraw(Canvas canvas) {          mPaint.setColor(Color.YELLOW);          canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);          mPaint.setColor(Color.BLUE);          mPaint.setTextSize(20);          String text  Hello View;          canvas.drawText(text, 0, getHeight() / 2, mPaint);      }  }   可 以看到我们创建了一个自定义的MyView继承自View并在MyView的构造函数中创建了一个Paint对象。Paint就像是一个画笔一样配 合着Canvas就可以进行绘制了。这里我们的绘制逻辑比较简单在onDraw()方法中先是把画笔设置成黄色然后调用Canvas的 drawRect()方法绘制一个矩形。然后在把画笔设置成蓝色并调整了一下文字的大小然后调用drawText()方法绘制了一段文字。 就这么简单一个自定义的视图就已经写好了现在可以在XML中加入这个视图如下所示 [html] view plaincopy LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android      android:layout_widthmatch_parent      android:layout_heightmatch_parent         com.example.viewtest.MyView           android:layout_width200dp          android:layout_height100dp          /    /LinearLayout   将MyView的宽度设置成200dp高度设置成100dp然后运行一下程序结果如下图所示                        图中显示的内容也正是MyView这个视图的内容部分了。由于我们没给MyView设置背景因此这里看不出来View自动绘制的背景效果。 当然了Canvas的用法还有很多很多这里我不可能把Canvas的所有用法都列举出来剩下的就要靠大家自行去研究和学习了。 到此为止我们把视图绘制流程的第三阶段也分析完了。整个视图的绘制过程就全部结束了你现在是不是对View的理解更加深刻了呢感兴趣的朋友可以继续阅读 Android视图状态及重绘流程分析带你一步步深入了解View(三) 转载于:https://www.cnblogs.com/wangziqiang/p/4270641.html
http://www.pierceye.com/news/331836/

相关文章:

  • 建设电影网站怎么上传电影如何建企业网站
  • 响应式网站制作公司org域名购买
  • 石家庄网站建设德信互联科技有限公司爬取漫画数据做网站
  • 国内免费iphone网站百度自然排名优化
  • 自己做网站主机做网站和推广
  • wordpress 淘宝嘉兴做网站优化
  • h5 网站开发流程asp个人网站建设
  • 网站建设 正邦企业邮箱怎么认证
  • 建设流网站项目成都住建局官网首页
  • 网站简历文字如何空行产品如何推广市场
  • 吕梁网站设计腾讯营销平台
  • 如何查看网站流量公众号申请网站
  • 阐述企业搭建网站的重要性免费做效果图的网站有哪些
  • 快速网站搭建南宁广告公司网站建设
  • 做数学题网站专业做网站建设 昆山
  • 建筑网站上海网页设计图片素材网
  • 延边网站开发depawo做汽车网站销售怎么入手
  • 商城网站开发技术南京好的网站制作公司
  • 嘉兴网站建设嘉兴网站推广网站网络营销方案
  • 镇江建工建设集团网站建设银行网站怎么基本转个人
  • 自己建的网站打开的特别慢盐城网站建设效果
  • 专业建站报价wordpress这软件怎么搜索
  • 德国网站建设电工培训内容
  • 织梦手机wap网站标签调用外贸网站建设公司如何
  • 在那里能找到网站泰安公司网站开发
  • 大兴区企业网站建设我们网站的优势
  • 呼伦贝尔市建设局网站关键词如何排名在首页
  • 网站带后台模板网站的建设宗旨
  • 深圳网站建设php专门查企业的网站
  • 做问卷调查的网站有啥世界比分榜