2019独角兽企业重金招聘Python工程师标准>>>

在android组件中主要分为两种:容器(LinearLayout....)和子View(TextView......),但是这些现有的组件往往不能满足app的开发。比如实现一个流式标签,炫酷的进度条显示呢。都需要我们探寻源码,分析和改造成我们想要的效果。这次的主题先从View的自定义入手。

概述:

针对View的自定义主要从 onMeasure()和onDraw()这两个方法入手。
    onMeasure():测量View的大小
        测量view只要针对我们在xml中wrap_content和match_parent的两种属性。而在onMeasure()对      应的字段是AT_MOST 和EXACTLY。Android默认的实现了EXACTLY的测量也就是精准测量(对应xml属性    match_parent和具体值),对于AT_MOST默认填充父容器,如果要实现包裹那就需要我们自己动手丰衣      足食了。

onDraw():绘制内容,比如形状,图片啊都在这里实现。
        主要用到类有paint(画笔),canvas(画板)由这两个类,我们就可以随心所欲的画画l咯。

实践出真理:

“纸上得来终觉浅,绝知此事要躬行“,现在我们就一步一步的用代码来分析自定义View的实践。想了好久我要用啥一个相对简单但大家都熟悉的view作为本文的的入门demo呢?最后决定我们来实现android自带的TextView。

入门demo:

创建一个class类,名为MyTextView  继承View。

package huangzhibo.com.learndemo.view;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;import huangzhibo.com.learndemo.R;/*** Created by HuangZhiBo on 2017/8/20/020.*/public class MyTextView extends View {private String text;private int paintColor = Color.BLACK;public MyTextView(Context context) {super(context);}public MyTextView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);/**实现AT_MOST的测量*/}public void setText(String info) {text = info;}public void setTextColor(int color) {paintColor = color;}@Overrideprotected void onDraw(Canvas canvas) {if (text == null) {  //文本为空return;}Paint paint = new Paint();paint.setColor(paintColor);paint.setAntiAlias(true); //抗锯齿paint.setTextSize(30);  //文本大小canvas.drawText(text,this.getWidth()/2-getTextWidth(paint,text)/2,this.getHeight()/2,paint);}/*** 测量文字宽度* @param paint* @param str* @return*/public static int getTextWidth(Paint paint, String str) {int w= 0;if (str != null && str.length() > 0) {int len = str.length();float[] widths = new float[len];paint.getTextWidths(str, widths);for (int j = 0; j < len; j++) {w+= (int) Math.ceil(widths[j]);}}return w;}
}

xml布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:orientation="vertical"android:layout_height="match_parent"tools:context="huangzhibo.com.learndemo.activity.CustomView"><huangzhibo.com.learndemo.view.MyTextViewandroid:background="@color/colorAccent"android:id="@+id/myTextView"android:layout_width="wrap_content"android:layout_height="wrap_content"/>
</RelativeLayout>

代码都有相应的注释,这里就不在废话,这里对于onMeasure并没有处理,采用默认。注意看layout_width和layout_height的属性值为wrap_content。上文我说道,如果对于onMearsure并未处理,那么view将填充父容器。为了看清楚边界,view设置了一个背景。结果不出所料,那么接下我们就来实现Android对于wrap_content的测量。

效果图:

在对于onMeasure的AT_MOST,我查了下网上很多资料都是直接给个固定值,然后通过比较返回最大值,这样根本就是治标不治本,我们要的效果是完全的TextView效果。那么问题来了,要实现包裹效果,也就需要确定宽和高。而这个宽和高就是文本的宽和高加上内边距(pading)。思路有了那就好办了。

package huangzhibo.com.learndemo.view;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;import huangzhibo.com.learndemo.R;import static android.os.Build.VERSION_CODES.M;/*** Created by HuangZhiBo on 2017/8/20/020.*/public class MyTextView extends View {private String text;private int paintColor = Color.BLACK;private Paint mPaint;public MyTextView(Context context) {super(context);}public MyTextView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);mPaint=new Paint();mPaint.setColor(paintColor);mPaint.setAntiAlias(true); //抗锯齿mPaint.setTextSize(30);  //文本大小}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int result = 200;/**实现AT_MOST的测量*/setMeasuredDimension(meaSureWidth(widthMeasureSpec),meaSureHeight(heightMeasureSpec));}/*** 测量宽度* @param measureSpec* @return*/private int meaSureWidth(int measureSpec){int result=0;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);if (specMode==MeasureSpec.AT_MOST){/**文本宽度+左右内边距*/int v = (int)mPaint.measureText(text) + getPaddingLeft() + getPaddingRight();result= Math.min(v, specSize);}return result;}/*** 测量宽度* @param measureSpec* @return*/private int meaSureHeight(int measureSpec){int result=0;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);if (specMode==MeasureSpec.AT_MOST){/**文本高度+上下内边距*/int v = (int) (-mPaint.ascent() + mPaint.descent())  + getPaddingTop() + getPaddingBottom();result= Math.min(v, specSize);}return result;}public void setText(String info) {text = info;}public void setTextColor(int color) {paintColor = color;}@Overrideprotected void onDraw(Canvas canvas) {if (text == null) {  //文本为空return;}/**注意 drawText中的x,y分别指的是文字的左边位置,文字baseLine的位置*/canvas.drawText(text,getPaddingLeft(),this.this.getHeight()-mPaint.descent()-getPaddingBottom(),mPaint);}/*** 测量文字宽度* @param paint* @param str* @return*/public static int getTextWidth(Paint paint, String str) {int w= 0;if (str != null && str.length() > 0) {int len = str.length();float[] widths = new float[len];paint.getTextWidths(str, widths);for (int j = 0; j < len; j++) {w+= (int) Math.ceil(widths[j]);}}return w;}
}

