周精益分享 - 动画

Android L动画 - 吴明

  • Touch feedback(触摸反馈)
    • 波纹有边界
    • 波纹超出边界
    • 波纹颜色
  • Reveal effect(揭露效果)
  • liecap
    • Circular Reveal动画
  • Activity transitions(Activity转换效果)
    • Enter(进入)
      • 普通Transition
        • explode:从场景的中心移入或移出
        • slide:从场景的边缘移入或移出
        • fade:调整透明度产生渐变效果
      • Shared Elements Transition 共享元素转换:共享两个 acitivity中共同的元素
        • changeBounds - 改变目标视图的布局边界
        • changeClipBounds - 裁剪目标视图边界
        • changeTransform - 改变目标视图的缩放比例和旋转角度 changeImageTransform - 改变目标图片的大小和缩放比例
    • Exit(退出)
    • demo地址
      Markdown preferences pane

objectorAnimator 动画 王进

动画类型

  • View Animation(Tween Animation 补间动画)
    只能支持简单的缩放、平移、旋转、透明度等基本的动画,且有一定的局限性
    动画时View的真正的View的属性保持不变,实际位置未改变
    原理:提供动画的起始和结束状态信息,中间的状态根据上述类里差值器算法填充
  • Drawable Animation(Frame Animation 帧动画)
  • Property Animation(属性动画)
    它更改的是对象的实际属性,

Property Animation属性

  • Duration:动画的持续时间
  • TimeInterpolation:属性值的计算方式,如先快后慢
  • TypeEvaluator:根据属性的开始、结束值与TimeInterpolation计算出的因子计算出当前时间的属性值
  • Repeat Count and behavoir:重复次数与方式,如播放3次、5次、无限循环,可以此动画一直重复,或播放完时再反向播放
  • Animation sets:动画集合,即可以同时对一个对象应用几个动画,这些动画可以同时播放也可以对不同动画设置不同开始偏移
  • Frame refreash delay:多少时间刷新一次,即每隔多少时间计算一次属性值,默认为10ms,最终刷新时间还受系统进程调度与硬件的影响

Property Animation 动画流程

ValueAnimator

ValueAnimator包含Property Animation动画的所有核心功能,如动画时间,开始、结束属性值,相应时间属性值计算方法等。应用Property Animation有两个步聚:

  1. 计算属性值
  2. 根据属性值执行相应的动作,如改变对象的某一属性。(需要在onAnimationUpdate中传入执行动画的对象)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
    animation.setDuration(1000);
    animation.addUpdateListener(new AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
    Log.i("update", ((Float) animation.getAnimatedValue()).toString());
    //这个函数中会传入ValueAnimator对象做为参数,通过这个ValueAnimator对象的getAnimatedValue()函数可以得到当前的属性值
    }
    });

    animation.setInterpolator(new CycleInterpolator(3));
    animation.start();

ObjectAnimator

ObjectAnimator继承自ValueAnimator,要指定一个对象及该对象的一个属性,例如

  • 常用方法有ofFloat(),ofInt(),ofObject(),ofArgb(),ofPropertyValuesHolder()。
  • 属性动画可用的属性
    答案是:任何一切带有set开头的方法属性名字。可能我们常用的有:

    • 平移 translationX,translationY, X,Y。
    • 缩放 scaleX,scaleY。
    • 旋转 rotationX, rotationY。
    • 透明度 alpha。

    也就是说我们所有控件都有以上setTranslationX(),setScaleX(),setRotationX(),setAlpha()等方法。
    我们不仅限于这几个属性,就拿TextView控件来说,只要是TextView有的属性都可以用来实现动画效果,比如 字体大小:“textColor”,字体颜色“textSize”等。

    限制:对象应该有一个setter函数:set(驼峰命名法)及要有相应属性的getter方法:get
    且应返回值类型应与相应的setter方法的参数类型一致。
    如果上述条件不满足,则不能用ObjectAnimator,应用ValueAnimator代替。

