热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

onMeasure多次调用问题

getWidth()与getMeasuredWidth()的区别一般在自定义控件的时候getMeasuredWidthgetMeasuredHeight它的赋值在View的set

getWidth()与getMeasuredWidth()的区别

一般在自定义控件的时候getMeasuredWidth/getMeasuredHeight它的赋值在View的setMeasuredDimension中,所以有时可以在onMeasure方法中看到利用getMeasuredWidth/getMeasuredHeight初始化别的参数。而getWidth/getHeight一直在onLayout完成后才会被赋值。一般情况下,如果都完成了赋值,两者值是相同的.(摘自鸿洋)
参考:(Android视图绘制流程完全解析,带你一步步深入了解View(二))

关于View的onMeasure()、onSizeChanged()、onLayout()、onDraw()调用顺序
调用顺序应该是 构造函数——->onMeasure——->onSizeChanged——->onLayout——->onDraw
后面有可能 onMeasure——->onLayout——->onDraw
(摘自here)
并且是在onResume之后。

需要注意的是,在setMeasuredDimension()方法调用之后,我们才能使用getMeasuredWidth()和getMeasuredHeight()来获取视图测量出的宽高,以此之前调用这两个方法得到的值都会是0。

视图大小的控制是由父视图、布局文件、以及视图本身共同完成的,父视图会提供给子视图参考的大小,而开发人员可以在XML文件中指定视图的大小,然后视图本身会对最终的大小进行拍板。
参考:Android视图绘制流程完全解析,带你一步步深入了解View(二)

onMeasure多次调用问题

Android如何绘制视图,解释了为何onMeasure有时要调用多次
为什么FlowLayout的 onMeasure方法会执行4次,onLayout方法会执行两次呢,

父视图可能在它的子视图上调用一次以上的measure(int,int)方法。例如,父视图可以使用unspecified dimensions来将它的每个子视图都测量一次来算出它们到底需要多大尺寸,如果所有这些子视图没被限制的尺寸的和太大或太小,那么它会用精确数值再次调用measure()(也就是说,如果子视图不满意它们获得的区域大小,那么父视图将会干涉并设置第二次测量规则)。

测试:

@SuppressWarnings("deprecation")
public class MyActivity extends FragmentActivity {@TargetApi(Build.VERSION_CODES.LOLLIPOP)@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.test3);Display display = getWindowManager().getDefaultDisplay();int displayWidth = display.getWidth();int displayHeight = display.getHeight();Log.e("111", "display.getWidth()==" + displayWidth);//1080Log.e("111", "display.getHeight()==" + displayHeight);//1812Log.e("111", "densityDpi==" + getResources().getDisplayMetrics().densityDpi);//480Log.e("111", "density==" + getResources().getDisplayMetrics().density);//3.0int statusHeight = getStatusHeight(this);Log.e("111", "statusHeight==" + statusHeight);//72Log.e("111", "displayHeight-statusHeight==" + (displayHeight - statusHeight));//1740int actionBarHeight = getActionBarHeight(this);Log.e("111", "actionBarHeight==" + actionBarHeight);//168Log.e("111", "statusHeight+statusHeight==" + (statusHeight + actionBarHeight));//240Log.e("111", "displayHeight-(statusHeight + actionBarHeight)==" + (displayHeight - (statusHeight + actionBarHeight)));//1572}public int getStatusHeight(Context context) {int statusHeight = -1;try {Class clazz = Class.forName("com.android.internal.R$dimen");Object object = clazz.newInstance();int height = Integer.parseInt(clazz.getField("status_bar_height").get(object).toString());statusHeight = context.getResources().getDimensionPixelSize(height);} catch (Exception e) {e.printStackTrace();}return statusHeight;}public int getActionBarHeight(Context context) {TypedValue localTypedValue = new TypedValue();if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, localTypedValue, true)) {return TypedValue.complexToDimensionPixelSize(localTypedValue.data, context.getResources().getDisplayMetrics());}return 0;}}

布局:



