Android自定义view之围棋动画

好久不见,最近公众号内粉丝要求上新一篇有点难度的自定义view文章,所以它来了!!


干货文,建议收藏

文章目录

  • Android自定义view之围棋动画
  • 前言
  • 完成效果图
  • 一、测量
    • 1.获取宽高
    • 2.定义测量最小长度
  • 二、绘制背景(棋盘)
    • 1.初始化画笔
    • 2.画棋盘
    • 3.补棋盘瑕疵
  • 三.画个不可改变的棋子(以便于了解动画移动位置)
  • 四.为动画开始做准备以及动画
    • 1.三个辅助类为动画做准备(参数模仿Android官方Demo)
    • 2.自定义该接口实例来控制动画的更新计算表达式
    • 3.棋子的创建
    • 4.动画的创建
    • 5.两个动画的同步执行
    • 6.效果图
    • 7.解决第6步问题
  • 五.自定义属性
  • 六.自定义属性设置后运行效果
  • 七.小改变,视觉效果就不一样了!
  • 八.源码
  • 总结

前言

废话不多说直接开始


老规矩,文章最后有源码

完成效果图

棋子加渐变色

棋子不加渐变色

一、测量

1.获取宽高

 @Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mWidth = w;mHeight = h;useWidth = mWidth;if (mWidth > mHeight) {useWidth = mHeight;}}

2.定义测量最小长度

将布局分为10份。以minwidth的1,3,5,7,9的倍数为标准点。

        minwidth = useWidth / 10;

二、绘制背景(棋盘)

1.初始化画笔

        mPaint = new Paint();        //创建画笔对象mPaint.setColor(Color.BLACK);    //设置画笔颜色mPaint.setStyle(Paint.Style.FILL); //设置画笔模式为填充mPaint.setStrokeWidth(4f);     //设置画笔宽度为10pxmPaint.setAntiAlias(true);     //设置抗锯齿mPaint.setAlpha(255);        //设置画笔透明度

2.画棋盘

        //细的X轴canvas.drawLine(minwidth, 3 * minwidth, 9 * minwidth, 3 * minwidth, mPaint);// 斜线canvas.drawLine(minwidth, 5 * minwidth, 9 * minwidth, 5 * minwidth, mPaint);// 斜线canvas.drawLine(minwidth, 7 * minwidth, 9 * minwidth, 7 * minwidth, mPaint);// 斜线//细的y轴canvas.drawLine(3 * minwidth, minwidth, 3 * minwidth, 9 * minwidth, mPaint);// 斜线canvas.drawLine(5 * minwidth, minwidth, 5 * minwidth, 9 * minwidth, mPaint);// 斜线canvas.drawLine(7 * minwidth, minwidth, 7 * minwidth, 9 * minwidth, mPaint);// 斜线mPaint.setStrokeWidth(8f);//粗的X轴(边框)canvas.drawLine(minwidth, minwidth, 9 * minwidth, minwidth, mPaint);// 斜线canvas.drawLine(minwidth, 9 * minwidth, 9 * minwidth, 9 * minwidth, mPaint);// 斜线//粗的y轴(边框)canvas.drawLine(minwidth, minwidth, minwidth, 9 * minwidth, mPaint);// 斜线canvas.drawLine(9 * minwidth, minwidth, 9 * minwidth, 9 * minwidth, mPaint);// 斜线

绘制完后,发现有点小瑕疵
效果图:

3.补棋盘瑕疵

        canvas.drawPoint(minwidth, minwidth, mPaint);canvas.drawPoint(9 * minwidth, minwidth, mPaint);canvas.drawPoint(minwidth, 9 * minwidth, mPaint);canvas.drawPoint(9 * minwidth, 9 * minwidth, mPaint);

效果图:

三.画个不可改变的棋子(以便于了解动画移动位置)