效果图:

到这里,我们已经完全解决了AT_MOST的测量了。不过对于以上的代码还需要解释下:

  • (-mPaint.ascent() + mPaint.descent()) 这个为啥能得到文字高度?原理可以看下这篇文章
    http://blog.csdn.net/mq2856992713/article/details/52327938(这个必须掌握)

基本的自定义view,我相信到这里你已经差不多掌握了,那么如何达到炉火纯青,信手拈来的境界呢?没有捷径,就是熟能生巧,接下来实现几个效果View。

仿华为记步View和常见的验证码View:

                           

华为记步View代码如下:

package huangzhibo.com.learndemo.view;import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;import huangzhibo.com.learndemo.R;/*** Created by HuangZhiBo on 2017/7/31/031.*/public class StepView extends View {/*圆弧宽度*/private float borderWidth = 38f;/* 画步数的数值的字体大小*/private float numberTextSize = 0;/*** 开始绘制圆弧的角度*/private float startAngle = 135;/*** 终点对应的角度和起始点对应的角度的夹角*/private float angleLength = 270;/*** 所要绘制的当前步数的红色圆弧终点到起点的夹角*/private float currentAngleLength = 0;private String stepNumber;/*** 动画时长*/private int animationLength = 3000;public StepView(Context context) {super(context);}public StepView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public StepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);/*中心点坐标*/float centerX = (getWidth()) / 2;/*指定圆弧的外轮廓矩形区域*/RectF rectF = new RectF(0 + borderWidth, borderWidth, 2 * centerX - borderWidth, 2 * centerX - borderWidth);/*绘制红色圆弧*/drawArcYellow(canvas, rectF);/*绘制蓝色走过步数*/drawArcRed(canvas, rectF);/*文字*/drawTex(canvas, rectF);}private void drawTex(Canvas canvas, RectF rectF) {Paint paint = new Paint();paint.setColor(getResources().getColor(R.color.colorAccent));paint.setAntiAlias(true);paint.setTextSize(32);int length = String.valueOf(currentAngleLength).length();canvas.drawText(currentAngleLength + "", rectF.centerX() - 70, getHeight() / 2, paint);}/*** 1.绘制总步数的黄色圆弧** @param canvas 画笔* @param rectF  参考的矩形*/private void drawArcYellow(Canvas canvas, RectF rectF) {Paint paint = new Paint();paint.setColor(getResources().getColor(R.color.colorAccent));/** 结合处为圆弧*/paint.setStrokeJoin(Paint.Join.ROUND);/** 设置画笔的样式 Paint.Cap.Round ,Cap.SQUARE等分别为圆形、方形*/paint.setStrokeCap(Paint.Cap.ROUND);/** 设置画笔的填充样式 Paint.Style.FILL  :填充内部;Paint.Style.FILL_AND_STROKE  :填充内部和描边;  Paint.Style.STROKE  :仅描边*/paint.setStyle(Paint.Style.STROKE);/**抗锯齿功能*/paint.setAntiAlias(true);/**设置画笔宽度*/paint.setStrokeWidth(38f);canvas.drawArc(rectF, startAngle, angleLength, false, paint);}/*** 2.绘制当前步数的蓝色圆弧** @param canvas* @param rectF*/private void drawArcRed(Canvas canvas, RectF rectF) {Paint paint = new Paint();/**设置结合处的样子,Miter:结合处为锐角, Round:结合处为圆弧:BEVEL:结合处为直线。*/paint.setStrokeJoin(Paint.Join.ROUND);paint.setStyle(Paint.Style.STROKE);//设置填充样式/*** 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式,如圆形样式Cap.ROUND,或方形样式Cap.SQUARE   */paint.setStrokeCap(Paint.Cap.ROUND);paint.setAntiAlias(true);//抗锯齿功能paint.setStrokeWidth(borderWidth);//设置画笔宽度paint.setColor(getResources().getColor(R.color.colorPrimary));canvas.drawArc(rectF, startAngle, currentAngleLength, false, paint);}/*** 所走的步数进度** @param totalStepNum  设置的步数* @param currentCounts 所走步数*/public void setCurrentCount(int totalStepNum, int currentCounts) {stepNumber = currentCounts + "";
/**如果当前走的步数超过总步数则圆弧还是270度,不能成为园*/if (currentCounts > totalStepNum) {currentCounts = totalStepNum;}
/**所走步数占用总共步数的百分比*/float scale = (float) currentCounts / totalStepNum;
/**换算成弧度最后要到达的角度的长度-->弧长*/float currentAngleLength = scale * angleLength;
/**开始执行动画*/setAnimation(0, currentAngleLength, animationLength);}/*** 为进度设置动画* ValueAnimator是整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行操作来实现的,* 而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。* 它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,* 我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,* 那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。** @param last* @param current*/private void setAnimation(float last, float current, int length) {ValueAnimator progressAnimator = ValueAnimator.ofFloat(last, current);progressAnimator.setDuration(length);progressAnimator.setTarget(currentAngleLength);progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {currentAngleLength = (float) animation.getAnimatedValue();invalidate();}});progressAnimator.start();}
}