@SuppressLint("AppCompatCustomView")
public class RoundImageView extends ImageView {private Paint mPaint;private Bitmap bitmap;private Matrix mMatrix;private int mWidth;private int mRadius;private BitmapShader bitmapShader;private RectF mRoundRect;public RoundImageView(Context context) {super(context);init();}public RoundImageView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}public RoundImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {mPaint = new Paint();mPaint.setAntiAlias(true);mMatrix = new Matrix();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);Log.e("111", ".......onMeasure.......");Log.e("111", "0getHeight......." + getHeight());//0Log.e("111", "0getWidth......." + getWidth());//0//1560->1740(调用了两次onmeasure,第二次的1740=屏幕高1812-状态栏高72)Log.e("111", "0getMeasuredHeight......." + getMeasuredHeight());//1080->1080(调用了两次onmeasure)Log.e("111", "0getMeasuredWidth......." + getMeasuredWidth());Log.e("111", "0getSuggestedMinimumHeight......." + getSuggestedMinimumHeight());//0Log.e("111", "0getSuggestedMinimumWidth......." + getSuggestedMinimumWidth());//0mWidth = Math.min(getMeasuredWidth(), getMeasuredHeight());mRadius = mWidth / 2;setMeasuredDimension(mWidth, mWidth);//1080Log.e("111", ".......onMeasure......................");Log.e("111", "0getHeight......." + getHeight());//0Log.e("111", "0getWidth......." + getWidth());//0Log.e("111", "0getMeasuredHeight......." + getMeasuredHeight());//1080->1080Log.e("111", "0getMeasuredWidth......." + getMeasuredWidth());//1080-->1080Log.e("111", "0getSuggestedMinimumHeight......." + getSuggestedMinimumHeight());//0Log.e("111", "0getSuggestedMinimumWidth......." + getSuggestedMinimumWidth());//0}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);Log.e("111", ".......onSizeChanged.......");Log.e("111", "1getHeight......." + getHeight());//1080(setMeasuredDimension决定)Log.e("111", "1getWidth......." + getWidth());//1080Log.e("111", "1getMeasuredHeight......." + getMeasuredHeight());//1080Log.e("111", "1getMeasuredWidth......." + getMeasuredWidth());//1080Log.e("111", "1getSuggestedMinimumHeight......." + getSuggestedMinimumHeight());//0Log.e("111", "1getSuggestedMinimumWidth......." + getSuggestedMinimumWidth());//0mRoundRect = new RectF(0, 0, getWidth(), getHeight());}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);Log.e("111", ".......onLayout.......");Log.e("111", "2getHeight......." + getHeight());//1080Log.e("111", "2getWidth......." + getWidth());//1080Log.e("111", "2getMeasuredHeight......." + getMeasuredHeight());//1080Log.e("111", "2getMeasuredWidth......." + getMeasuredWidth());//1080Log.e("111", "2getSuggestedMinimumHeight......." + getSuggestedMinimumHeight());//0Log.e("111", "2getSuggestedMinimumWidth......." + getSuggestedMinimumWidth());//0}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);Log.e("111", ".......onDraw.......");Log.e("111", "3getHeight......." + getHeight());//1080Log.e("111", "3getWidth......." + getWidth());//1080Log.e("111", "3getMeasuredHeight......." + getMeasuredHeight());//1080Log.e("111", "3getMeasuredWidth......." + getMeasuredWidth());//1080Log.e("111", "3getSuggestedMinimumHeight......." + getSuggestedMinimumHeight());//0Log.e("111", "3getSuggestedMinimumWidth......." + getSuggestedMinimumWidth());//0bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);Log.e("111", ".bitmap.getHeight......." + bitmap.getHeight());//300Log.e("111", ".bitmap.getWidth........" + bitmap.getWidth());//300bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);int bSize = Math.min(bitmap.getWidth(), bitmap.getHeight());float scale = mWidth * 1.0f / bSize;mMatrix.setScale(scale, scale);bitmapShader.setLocalMatrix(mMatrix);mPaint.setShader(bitmapShader);Log.e("111", "===drawCircle==mRadius=" + mRadius);//540canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);}
}