位置比例
(3,3)(3,5)(3,7)
(5,3)(5,5)(5,7)
(7,3)(7,5)(7,7)

        //画围棋canvas.drawCircle(3*minwidth, 3*minwidth, useWidth/16, mPaint);canvas.drawCircle(3*minwidth, 7*minwidth, useWidth/16, mPaint);canvas.drawCircle(5*minwidth, 5*minwidth, useWidth/16, mPaint);canvas.drawCircle(7*minwidth, 3*minwidth, useWidth/16, mPaint);canvas.drawCircle(7*minwidth, 7*minwidth, useWidth/16, mPaint);mPaint.setColor(rightcolor);canvas.drawCircle(3*minwidth, 5*minwidth, useWidth/16, mPaint);canvas.drawCircle(5*minwidth, 3*minwidth, useWidth/16, mPaint);canvas.drawCircle(5*minwidth, 7*minwidth, useWidth/16, mPaint);canvas.drawCircle(7*minwidth, 5*minwidth, useWidth/16, mPaint);

效果图:

四.为动画开始做准备以及动画

1.三个辅助类为动画做准备(参数模仿Android官方Demo)

主要为get set构造,代码会贴到最后

2.自定义该接口实例来控制动画的更新计算表达式

public class XYEvaluator implements TypeEvaluator {public Object evaluate(float fraction, Object startValue, Object endValue) {XYHolder startXY = (XYHolder) startValue;XYHolder endXY = (XYHolder) endValue;return new XYHolder(startXY.getX() + fraction * (endXY.getX() - startXY.getX()),startXY.getY() + fraction * (endXY.getY() - startXY.getY()));}
}

3.棋子的创建

    private ShapeHolder createBall(float x, float y, int color) {OvalShape circle = new OvalShape();circle.resize(useWidth / 8f, useWidth / 8f);ShapeDrawable drawable = new ShapeDrawable(circle);ShapeHolder shapeHolder = new ShapeHolder(drawable);shapeHolder.setX(x - useWidth / 16f);shapeHolder.setY(y - useWidth / 16f);Paint paint = drawable.getPaint();paint.setColor(color);return shapeHolder;}

4.动画的创建

    private void createAnimation() {if (bounceAnim == null) {XYHolder lstartXY = new XYHolder(3 * minwidth - useWidth / 16f, 3 * minwidth - useWidth / 16f);XYHolder processXY = new XYHolder(7 * minwidth - useWidth / 16f, 3 * minwidth - useWidth / 16f);XYHolder lendXY = new XYHolder(7 * minwidth - useWidth / 16f, 7 * minwidth - useWidth / 16f);bounceAnim = ObjectAnimator.ofObject(ballHolder, "xY",new XYEvaluator(), lstartXY, processXY, lendXY, lstartXY);bounceAnim.setDuration(animaltime);bounceAnim.setRepeatCount(ObjectAnimator.INFINITE);bounceAnim.setRepeatMode(ObjectAnimator.RESTART);bounceAnim.addUpdateListener(this);}if (bounceAnim1 == null) {XYHolder lstartXY = new XYHolder(7 * minwidth - useWidth / 16f, 7 * minwidth - useWidth / 16f);XYHolder processXY = new XYHolder(3 * minwidth - useWidth / 16f, 7 * minwidth - useWidth / 16f);XYHolder lendXY = new XYHolder(3 * minwidth - useWidth / 16f, 3 * minwidth - useWidth / 16f);bounceAnim1 = ObjectAnimator.ofObject(ballHolder1, "xY",new XYEvaluator(), lstartXY, processXY, lendXY, lstartXY);bounceAnim1.setDuration(animaltime);bounceAnim1.setRepeatCount(ObjectAnimator.INFINITE);bounceAnim1.setRepeatMode(ObjectAnimator.RESTART);bounceAnim1.addUpdateListener(this);}}

5.两个动画的同步执行

        AnimatorSet animatorSet = new AnimatorSet();animatorSet.play(bounceAnim).with(bounceAnim1);animatorSet.start();

6.效果图


视觉效果:感觉白子不太明显

7.解决第6步问题

在棋子的创建方法中添加渐变色

        RadialGradient gradient = new RadialGradient(useWidth / 16f, useWidth / 16f,useWidth / 8f, color, Color.GRAY, Shader.TileMode.CLAMP);paint.setShader(gradient);shapeHolder.setPaint(paint);

效果图:

五.自定义属性

attrs文件:

    <declare-styleable name="WeiqiView">