1
2
3
4
5
6
7
ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, 'alpha', 1.0f, 0.3f, 1.0F);
animator.setDuration(2000);//动画时间
animator.setInterpolator(new BounceInterpolator());//动画插值
animator.setRepeatCount(-1);//设置动画重复次数
animator.setRepeatMode(ValueAnimator.RESTART);//动画重复模式
animator.setStartDelay(1000);//动画延时执行
animator.start();//启动动画

根据应用动画的对象或属性的不同,可能需要在onAnimationUpdate函数中调用invalidate()函数刷新视图。

组合动画

  • 组合动画1–AnimatorSet的使用
    这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:

    • after(Animator anim) 将现有动画插入到传入的动画之后执行
    • after(long delay) 将现有动画延迟指定毫秒后执行
    • before(Animator anim) 将现有动画插入到传入的动画之前执行
    • with(Animator anim) 将现有动画和传入的动画同时执行

    Android 除了提供play(),还有playSequentially(),playTogether() 可供使用,可传入一个或者多个动画对象(,隔开),或者动画集合

1
2
3
4
5
6
7
8
9
10
11
ObjectAnimator animator = ObjectAnimator.ofInt(container, "backgroundColor", 0xFFFF0000, 0xFFFF00FF);
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationX", 0.0f, 200.0f, 0f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "scaleX", 1.0f, 2.0f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(view, "rotationX", 0.0f, 90.0f, 0.0F);
ObjectAnimator animator4 = ObjectAnimator.ofFloat(view, "alpha", 1.0f, 0.2f, 1.0F);

//组合动画方式1
AnimatorSet set = new AnimatorSet();
((set.play(animator).with(animator1).before(animator2)).before(animator3)).after(animator4);
set.setDuration(5000);
set.start();
  • 组合动画2–PropertyValuesHolder的使用
    使用方法ObjectAnimator.ofPropertyValuesHolder(Object target,PropertyValuesHolder… values);第一个参数是动画的目标对象,之后的参数是PropertyValuesHolder类的实例,可以有多个这样的实例。代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    PropertyValuesHolder valuesHolder = PropertyValuesHolder.ofFloat("translationX", 0.0f, 300.0f);
    PropertyValuesHolder valuesHolder1 = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 1.5f);
    PropertyValuesHolder valuesHolder2 = PropertyValuesHolder.ofFloat("rotationX", 0.0f, 90.0f, 0.0F);
    PropertyValuesHolder valuesHolder3 = PropertyValuesHolder.ofFloat("alpha", 1.0f, 0.3f, 1.0F);

    ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(view, valuesHolder, valuesHolder1, valuesHolder2, valuesHolder3);
    objectAnimator.setDuration(2000).start();
    //类似于AnimatorSet.playTogether(Animator... items);
  • 组合动画3-ViewPropertyAnimator(多属性动画)

    1
    2
    ViewPropertyAnimator animator5 = imageView.animate();
    animator5.translationX(200).scaleX(2).setDuration(2000).start();

注意:使用ViewPropertyAnimator类需要API>=12

动画监听

  • animator.addListener(new Animator.AnimatorListener(){});//监听动画开始,结束,取消,重复(四种都包括)
  • animator.addListener(new AnimatorListenerAdapter(){});
    推荐,可代替AnimatorListener,需要监听动画开始,结束,取消,重复那种就直接实现那种方法就行
    其实AnimatorListenerAdapter的源码只是一个实现了AnimatorListener接口的抽象类而已
  • animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){});
    更加精确的方法来时刻监听当前动画的执行情况,可以读取到动画的每个更新值了
    1
    2
    3
    4
    5
    6
    7
    8
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {

    float value = (float) animation.getAnimatedValue();
    //可以根据自己的需要来获取动画更新值。
    Log.e('TAG', 'the animation value is ' + value);
    }
    });

Keyframes

keyFrame是一个 时间/值 对,通过它可以定义一个在特定时间的特定状态,即关键帧,而且在两个keyFrame之间可以定义不同的Interpolator,就好像多个动画的拼接,第一个动画的结束点是第二个动画的开始点。KeyFrame是抽象类,要通过ofInt(),ofFloat(),ofObject()获得适当的KeyFrame,然后通过PropertyValuesHolder.ofKeyframe获得PropertyValuesHolder对象,如以下例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* 动画效果:btn对象的width属性值使其:
* 开始时 Width=400
* 动画开始1/4时 Width=200
* 动画开始1/2时 Width=400
* 动画开始3/4时 Width=100
* 动画结束时 Width=500
*/