activity 用了NoActionBar的style


为什么getSuggestedMinimumHeight、Width打印为零

我们修改布局:添加 android:minHeight=”100dp”和android:minWidth=”100dp”



再次打印如下:

可见android:minHeight影响了getSuggestedMinimumHeight,
而且getSuggestedMinimumHeight默认为零。

display.getWidth()==1080
display.getHeight()==1812
densityDpi==480
density==3.0
statusHeight==72
displayHeight-statusHeight==1740
actionBarHeight==168
statusHeight+statusHeight==240
displayHeight-(statusHeight + actionBarHeight)==1572.......onMeasure.......
0getHeight.......0
0getWidth.......0
0getMeasuredHeight.......1560
0getMeasuredWidth.......1080
0getSuggestedMinimumHeight.......300
0getSuggestedMinimumWidth.......300
.......onMeasure......................
0getHeight.......0
0getWidth.......0
0getMeasuredHeight.......1080
0getMeasuredWidth.......1080
0getSuggestedMinimumHeight.......300
0getSuggestedMinimumWidth.......300.......onMeasure.......
0getHeight.......0
0getWidth.......0
0getMeasuredHeight.......1740
0getMeasuredWidth.......1080
0getSuggestedMinimumHeight.......300
0getSuggestedMinimumWidth.......300
.......onMeasure......................
0getHeight.......0
0getWidth.......0
0getMeasuredHeight.......1080
0getMeasuredWidth.......1080
0getSuggestedMinimumHeight.......300
0getSuggestedMinimumWidth.......300.......onSizeChanged.......
1getHeight.......1080
1getWidth.......1080
1getMeasuredHeight.......1080
1getMeasuredWidth.......1080
1getSuggestedMinimumHeight.......300
1getSuggestedMinimumWidth.......300.......onLayout.......
2getHeight.......1080
2getWidth.......1080
2getMeasuredHeight.......1080
2getMeasuredWidth.......1080
2getSuggestedMinimumHeight.......300
2getSuggestedMinimumWidth.......300.......onDraw.......
3getHeight.......1080
3getWidth.......1080
3getMeasuredHeight.......1080
3getMeasuredWidth.......1080
3getSuggestedMinimumHeight.......300
3getSuggestedMinimumWidth.......300.bitmap.getHeight.......300
.bitmap.getWidth........300
===drawCircle==mRadius=540

onLayout、layout

Android的onLayout、layout方法讲解
onLayout方法是ViewGroup中子View的布局方法,用于放置子View的位置。放置子View很简单,只需在重写onLayout方法,然后获取子View的实例,调用子View的layout方法实现布局。在实际开发中,一般要配合onMeasure测量方法一起使用。

@Override
protected abstract void onLayout(boolean changed,int l, int t, int r, int b);

该方法在ViewGroup中定义是抽象函数,继承该类必须实现onLayout方法,而ViewGroup的onMeasure并非必须重写的。View的放置都是根据一个矩形空间放置的,onLayout传下来的l,t,r,b分别是放置父控件的矩形可用空间(除去margin和padding的空间)的左上角的left、top以及右下角right、bottom值。

public void layout(int l, int t, int r, int b);

该方法是View的放置方法,在View类实现。调用该方法需要传入放置View的矩形空间左上角left、top值和右下角right、bottom值。这四个值是相对于父控件而言的。例如传入的是(10, 10, 100, 100),则该View在距离父控件的左上角位置(10, 10)处显示,显示的大小是宽高是90(参数r,b是相对左上角的),这有点像绝对布局。

使用方法:

public class MyViewGroup extends ViewGroup {// 子View的水平间隔private final static int padding = 20;public MyViewGroup(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stub}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {// TODO Auto-generated method stub// 动态获取子View实例for (int i = 0, size = getChildCount(); i

自己的测试:


public class CircleProgressView extends View {public CircleProgressView(Context context, AttributeSet attrs,int defStyleAttr) {super(context, attrs, defStyleAttr);int measureHeight=getMeasuredHeight();int measureWidth=getMeasuredWidth();int height=getHeight();int width=getWidth();Log.e(TAG, "333onMeasure: measureHeight=="+measureHeight+"--measureWidth=="+measureWidth+"--height=="+height+"--width=="+width );}public CircleProgressView(Context context, AttributeSet attrs) {super(context, attrs);int measureHeight=getMeasuredHeight();int measureWidth=getMeasuredWidth();int height=getHeight();int width=getWidth();int left=getLeft();int right=getRight();Log.e(TAG, "444构造方法: measureHeight=="+measureHeight+"--measureWidth=="+measureWidth+"--height=="+height+"--width=="+width +"--left=="+left+"--right=="+right);}public CircleProgressView(Context context) {super(context);int measureHeight=getMeasuredHeight();int measureWidth=getMeasuredWidth();int height=getHeight();int width=getWidth();Log.e(TAG, "555onMeasure: measureHeight=="+measureHeight+"--measureWidth=="+measureWidth+"--height=="+height+"--width=="+width );}@Overrideprotected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {//widthMeasureSpec和heightMeasureSpec不是一般的尺寸数值,而是将模式和尺寸组合在一起的数值。mMeasureWidth = MeasureSpec.getSize(widthMeasureSpec);//测量后的宽度mMeasureHeigth = MeasureSpec.getSize(heightMeasureSpec);//测量后的高度setMeasuredDimension(mMeasureWidth, mMeasureHeigth);int measureHeight=getMeasuredHeight();int measureWidth=getMeasuredWidth();int height=getHeight();int width=getWidth();int left=getLeft();int right=getRight();Log.e(TAG, "1111onMeasure: measureHeight=="+measureHeight+"--measureWidth=="+measureWidth+"--height=="+height+"--width=="+width +"--left=="+left+"--right=="+right);initView();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 绘制圆canvas.drawCircle(mCircleXY, mCircleXY, mRadius, mCirclePaint);//圆心、半径// 绘制弧线canvas.drawArc(mArcRectF, 270, mSweepAngle, false, mArcPaint);//起始角度、扫过角度、不用中心// 绘制文字canvas.drawText(mShowText, 0, mShowText.length(),mCircleXY, mCircleXY + (mShowTextSize / 4), mTextPaint);//文本,文本的起始与终止,文本位置(要看对齐方式)int measureHeight=getMeasuredHeight();int measureWidth=getMeasuredWidth();int height=getHeight();int width=getWidth();int left=getLeft();int right=getRight();Log.e(TAG, "2222onDraw: measureHeight=="+measureHeight+"--measureWidth=="+measureWidth+"--height=="+height+"--width=="+width +"--left=="+left+"--right=="+right);}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);int measureHeight=getMeasuredHeight();int measureWidth=getMeasuredWidth();int height=getHeight();int width=getWidth();Log.e(TAG, "666onLayout: measureHeight=="+measureHeight+"--measureWidth=="+measureWidth+"--height=="+height+"--width=="+width +"--left=="+left+"--right=="+right);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);int measureHeight=getMeasuredHeight();int measureWidth=getMeasuredWidth();int height=getHeight();int width=getWidth();int left=getLeft();int right=getRight();Log.e(TAG, "777onSizeChanged: measureHeight=="+measureHeight+"--measureWidth=="+measureWidth+"--height=="+height+"--width=="+width +"--left=="+left+"--right=="+right);}private void initView() {float length = 0;if (mMeasureHeigth >= mMeasureWidth) {length = mMeasureWidth;} else {length = mMeasureHeigth;}mCircleXY = length / 2;mRadius = (float) (length * 0.5 / 2);mCirclePaint = new Paint();mCirclePaint.setAntiAlias(true);mCirclePaint.setColor(getResources().getColor(android.R.color.holo_blue_bright));mArcRectF = new RectF((float) (length * 0.1),(float) (length * 0.1),(float) (length * 0.9),(float) (length * 0.9));mSweepAngle = (mSweepValue / 100f) * 360f;mArcPaint = new Paint();mArcPaint.setAntiAlias(true);mArcPaint.setColor(getResources().getColor(android.R.color.holo_blue_bright));mArcPaint.setStrokeWidth((float) (length * 0.1));mArcPaint.setStyle(Style.STROKE);//描边mShowText = setShowText();mShowTextSize = setShowTextSize();mTextPaint = new Paint();mTextPaint.setTextSize(mShowTextSize);mTextPaint.setTextAlign(Paint.Align.CENTER);}}

log结果:

02-07 10:07:34.930 13920-13920/com.imooc.systemwidget E/ContentValues:
444构造方法:
measureHeight==0--measureWidth==0--
height==0--width==0--
left==0--right==002-07 10:07:34.955 13920-13920/com.imooc.systemwidget E/ContentValues:
1111onMeasure:
measureHeight==1692--measureWidth==1080--
height==0--width==0--
left==0--right==002-07 10:07:35.005 13920-13920/com.imooc.systemwidget E/ContentValues:
1111onMeasure: measureHeight==1692--measureWidth==1080--
height==0--width==0--
left==0--right==002-07 10:07:35.006 13920-13920/com.imooc.systemwidget E/ContentValues:
777onSizeChanged: measureHeight==1692--measureWidth==1080--
height==1692--width==1080--
left==0--right==108002-07 10:07:35.006 13920-13920/com.imooc.systemwidget E/ContentValues:
666onLayout: measureHeight==1692--measureWidth==1080--
height==1692--width==1080--
left==0--right==108002-07 10:07:35.023 13920-13920/com.imooc.systemwidget E/ContentValues:
2222onDraw: measureHeight==1692--measureWidth==1080--
height==1692--width==1080--
left==0--right==108002-07 10:07:35.092 13920-13920/com.imooc.systemwidget E/ContentValues:
1111onMeasure: measureHeight==1692--measureWidth==1080--
height==1692--width==1080--
left==0--right==108002-07 10:07:35.094 13920-13920/com.imooc.systemwidget E/ContentValues:
666onLayout: measureHeight==1692--measureWidth==1080--
height==1692--width==1080--
left==0--right==108002-07 10:07:35.095 13920-13920/com.imooc.systemwidget E/ContentValues:
2222onDraw: measureHeight==1692--measureWidth==1080--
height==1692--width==1080--
left==0--right==1080

结论:


1、调用顺序应该是
构造函数——->onMeasure——->onSizeChanged——->onLayout——->onDraw——-> onMeasure——->onLayout——->onDraw
2、getMeasuredWidth()在onmeasure方法中可获取
getWidth()在onSizeChanged、onLayout、onDraw等方法中即可获取。
3、getWidth()=getRight()-getLeft()
4、当getMeasuredWidth()和getWidth()都有值的时候,两者相等。


参考

Android群英传

Android艺术探索


推荐阅读
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 本文详细介绍了Android中的坐标系以及与View相关的方法。首先介绍了Android坐标系和视图坐标系的概念,并通过图示进行了解释。接着提到了View的大小可以超过手机屏幕,并且只有在手机屏幕内才能看到。最后,作者表示将在后续文章中继续探讨与View相关的内容。 ... [详细]
  • 带添加按钮的GridView,item的删除事件
    先上图片效果;gridView无数据时显示添加按钮,有数据时,第一格显示添加按钮,后面显示数据:布局文件:addr_manage.xml<?xmlve ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 标题: ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 如何搭建Java开发环境并开发WinCE项目
    本文介绍了如何搭建Java开发环境并开发WinCE项目,包括搭建开发环境的步骤和获取SDK的几种方式。同时还解答了一些关于WinCE开发的常见问题。通过阅读本文,您将了解如何使用Java进行嵌入式开发,并能够顺利开发WinCE应用程序。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
author-avatar
无淚丑尜鸭
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有