代码里面也注释很多了,这里就不在过多解释

常见的验证码View代码:

package huangzhibo.com.learndemo.utils.commonedite;import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.ColorRes;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.AppCompatEditText;
import android.text.Editable;
import android.text.TextPaint;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.WindowManager;import huangzhibo.com.learndemo.R;/*** Created by HuangZhiBo on 2017/7/11/011.*/public class VerificationCodeEditText extends AppCompatEditText implements VerificationAction, TextWatcher {private int mFigures;private int mVerCodeMargin;private int mBottomSelectedColor; //底部选种颜色private int mBottomNormalColor;  //未选中颜色private float mBottomLineHeigth; //底部高度private int mSeleceBackgroundColor; //选中的背景颜色private OnVerificationCodeChangedListener onCodeChangeListener;private int mCurrentPosition = 0;private int mEachRectLength = 0;private Paint mSelectBackGroundPaint;private Paint mNormalBackGroundPaint;private Paint mBottomSelectdPaint;private Paint mBottomNormalPaint;public VerificationCodeEditText(Context context) {this(context, null);}public VerificationCodeEditText(Context context, AttributeSet attrs) {this(context, null,0);}public VerificationCodeEditText(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initAttrs(attrs);setBackgroundColor(ContextCompat.getColor(context, android.R.color.transparent)); //防止出现下划线initPaint();setFocusableInTouchMode(true);super.addTextChangedListener(this);}/*** 初始化pain*/private void initPaint() {mSelectBackGroundPaint = new Paint();mSelectBackGroundPaint.setColor(mSeleceBackgroundColor);mNormalBackGroundPaint = new Paint();mNormalBackGroundPaint.setColor(getColor(android.R.color.transparent));mBottomSelectdPaint = new Paint();mBottomSelectdPaint.setColor(mBottomSelectedColor);mBottomNormalPaint = new Paint();mBottomNormalPaint.setColor(mBottomNormalColor);mBottomSelectdPaint.setStrokeWidth(mBottomLineHeigth);mBottomNormalPaint.setStrokeWidth(mBottomLineHeigth);}private void initAttrs(AttributeSet attrs) {TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.EditText);mFigures = ta.getInteger(R.styleable.EditText_figures, 4);mVerCodeMargin = (int) ta.getDimension(R.styleable.EditText_verCodeMargin, 10);mBottomSelectedColor = ta.getColor(R.styleable.EditText_bottomLineSelectedColor, getCurrentTextColor());mBottomNormalColor = ta.getColor(R.styleable.EditText_bottomLineNormalColor, getColor(android.R.color.holo_red_dark));mBottomLineHeigth = ta.getDimension(R.styleable.EditText_bottomLineHeight, dp2px(5));mSeleceBackgroundColor = ta.getColor(R.styleable.EditText_selectedBackgroundColor, getColor(android.R.color.holo_red_dark));ta.recycle();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthResult = 0, heighResult = 0;//最终宽度int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);if (widthMode == MeasureSpec.EXACTLY) {widthResult = widthSize;} else {widthResult = getScreenWidth(getContext());}//每个矩形的宽度mEachRectLength = (widthResult - (mVerCodeMargin * (mFigures - 1))) / mFigures;//最终高度int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);if (heightMode == MeasureSpec.EXACTLY) {heighResult = heightSize;} else {heighResult = mEachRectLength;}setMeasuredDimension(widthResult, heighResult);}@Overrideprotected void onDraw(Canvas canvas) {mCurrentPosition = getText().length();int width = mEachRectLength - getPaddingLeft() - getPaddingRight(); //每个矩形宽度int height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); //整体高度//绘制每个矩形for (int i = 0; i < mFigures; i++) {canvas.save();int start = width * i + i * mVerCodeMargin;int end = width + start;//画一个矩形if (i == mCurrentPosition) {//选中canvas.drawRect(start, 0, end, height, mSelectBackGroundPaint);} else {canvas.drawRect(start, 0, end, height, mNormalBackGroundPaint);}canvas.restore();}//绘制文字String value = getText().toString();for (int i = 0; i < value.length(); i++) {canvas.save();int start = width * i + i * mVerCodeMargin;float x = start + width / 2;TextPaint paint = getPaint();paint.setTextAlign(Paint.Align.CENTER);paint.setColor(getCurrentTextColor());Paint.FontMetrics fontMetrics = paint.getFontMetrics();float baseLine = (height - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;//top 是个负数canvas.drawText(String.valueOf(value.charAt(i)), x, baseLine, paint);canvas.restore();}//绘制底线for (int i = 0; i < mFigures; i++) {canvas.save();float lineY = height - mBottomLineHeigth / 2;int start = width * i + i * mVerCodeMargin;int end = width + start;if (i < mCurrentPosition) {canvas.drawLine(start, lineY, end, lineY, mSelectBackGroundPaint);} else {canvas.drawLine(start, lineY, end, lineY, mBottomNormalPaint);}canvas.restore();}}/*** 获取手机屏幕的宽度*/static int getScreenWidth(Context context) {DisplayMetrics metrics = new DisplayMetrics();WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);wm.getDefaultDisplay().getMetrics(metrics);return metrics.widthPixels;}@Overridefinal public void setCursorVisible(boolean visible) {super.setCursorVisible(false);//隐藏光标的显示}@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {mCurrentPosition = getText().length();postInvalidate();}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {mCurrentPosition = getText().length();postInvalidate();if (onCodeChangeListener != null) {onCodeChangeListener.onVerCodeChanged(getText(), start, before, count);}}@Overridepublic void afterTextChanged(Editable s) {mCurrentPosition = getText().length();postInvalidate();if (getText().length() == mFigures) {if (onCodeChangeListener != null) {onCodeChangeListener.onInputCompleted(getText());}} else if (getText().length() > mFigures) {getText().delete(mFigures, getText().length());}}@Overridepublic void setFigures(int figures) {mFigures = figures;postInvalidate();}@Overridepublic void setVerCodeMargin(int margin) {mVerCodeMargin = margin;postInvalidate();}@Overridepublic void setBottomSelectedColor(@ColorRes int bottomSelectedColor) {mBottomSelectedColor = bottomSelectedColor;postInvalidate();}@Overridepublic void setBottomNormalColor(@ColorRes int bottomNormalColor) {mBottomNormalColor = bottomNormalColor;postInvalidate();}@Overridepublic void setSelectedBackgroundColor(@ColorRes int selectedBackground) {mSeleceBackgroundColor = selectedBackground;postInvalidate();}@Overridepublic void setBottomLineHeight(int bottomLineHeight) {mBottomLineHeigth=bottomLineHeight;postInvalidate();}@Overridepublic void setOnVerificationCodeChangedListener(OnVerificationCodeChangedListener listener) {this.onCodeChangeListener=listener;}/*** 返回颜色*/private int getColor(@ColorRes int color) {return ContextCompat.getColor(getContext(), color);}/*** dp转px*/private int dp2px(int dp) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,getResources().getDisplayMetrics());}}

对于自定义验证码的View,需要考虑超出文字字数处理,文字变化监听处理,相对于这个demo来说还是有一点难度,需要大家慢慢的去理解和消化。这里把几个要点总结下,以便更好的去理解这个实现逻辑

  • 初始界面,绘画一个矩形(分为选中和未选中),底部横线,绘制文字
  • 文字输入监听,重新绘制
  • 文字超出处理

以上的demo已经放在网上仓库,需要的话可以点击https://git.oschina.net/huagnzhibo123/LearnDemo

这个demo有我最近在整理的项目常用的工具类和框架,会不断完善。

转载于:https://my.oschina.net/huangzhi1bo/blog/1517689

Android 高级自定义View实战相关推荐

  1. Android 系统(201)---Android 自定义View实战系列 :时间轴

    Android 自定义View实战系列 :时间轴 Android开发中,时间轴的 UI需求非常常见,如下图: 本文将结合 自定义View & RecyclerView的知识,手把手教你实现该常 ...

  2. 【Android自定义View实战】之自定义评价打分控件RatingBar,可以自定义星星大小和间距...

    [Android自定义View实战]之自定义评价打分控件RatingBar,可以自定义星星大小和间距

  3. android 行布局选择器,『自定义View实战』—— 银行种类选择器

    在工作中难免遇到自定义 View 的相关需求,本身这方面比较薄弱,因此做个记录,也是自己学习和成长的积累.自定义View实战 前言 年前的最后一个开发需求,将之前H5开卡界面转变成native.意思就 ...

  4. android 选择银行类型,『自定义View实战』—— 银行种类选择器

    在工作中难免遇到自定义 View 的相关需求,本身这方面比较薄弱,因此做个记录,也是自己学习和成长的积累.自定义View实战 前言 年前的最后一个开发需求,将之前H5开卡界面转变成native.意思就 ...

  5. [自定义控件]android自定义view实战之太极图

    android自定义view实战之太极图 尊重原创,转载请注明出处: http://blog.csdn.net/qq137722697 自定义view是Android工程师进阶不可避免要接触的,我的学 ...

  6. Android进阶之自定义View实战(二)九宫格手势解锁实现

    一.引言 在上篇博客Android进阶之自定义View实战(一)仿iOS UISwitch控件实现中我们主要介绍了自定义View的最基本的实现方法.作为自定义View的入门篇,仅仅介绍了Canvas的 ...

  7. android 图片处理过程中添加进度条,『Android自定义View实战』给我一个图标,还你一个水波纹进度球...

    前言 我们都知道,平时表现进度的方式有千千万万种(没有UI想不到的,只有你做不到的= =.),其中有一种就是水波纹进度球的形式,网上很多种实现都是直接采用纯色填充的方式,即水波纹都是纯颜色填充,效果看 ...

  8. android 弹出菜单环形,『Android自定义View实战』实现一个小清新的弹出式圆环菜单...

    前言 Android表现快捷菜单的形式有很多种,比如使用PopupWindow弹出来的小弹窗,类似QQ的侧拉功能菜单,以及之前讲过的弧形菜单( Android 自定义弧形旋转菜单栏--卫星菜单),这次 ...

  9. android 动态画直线,Android使用自定义view在指定时间内匀速画一条直线的实例代码...

    本文讲述了Android使用自定义view在指定时间内匀速画一条直线的实例代码.分享给大家供大家参考,具体如下: 1.效果图: 2.自定义view实现 public class UniformLine ...

最新文章

  1. Mastercam X9中文版
  2. linux 产生0~100内的随机数
  3. VTK:图表之VisualizeGraph
  4. iis5.0+php5.0+mysql5.0配置完全手册_IIS5.0+PHP5.0+MySQL5.0配置完全手册
  5. java 动态加载控件_JS动态添加节点后渲染为EasyUI控件,EasyUI动态渲染解析解决方案...
  6. C++之命名空间探究
  7. 2021年衡阳仁爱中学高考成绩查询,南岳衡阳蒸湘仁爱中学简介
  8. [UE4]复制引起的重复对象
  9. 锋利的jQuery总结(三)
  10. 恩智浦智能汽车竞赛电磁组总结
  11. iOS不能显示英文音标问题
  12. 最近在做微信支付委托代扣交互图。总结了几点需要注意的地方,与大家分享一下
  13. 傅里叶Fourier变换fft-python-scipy-幅值-辐角-相位(一)
  14. 尚医通项目101-123:前台用户系统、登录注册、邮箱登录
  15. 如何通过发送短信唤起三方app?
  16. 迪赛智慧数——柱状图(象形柱图):水果VC含量排行榜
  17. Gradual warmup lr schedule--pytorch
  18. python有架构师吗_运维架构师-Python 自动化运维开发-021
  19. 使用Dockerfile创建包含nginx-fair和nginx-check模块的nginx镜像
  20. 短文本匹配模型-ESIM

热门文章

  1. 1、Angular2 Component 组件
  2. Cardinality 对执行计划的重要性
  3. python8_python8
  4. 系统架构_Linux内核系统架构介绍
  5. 服务器文件数量监控,服务器监控指标有哪些?好文章一定要收藏
  6. 字符转char android,android – 如何在kotlin语言中将char转换为ascii值
  7. php 自留地,重蔚自留地php基本语法-函数(附代码)
  8. java 判断pc还是手机_java-判断是移动端还是PC端访问
  9. 乐迪机器人正确操作_什么是机器人示教器?
  10. 解决 win10 下载文件时 没有权限保存到某盘