<!--        黑子颜色--><attr name="leftscolor" format="reference|color"/>
<!--        白子颜色--><attr name="rightscolor" format="reference|color"/>
<!--        棋盘颜色--><attr name="qipancolor" format="reference|color"/>
<!--        动画时间--><attr name="animalstime" format="integer"/></declare-styleable>

java文件中获取

    /*** 获取自定义属性*/private void initCustomAttrs(Context context, AttributeSet attrs) {//获取自定义属性TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.WeiqiView);//获取颜色leftcolor = ta.getColor(R.styleable.WeiqiView_leftscolor, Color.BLACK);rightcolor = ta.getColor(R.styleable.WeiqiView_rightscolor, Color.WHITE);qipancolor = ta.getColor(R.styleable.WeiqiView_qipancolor, Color.BLACK);//获取动画时间animaltime = ta.getInt(R.styleable.WeiqiView_animalstime, 2000);//回收ta.recycle();}

六.自定义属性设置后运行效果

七.小改变,视觉效果就不一样了!

然后,把背景注释,像不像那些等待动画?

八.源码

WeiqiView.java

public class WeiqiView extends View implements ValueAnimator.AnimatorUpdateListener {private Paint mPaint;private int mWidth;private int mHeight;private int useWidth, minwidth;private int leftcolor;private int rightcolor;private int qipancolor;private int animaltime;//画一个圆(棋子)ValueAnimator bounceAnim, bounceAnim1 = null;ShapeHolder ball, ball1 = null;QiziXYHolder ballHolder, ballHolder1 = null;@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)public WeiqiView(Context context) {this(context, null);}@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)public WeiqiView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)public WeiqiView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {this(context, attrs, defStyleAttr, 0);initCustomAttrs(context, attrs);}@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)public WeiqiView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);}private void init() {initPaint();}/*** 获取自定义属性*/private void initCustomAttrs(Context context, AttributeSet attrs) {//获取自定义属性。TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.WeiqiView);//获取颜色leftcolor = ta.getColor(R.styleable.WeiqiView_leftscolor, Color.BLACK);rightcolor = ta.getColor(R.styleable.WeiqiView_rightscolor, Color.WHITE);qipancolor = ta.getColor(R.styleable.WeiqiView_qipancolor, Color.BLACK);animaltime = ta.getInt(R.styleable.WeiqiView_animalstime, 2000);//回收ta.recycle();}/*** 初始化画笔*/private void initPaint() {mPaint = new Paint();        //创建画笔对象mPaint.setColor(Color.BLACK);    //设置画笔颜色mPaint.setStyle(Paint.Style.FILL); //设置画笔模式为填充mPaint.setStrokeWidth(4f);     //设置画笔宽度为10pxmPaint.setAntiAlias(true);     //设置抗锯齿mPaint.setAlpha(255);        //设置画笔透明度}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mWidth = w;mHeight = h;useWidth = mWidth;if (mWidth > mHeight) {useWidth = mHeight;}}@RequiresApi(api = Build.VERSION_CODES.KITKAT)@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);init();minwidth = useWidth / 10;mPaint.setColor(qipancolor);if (ball == null) {ball = createBall(3 * minwidth, 3 * minwidth, leftcolor);ballHolder = new QiziXYHolder(ball);}if (ball1 == null) {ball1 = createBall(7 * minwidth, 7 * minwidth, rightcolor);ballHolder1 = new QiziXYHolder(ball1);}//细的X轴canvas.drawLine(minwidth, 3 * minwidth, 9 * minwidth, 3 * minwidth, mPaint);// 斜线canvas.drawLine(minwidth, 5 * minwidth, 9 * minwidth, 5 * minwidth, mPaint);// 斜线canvas.drawLine(minwidth, 7 * minwidth, 9 * minwidth, 7 * minwidth, mPaint);// 斜线//细的y轴canvas.drawLine(3 * minwidth, minwidth, 3 * minwidth, 9 * minwidth, mPaint);// 斜线canvas.drawLine(5 * minwidth, minwidth, 5 * minwidth, 9 * minwidth, mPaint);// 斜线canvas.drawLine(7 * minwidth, minwidth, 7 * minwidth, 9 * minwidth, mPaint);// 斜线mPaint.setStrokeWidth(8f);//粗的X轴(边框)canvas.drawLine(minwidth, minwidth, 9 * minwidth, minwidth, mPaint);// 斜线canvas.drawLine(minwidth, 9 * minwidth, 9 * minwidth, 9 * minwidth, mPaint);// 斜线//粗的y轴(边框)canvas.drawLine(minwidth, minwidth, minwidth, 9 * minwidth, mPaint);// 斜线canvas.drawLine(9 * minwidth, minwidth, 9 * minwidth, 9 * minwidth, mPaint);// 斜线//补瑕疵canvas.drawPoint(minwidth, minwidth, mPaint);canvas.drawPoint(9 * minwidth, minwidth, mPaint);canvas.drawPoint(minwidth, 9 * minwidth, mPaint);canvas.drawPoint(9 * minwidth, 9 * minwidth, mPaint);
//        //画围棋
//        canvas.drawCircle(3*minwidth, 3*minwidth, useWidth/16, mPaint);
//        canvas.drawCircle(3*minwidth, 7*minwidth, useWidth/16, mPaint);
//        canvas.drawCircle(5*minwidth, 5*minwidth, useWidth/16, mPaint);
//        canvas.drawCircle(7*minwidth, 3*minwidth, useWidth/16, mPaint);
//        canvas.drawCircle(7*minwidth, 7*minwidth, useWidth/16, mPaint);
//        mPaint.setColor(rightcolor);
//        canvas.drawCircle(3*minwidth, 5*minwidth, useWidth/16, mPaint);
//        canvas.drawCircle(5*minwidth, 3*minwidth, useWidth/16, mPaint);
//        canvas.drawCircle(5*minwidth, 7*minwidth, useWidth/16, mPaint);
//        canvas.drawCircle(7*minwidth, 5*minwidth, useWidth/16, mPaint);canvas.save();canvas.translate(ball.getX(), ball.getY());ball.getShape().draw(canvas);canvas.restore();canvas.save();canvas.translate(ball1.getX(), ball1.getY());ball1.getShape().draw(canvas);canvas.restore();}private ShapeHolder createBall(float x, float y, int color) {OvalShape circle = new OvalShape();circle.resize(useWidth / 8f, useWidth / 8f);ShapeDrawable drawable = new ShapeDrawable(circle);ShapeHolder shapeHolder = new ShapeHolder(drawable);shapeHolder.setX(x - useWidth / 16f);shapeHolder.setY(y - useWidth / 16f);Paint paint = drawable.getPaint();paint.setColor(color);RadialGradient gradient = new RadialGradient(useWidth / 16f, useWidth / 16f,useWidth / 8f, color, Color.GRAY, Shader.TileMode.CLAMP);paint.setShader(gradient);shapeHolder.setPaint(paint);return shapeHolder;}private void createAnimation() {if (bounceAnim == null) {XYHolder lstartXY = new XYHolder(3 * minwidth - useWidth / 16f, 3 * minwidth - useWidth / 16f);XYHolder processXY = new XYHolder(7 * minwidth - useWidth / 16f, 3 * minwidth - useWidth / 16f);XYHolder lendXY = new XYHolder(7 * minwidth - useWidth / 16f, 7 * minwidth - useWidth / 16f);bounceAnim = ObjectAnimator.ofObject(ballHolder, "xY",new XYEvaluator(), lstartXY, processXY, lendXY, lstartXY);bounceAnim.setDuration(animaltime);bounceAnim.setRepeatCount(ObjectAnimator.INFINITE);bounceAnim.setRepeatMode(ObjectAnimator.RESTART);bounceAnim.addUpdateListener(this);}if (bounceAnim1 == null) {XYHolder lstartXY = new XYHolder(7 * minwidth - useWidth / 16f, 7 * minwidth - useWidth / 16f);XYHolder processXY = new XYHolder(3 * minwidth - useWidth / 16f, 7 * minwidth - useWidth / 16f);XYHolder lendXY = new XYHolder(3 * minwidth - useWidth / 16f, 3 * minwidth - useWidth / 16f);bounceAnim1 = ObjectAnimator.ofObject(ballHolder1, "xY",new XYEvaluator(), lstartXY, processXY, lendXY, lstartXY);bounceAnim1.setDuration(animaltime);bounceAnim1.setRepeatCount(ObjectAnimator.INFINITE);bounceAnim1.setRepeatMode(ObjectAnimator.RESTART);bounceAnim1.addUpdateListener(this);}}public void startAnimation() {createAnimation();AnimatorSet animatorSet = new AnimatorSet();animatorSet.play(bounceAnim).with(bounceAnim1);animatorSet.start();}@Overridepublic void onAnimationUpdate(ValueAnimator animation) {invalidate();}
}

QiziXYHolder.java

public class QiziXYHolder {private ShapeHolder mBall;public QiziXYHolder(ShapeHolder ball) {mBall = ball;}public void setXY(XYHolder xyHolder) {mBall.setX(xyHolder.getX());mBall.setY(xyHolder.getY());}public XYHolder getXY() {return new XYHolder(mBall.getX(), mBall.getY());}
}

ShapeHolder.java

public class ShapeHolder {private float x = 0, y = 0;private ShapeDrawable shape;private int color;private RadialGradient gradient;private float alpha = 1f;private Paint paint;public void setPaint(Paint value) {paint = value;}public Paint getPaint() {return paint;}public void setX(float value) {x = value;}public float getX() {return x;}public void setY(float value) {y = value;}public float getY() {return y;}public void setShape(ShapeDrawable value) {shape = value;}public ShapeDrawable getShape() {return shape;}public int getColor() {return color;}public void setColor(int value) {shape.getPaint().setColor(value);color = value;}public void setGradient(RadialGradient value) {gradient = value;}public RadialGradient getGradient() {return gradient;}public void setAlpha(float alpha) {this.alpha = alpha;shape.setAlpha((int)((alpha * 255f) + .5f));}public float getWidth() {return shape.getShape().getWidth();}public void setWidth(float width) {Shape s = shape.getShape();s.resize(width, s.getHeight());}public float getHeight() {return shape.getShape().getHeight();}public void setHeight(float height) {Shape s = shape.getShape();s.resize(s.getWidth(), height);}public ShapeHolder(ShapeDrawable s) {shape = s;}
}

XYEvaluator.java

public class XYEvaluator implements TypeEvaluator {public Object evaluate(float fraction, Object startValue, Object endValue) {XYHolder startXY = (XYHolder) startValue;XYHolder endXY = (XYHolder) endValue;return new XYHolder(startXY.getX() + fraction * (endXY.getX() - startXY.getX()),startXY.getY() + fraction * (endXY.getY() - startXY.getY()));}
}

XYHolder.java

public class XYHolder {private float mX;private float mY;public XYHolder(float x, float y) {mX = x;mY = y;}public float getX() {return mX;}public void setX(float x) {mX = x;}public float getY() {return mY;}public void setY(float y) {mY = y;}
}

attrs.xml

<resources><declare-styleable name="WeiqiView">
<!--        黑子颜色--><attr name="leftscolor" format="reference|color"/>
<!--        白子颜色--><attr name="rightscolor" format="reference|color"/>
<!--        棋盘颜色--><attr name="qipancolor" format="reference|color"/>
<!--        动画时间--><attr name="animalstime" format="integer"/></declare-styleable>
</resources>

布局调用

<com.shenzhen.jimeng.lookui.UI.WeiqiViewandroid:layout_centerInParent="true"android:id="@+id/weiqi"android:layout_width="400dp"android:layout_height="400dp"/>

activity文件中开启动画

     weiqi = (WeiqiView) findViewById(R.id.weiqi);weiqi.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {weiqi.startAnimation();}});