Keyframe kf0 = Keyframe.ofInt(0, 400);
Keyframe kf1 = Keyframe.ofInt(0.25f, 200);
Keyframe kf2 = Keyframe.ofInt(0.5f, 400);
Keyframe kf4 = Keyframe.ofInt(0.75f, 100);
Keyframe kf3 = Keyframe.ofInt(1f, 500);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("width", kf0, kf1, kf2, kf4, kf3);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(btn2, pvhRotation);
rotationAnim.setDuration(2000);

Property Animation在XML中使用

  • xml文件放在res/animator/中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <set xmlns:android='http://schemas.android.com/apk/res/android'
    android:duration='2000'
    android:ordering='sequentially'><!--动画执行顺序 sequentially:顺序执行;together:同时执行。 -->


    <objectAnimator
    android:propertyName='translationX'
    android:valueFrom='0'
    android:valueTo='200'
    android:valueType='floatType' />


    <set android:ordering='together'>
    <objectAnimator
    android:propertyName='scaleX'
    android:valueFrom='1'
    android:valueTo='2'
    android:valueType='floatType' />

    <objectAnimator
    android:propertyName='rotationX'
    android:valueFrom='0'
    android:valueTo='90'
    android:valueType='floatType' /><!--动画值的类型-->


    </set>
  • 通过AnimatorInflater.loadAnimator方法加载xml动画返回一个Animator的对象,然后调用setTarget方法给动画设置对象调用哪个start启动动画即可完成xml动画效果

    1
    2
    3
    Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);
    animator.setTarget(view);
    animator.start();

花花绿绿的ProgressBar 杨俊构

参考Android 基础入门教程

  • ProgressBar(进度条)是Android基本UI控件,ProgressBar的应用场景很多,比如 用户登录时,后台在发请求,以及等待服务器返回信息,这个时候会用到进度条;或者当在进行一些比较 耗时的操作,需要等待一段较长的时间,这个时候如果没有提示,用户可能会以为程序Carsh或者手机死机 了,这样会大大降低用户体验,所以在需要进行耗时操作的地方,添加上进度条,让用户知道当前的程序 在执行中,也可以直观的告诉用户当前任务的执行进度等!使用进度条可以给我带来这样的便利!
  • ProgressBar官方API文档,ProgressBar继承与View类,直接子类有AbsSeekBar和ContentLoadingProgressBar, 其中AbsSeekBar的子类有SeekBar和RatingBar,可见这二者也是基于ProgressBar实现的

  • 常用属性详解:
  • android:max:进度条的最大值
  • android:progress:进度条已完成进度值
  • android:progressDrawable:设置轨道对应的Drawable对象
  • android:indeterminate:如果设置成true,则进度条不精确显示进度
  • android:indeterminateDrawable:设置不显示进度的进度条的Drawable对象
  • android:indeterminateDuration:设置不精确显示进度的持续时间
  • android:secondaryProgress:二级进度条,类似于视频播放的一条是当前播放进度,一条是缓冲进度,前者通过progress属性进行设置!
  • 对应的再Java中我们可调用下述方法:
  • getMax():返回这个进度条的范围的上限
  • getProgress():返回进度
  • getSecondaryProgress():返回次要进度
  • incrementProgressBy(int diff):指定增加的进度
  • isIndeterminate():指示进度条是否在不确定模式下
  • setIndeterminate(boolean indeterminate):设置不确定模式下
  • 系统默认进度条使用实例:

耀眼的IOS的ProgressBar ,参看开源中国IOS代码库

圆形进度条

网上一个简单的自定义圆形进度条!代码还是比较简单,容易理解,有兴趣可以看看,或者进行相关扩展~

  • 实现代码:自定义View类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

/**
* Created by Jay on 2015/8/5 0005.
*/

public class CirclePgBar extends View {


private Paint mBackPaint;
private Paint mFrontPaint;
private Paint mTextPaint;
private float mStrokeWidth = 50;
private float mHalfStrokeWidth = mStrokeWidth / 2;
private float mRadius = 200;
private RectF mRect;
private int mProgress = 0;
//目标值,想改多少就改多少
private int mTargetProgress = 90;
private int mMax = 100;
private int mWidth;
private int mHeight;


public CirclePgBar(Context context) {
super(context);
init();
}

public CirclePgBar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public CirclePgBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}


//完成相关参数初始化
private void init() {
mBackPaint = new Paint();
mBackPaint.setColor(Color.WHITE);
mBackPaint.setAntiAlias(true);
mBackPaint.setStyle(Paint.Style.STROKE);
mBackPaint.setStrokeWidth(mStrokeWidth);

mFrontPaint = new Paint();
mFrontPaint.setColor(Color.GREEN);
mFrontPaint.setAntiAlias(true);
mFrontPaint.setStyle(Paint.Style.STROKE);
mFrontPaint.setStrokeWidth(mStrokeWidth);


mTextPaint = new Paint();
mTextPaint.setColor(Color.GREEN);
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(80);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}


//重写测量大小的onMeasure方法和绘制View的核心方法onDraw()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getRealSize(widthMeasureSpec);
mHeight = getRealSize(heightMeasureSpec);
setMeasuredDimension(mWidth, mHeight);

}


@Override
protected void onDraw(Canvas canvas) {
initRect();
float angle = mProgress / (float) mMax * 360;
canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius, mBackPaint);
canvas.drawArc(mRect, -90, angle, false, mFrontPaint);
canvas.drawText(mProgress + "%", mWidth / 2 + mHalfStrokeWidth, mHeight / 2 + mHalfStrokeWidth, mTextPaint);
if (mProgress < mTargetProgress) {
mProgress += 1;
invalidate();
}

}

public int getRealSize(int measureSpec) {
int result = 1;
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);

if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.UNSPECIFIED) {
//自己计算
result = (int) (mRadius * 2 + mStrokeWidth);
} else {
result = size;
}

return result;
}

private void initRect() {
if (mRect == null) {
mRect = new RectF();
int viewSize = (int) (mRadius * 2);
int left = (mWidth - viewSize) / 2;
int top = (mHeight - viewSize) / 2;
int right = left + viewSize;
int bottom = top + viewSize;
mRect.set(left, top, right, bottom);
}
}


}
  • 然后在布局文件中加上:
1
2
3
4

<com.jay.progressbardemo.CirclePgBar
android:layout_width="match_parent"
android:layout_height="match_parent"/>

总结

  • progressbar是Android中的常用控件,在实际开发中和动画结合较多,好的进度条可以缓解使用者焦躁的情趣,自定义圆形进度条可以自行完善,然后用到实际开发中!

layer-list实现阴影效果——xpleemoon

  • 为控件实现阴影效果,可以有多种方式:

    • 多个drawable层叠在一起(不好的实现是多个View层叠达到多个drawable的层叠效果,相对好的实现是在同一个View钟实现多个drawable的层叠)
    • 自定义view
    • Material Design中设置Z轴的方式
  • 本文的思路是多个drawable叠在一起,但是不额外使用View,通过layer-list可以将多个item按照顺序层叠在一起显示。首先来看效果图:

LayerShadow

  • 第一个和第二个控件是用来展示layer-list实现阴影效果的基本款,而第三个控件是综合上述两个控件效果,再集合selector实现的。

  • 默认状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 阴影:左偏移2dp,上偏移4dp -->
<item
android:left="2dp"
android:top="4dp">

<shape>
<solid android:color="@android:color/holo_blue_dark" />
<corners android:radius="10dp" />
</shape>
</item>
<!-- 前景::底偏移4dp,右偏移2dp -->
<item
android:bottom="4dp"
android:right="2dp">

<shape>
<solid android:color="@android:color/holo_blue_bright" />
<corners android:radius="10dp" />
</shape>
</item>
</layer-list>
  • 点击状态:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 为了达到点击的真实感,将原来默认状态的前景色设置为阴影,并将前景设为无透明-->