总结

希望对您有所帮助,欢迎留言。如有问题可联系计蒙。

Android自定义view之围棋动画相关推荐

  1. Android自定义view之围棋动画,真牛皮

    @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, ...

  2. android view 渐变动画,Android自定义view渐变圆形动画

    本文实例为大家分享了Android自定义view渐变圆形动画的具体代码,供大家参考,具体内容如下 直接上效果图 自定义属性 attrs.xml文件 创建一个类 ProgressRing继承自 view ...

  3. android 自定义加载动画效果,Android自定义View实现loading动画加载效果

    项目开发中对Loading的处理是比较常见的,安卓系统提供的不太美观,引入第三发又太麻烦,这时候自己定义View来实现这个效果,并且进行封装抽取给项目提供统一的loading样式是最好的解决方式了. ...

  4. Android 自定义View之咖啡杯动画

    效果 大概思路 自定义view,直接继承view 复写onSizeChanged()方法,在此计算杯垫,杯子,烟雾效果的path 在onDraw()方法中,描绘杯垫,杯子 处理烟雾动画效果 画杯子 这 ...

  5. android下雨动画效果,Android 自定义View之下雨动画

    开始前先做个热身( ˘•灬•˘ ) 自己实现比较容易,但是到了要出博客整理思路,总结要点的时候就挠头,不知云所以,所以最简单的还是 如果对安卓UI有兴趣的朋友可以加我好友互相探讨, 思路 思路比较简单 ...

  6. android自定义view之星星动画

    先上效果图 其实上边效果分为几个部分,一个部分是多个小星星四处扩散,第二个部分是一个小星星从小变大,还有一个是实心圆的动画. 代码如下 第一:所有的小星星动画 public class StarVie ...

  7. 一篇文章带你走近Android自定义view

    系列文章目录 一篇文章带你走近Android自定义view 文章目录 系列文章目录 前言 一.为什么要自定义view 二.先看看一个超级简单的自定义view(三个构造函数) 三.了解手机的坐标系 四. ...

  8. android刷新时的圆形动画_Android自定义view渐变圆形动画

    本文实例为大家分享了Android自定义view渐变圆形动画的具体代码,供大家参考,具体内容如下 直接上效果图 自定义属性 attrs.xml文件 创建一个类 ProgressRing继承自 view ...

  9. android录音波浪动画_Android自定义View实现波浪动画

    本文实例为大家分享了Android自定义View实现波浪动画的具体代码,供大家参考,具体内容如下 效果演示 代码调用与实现效果 xml中调用 android:layout_width="ma ...