<item
android:left="2dp"
android:top="4dp">

<shape>
<solid android:color="@android:color/holo_blue_bright" />
<corners android:radius="10dp" />
</shape>
</item>
<item
android:bottom="4dp"
android:right="2dp">

<shape>
<corners android:radius="10dp" />
</shape>
</item>
</layer-list>

layer-list的item可以通过以下属性设置偏移量:

  • android:top 顶部的偏移量
  • android:bottom 底部的偏移量
  • android:left 左边的偏移量
  • android:right 右边的偏移量
  • selector,使用上述layer-list:
1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/layer_list_btn_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/layer_list_btn_pressed" android:state_selected="true" />
<item android:drawable="@drawable/layer_list_btn" />

</selector>
  • 最后再来看下,布局代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">


<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:background="@drawable/layer_list_btn"
android:clickable="true"
android:gravity="center"
android:text="默认状态" />


<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:background="@drawable/layer_list_btn_pressed"
android:clickable="true"
android:gravity="center"
android:text="点击状态" />


<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:background="@drawable/selector_btn"
android:clickable="true"
android:gravity="center"
android:text="点我" />

</LinearLayout>
  • 第一个和第二个TextView分别引用了对应的layer-list(默认和点击)作为背景,第三个引用了selector

  • 源码地址

零部署的云服务 - 王胜

本周的分享主题是动画,但之前7月份我已经分享过一次,知其所以然 。再加上前面几位已经对android的动画介绍的很完整了,所以我就分享另一个话题『云平台下的零部署开发网站』。

牵扯的技能

  • Git
  • 任何一门后端开发语言

搭建步骤

AZURE -> 点击 Web应用模块 -> 选择语言,点击创建Web应用 -> 选择模板,点击创建,至此,应用以创建,点击页面上站点url,就能看到新创建的Web站点了。

修改代码,Git提交自动完成部署

复制创建成功页面『使用 Git 克隆或推送』里的Git地址,将初始化源码clone到本地,然后编辑代码,修改功能。完成后,直接push到远程,云平台自动完成部署。再看看网页是不是已经发生变化了。

1
Victors-MPB:azure wangsheng$ git clone https://$4f743e5d-0ee0-4-231-b9ee:WSc8Szx4Rf30apo3Ky7hpw6mXAadAZYvn1zagCvMaboj7mHFyJ2vzuYn9i12@4f743e5d-0ee0-4-231-b9ee.scm.azurewebsites.net/4f743e5d-0ee0-4-231-b9ee.git
Cloning into '4f743e5d-0ee0-4-231-b9ee'...
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (4/4), done.
Checking connectivity... done.
Victors-MPB:azure wangsheng$ ls
4f743e5d-0ee0-4-231-b9ee
Victors-MPB:azure wangsheng$ cd 4f743e5d-0ee0-4-231-b9ee/
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ git br
* master
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ git log
commit cf6edda65210ef7254f9bc545c778489f123620e
Author: windowsazure <windowsazure>
Date:   Fri Nov 20 03:53:00 2015 +0000

    Initial Commit
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ ls
favicon.ico	index.php
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ vi index.php
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ git st
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   index.php

no changes added to commit (use "git add" and/or "git commit -a")
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ git diff
diff --git a/index.php b/index.php
<?php
index 712f825..50a17e7 100644
--- a/index.php
+++ b/index.php
@@ -1,3 +1,4 @@
 <?php
     echo "Hello World"
-?>
\ No newline at end of file
+    echo "hack it."
+?>
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ git ci -am "add one line."
[master 6c5551f] add one line.
 1 file changed, 2 insertions(+), 1 deletion(-)
Victors-MPB:4f743e5d-0ee0-4-231-b9ee wangsheng$ git push origin master
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 324 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Updating branch 'master'.
remote: Updating submodules.
remote: Preparing deployment for commit id '6c5551f090'.
remote: Generating deployment script.
remote: Generating deployment script for Web Site
remote: Generated deployment script files
remote: Running deployment command...
remote: Handling Basic Web Site deployment.
remote: Finished successfully.
remote: Deployment successful.

对,后端开发可以如此简单快乐地开发部署!!!