最新文章

  1. 使用条件卷积进行实例和全景分割
  2. 初学Java的那段日子
  3. 数学篇(三)向量的基本运算
  4. 51Nod- 1915 西湖游船
  5. 超出网络bios会话限制_如何设置网络以防止数据丢失
  6. ConcurrentHashMap的初步使用及场景
  7. MySQL操作实战(三):表联结
  8. .Net Conf 2020 之回顾
  9. sudo spctl --master-disable_量大从优批发--阳离子聚丙烯酰胺--用于生活污水、
  10. tensorflow计算图_通过从头开始模仿其API来了解TensorFlow
  11. 容器编排技术 -- Kubernetes Labels 和 Selectors
  12. VC下ctreectrl的使用方法及节点前图标添加方法
  13. 虚拟机与服务器桥接不能上网,vmware虚拟机桥接模式不能上网
  14. 计算机拆装与维修技能综述,综述虚拟机在计算机硬件组装与维护教学中的应用...
  15. (附源码)小程序 校园二手交易平台 毕业设计 191637
  16. 计算机word怎么插入图片,word如何插入图片 Word2003如何插入电脑中图片
  17. 谷歌浏览器SwitchyOmega插件下载安装
  18. threejs学习笔记:实现导入的动画gltf模型播放动画
  19. AFI - all in - 逍遥棋 - 游戏规则
  20. nginx 反向代理和正向代理区别

热门文章

  1. 3.2.6 Sweet Butter香甜的黄油 USACO
  2. Python pandas库|任凭弱水三千,我只取一瓢饮(3)
  3. 字符串解码(猿辅导笔试题数箱子)
  4. Django 创建第一个项目
  5. 【Java并发】double-checked-locking设计模式
  6. 一个90后对前辈们的“ 宣战”
  7. 新手上路:什么是API接口
  8. PMP 模拟200题
  9. [转帖]windows7/windows NT介绍
  10. python with open方式写入文件出现乱码问题解决