英文: Twitter’s like animation in Android - alternative
相关动画网址:http://frogermcs.github.io/twitters-like-animation-in-android-alternative/,
更多动画网址:http://frogermcs.github.io/
作者来源:Miroslaw Stanek

小编自己遇到的问题:最近在研究一个Twitter的like动画安卓版,在任何手机上面都可以正常播放动画,只有在一个htc的手机上面,没有任何错误日志,动画播放不出来
操作:设置–》开发者选择–》高级选项–》动画,把动画的选项打开就好了。

不久前Twitter展示了具有现代感的心形动画-作为star图标的替代。


虽然实现这个动画最简单的方法是使用 Frame Animation ,但是我们尝试用更灵活的方法来实现-手动绘制并用属性动画。这篇文章只是概要,没有深入的技术细节

一:实现

我们将创建一个名叫LikeButtonView的view,它是一个由三个子view构成的FrameLayout- CircleView 显示星星图标下面的圆,ImageView (星星)以及代表按钮周围浮点的DotsView 。

CircleView


这个视图负责绘制星星图标下面的大圆。它本可以实现得更简单(通过xml ),但是这里我们应该考虑按钮下面的背景颜色。

我们在canvas上绘制圆的实现:

Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);tempCanvas.drawColor(0xffffff, PorterDuff.Mode.CLEAR);tempCanvas.drawCircle(getWidth() / 2, getHeight() / 2, outerCircleRadiusProgress * maxCircleSize, circlePaint);tempCanvas.drawCircle(getWidth() / 2, getHeight() / 2, innerCircleRadiusProgress * maxCircleSize, maskPaint);canvas.drawBitmap(tempBitmap, 0, 0, null);
}

先使用CLEAR 模式绘制颜色以清除canvas。然后根据给定的进度(各自的进度是独立的)绘制内外圆。

内圆使用这样定义的mask paint :

Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);maxCircleSize = w / 2;tempBitmap = Bitmap.createBitmap(getWidth(), getWidth(), Bitmap.Config.ARGB_8888);tempCanvas = new Canvas(tempBitmap);
}

我们需要完全透明,不然的话内圆就会显示窗口颜色。

对于那些眼睛机灵的人应该还注意到了另外一件事-我们的外圆颜色是基于当前进度而变化的。这是通过 ArgbEvaluator 类来完成,该类可以基于一个给定的因子在两个颜色之间变换:

private void updateCircleColor() {float colorProgress = (float) Utils.clamp(outerCircleRadiusProgress, 0.5, 1);colorProgress = (float) Utils.mapValueFromRangeToRange(colorProgress, 0.5f, 1f, 0f, 1f);this.circlePaint.setColor((Integer) argbEvaluator.evaluate(colorProgress, START_COLOR, END_COLOR));
}

三:DotsView


这个view将绘制浮动在星星图标周围的圆点。跟CircleView一样,它是使用onDraw()来做这件事的:

protected void onDraw(Canvas canvas) {drawOuterDotsFrame(canvas);drawInnerDotsFrame(canvas);
}private void drawOuterDotsFrame(Canvas canvas) {for (int i = 0; i < DOTS_COUNT; i++) {int cX = (int) (centerX + currentRadius1 * Math.cos(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));int cY = (int) (centerY + currentRadius1 * Math.sin(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));canvas.drawCircle(cX, cY, currentDotSize1, circlePaints[i % circlePaints.length]);}
}private void drawInnerDotsFrame(Canvas canvas) {for (int i = 0; i < DOTS_COUNT; i++) {int cX = (int) (centerX + currentRadius2 * Math.cos((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180));int cY = (int) (centerY + currentRadius2 * Math.sin((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180));canvas.drawCircle(cX, cY, currentDotSize2, circlePaints[(i + 1) % circlePaints.length]);}
}

圆点是基于currentProgress绘制的,背后是数学逻辑,老实说从安卓sdk的角度来看这里没有什么有趣的地方,倒是有两个跟数学相关的东西:

圆点分布在一个圆上-它们的位置决定于:

int cX = (int) (centerX + currentRadius1 * Math.cos(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));

int cY = (int) (centerY + currentRadius1 * Math.sin(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));

意味着:在每个 OUTER_DOTS_POSITION_ANGLE 上设置圆点 (51 度).

每个圆点都有它自己的颜色动画:

private void updateDotsPaints() {if (currentProgress < 0.5f) {float progress = (float) Utils.mapValueFromRangeToRange(currentProgress, 0f, 0.5f, 0, 1f);circlePaints[0].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_1, COLOR_2));circlePaints[1].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_2, COLOR_3));circlePaints[2].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_3, COLOR_4));circlePaints[3].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_4, COLOR_1));} else {float progress = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.5f, 1f, 0, 1f);circlePaints[0].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_2, COLOR_3));circlePaints[1].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_3, COLOR_4));circlePaints[2].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_4, COLOR_1));circlePaints[3].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_1, COLOR_2));}
}

这意味着圆点颜色在3个区间形式的值之间动画。我们再一次使用ArgbEvaluator 让它平滑。其余就很简单了
CircleView完整代码

package com.loveta.umengexample;import android.animation.ArgbEvaluator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Property;
import android.view.View;/*** Created by Miroslaw Stanek on 21.12.2015.*/
public class CircleView extends View {private static final int START_COLOR = 0xFFFF5722;private static final int END_COLOR = 0xFFFFC107;private ArgbEvaluator argbEvaluator = new ArgbEvaluator();private Paint circlePaint = new Paint();private Paint maskPaint = new Paint();private Bitmap tempBitmap;private Canvas tempCanvas;private float outerCircleRadiusProgress = 0f;private float innerCircleRadiusProgress = 0f;private int maxCircleSize;public CircleView(Context context) {super(context);init();}public CircleView(Context context, AttributeSet attrs) {super(context, attrs);init();}public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}public CircleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);init();}private void init() {
//        Log.e("==tjj", "============");circlePaint.setStyle(Paint.Style.FILL);//实心园maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));//PorterDuff.Mode.CLEAR---所绘制不会提交到画布上。http://blog.csdn.net/edisonlg/article/details/7084977}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);maxCircleSize = w / 2;tempBitmap = Bitmap.createBitmap(getWidth(), getWidth(), Bitmap.Config.ARGB_8888);tempCanvas = new Canvas(tempBitmap);}@Overrideprotected void onDraw(Canvas canvas) {//视图负责绘制星星图标下面的大圆super.onDraw(canvas);Log.e("==========tan111===", "tan111");tempCanvas.drawColor(0xffffff, PorterDuff.Mode.CLEAR);//设置画布的背景颜色tempCanvas.drawCircle(getWidth() / 2, getHeight() / 2, outerCircleRadiusProgress * maxCircleSize, circlePaint);tempCanvas.drawCircle(getWidth() / 2, getHeight() / 2, innerCircleRadiusProgress * maxCircleSize, maskPaint);canvas.drawBitmap(tempBitmap, 0, 0, null);}public void setInnerCircleRadiusProgress(float innerCircleRadiusProgress) {this.innerCircleRadiusProgress = innerCircleRadiusProgress;
//        Log.e("==========tan===", "tan");postInvalidate();
//        invalidate();}public float getInnerCircleRadiusProgress() {return innerCircleRadiusProgress;}public void setOuterCircleRadiusProgress(float outerCircleRadiusProgress) {this.outerCircleRadiusProgress = outerCircleRadiusProgress;updateCircleColor();postInvalidate();
//        invalidate();}private void updateCircleColor() {float colorProgress = (float) Utils.clamp(outerCircleRadiusProgress, 0.5, 1);colorProgress = (float) Utils.mapValueFromRangeToRange(colorProgress, 0.5f, 1f, 0f, 1f);this.circlePaint.setColor((Integer) argbEvaluator.evaluate(colorProgress, START_COLOR, END_COLOR));}public float getOuterCircleRadiusProgress() {return outerCircleRadiusProgress;}public static final Property<CircleView, Float> INNER_CIRCLE_RADIUS_PROGRESS =new Property<CircleView, Float>(Float.class, "innerCircleRadiusProgress") {@Overridepublic Float get(CircleView object) {return object.getInnerCircleRadiusProgress();}@Overridepublic void set(CircleView object, Float value) {
//                    Log.e("==================tjj===","INNER_CIRCLE_RADIUS_PROGRESS");object.setInnerCircleRadiusProgress(value);}};public static final Property<CircleView, Float> OUTER_CIRCLE_RADIUS_PROGRESS =new Property<CircleView, Float>(Float.class, "outerCircleRadiusProgress") {@Overridepublic Float get(CircleView object) {return object.getOuterCircleRadiusProgress();}@Overridepublic void set(CircleView object, Float value) {object.setOuterCircleRadiusProgress(value);}};
}

DotsView完整代码

package com.loveta.umengexample;import android.animation.ArgbEvaluator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Property;
import android.view.View;/*** Created by Miroslaw Stanek on 20.12.2015.*/
public class DotsView extends View {private static final int DOTS_COUNT = 7;private static final int OUTER_DOTS_POSITION_ANGLE = 360 / DOTS_COUNT;private static final int COLOR_1 = 0xFFFFC107;private static final int COLOR_2 = 0xFFFF9800;private static final int COLOR_3 = 0xFFFF5722;private static final int COLOR_4 = 0xFFF44336;private final Paint[] circlePaints = new Paint[4];private int centerX;private int centerY;private float maxOuterDotsRadius;private float maxInnerDotsRadius;private float maxDotSize;private float currentProgress = 0;private float currentRadius1 = 0;private float currentDotSize1 = 0;private float currentDotSize2 = 0;private float currentRadius2 = 0;private ArgbEvaluator argbEvaluator = new ArgbEvaluator();public DotsView(Context context) {super(context);init();}public DotsView(Context context, AttributeSet attrs) {super(context, attrs);init();}public DotsView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}public DotsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);init();}private void init() {for (int i = 0; i < circlePaints.length; i++) {circlePaints[i] = new Paint();circlePaints[i].setStyle(Paint.Style.FILL);}}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);centerX = w / 2;centerY = h / 2;maxDotSize = 20;maxOuterDotsRadius = w / 2 - maxDotSize * 2;maxInnerDotsRadius = 0.8f * maxOuterDotsRadius;}@Overrideprotected void onDraw(Canvas canvas) {//绘制浮动在星星图标周围的圆点drawOuterDotsFrame(canvas);drawInnerDotsFrame(canvas);}private void drawOuterDotsFrame(Canvas canvas) {for (int i = 0; i < DOTS_COUNT; i++) {int cX = (int) (centerX + currentRadius1 * Math.cos(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));int cY = (int) (centerY + currentRadius1 * Math.sin(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));canvas.drawCircle(cX, cY, currentDotSize1, circlePaints[i % circlePaints.length]);}}private void drawInnerDotsFrame(Canvas canvas) {for (int i = 0; i < DOTS_COUNT; i++) {int cX = (int) (centerX + currentRadius2 * Math.cos((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180));int cY = (int) (centerY + currentRadius2 * Math.sin((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180));canvas.drawCircle(cX, cY, currentDotSize2, circlePaints[(i + 1) % circlePaints.length]);}}public void setCurrentProgress(float currentProgress) {this.currentProgress = currentProgress;updateInnerDotsPosition();//1updateOuterDotsPosition();//2updateDotsPaints();//3updateDotsAlpha();//4postInvalidate();//1}public float getCurrentProgress() {return currentProgress;}private void updateInnerDotsPosition() {if (currentProgress < 0.3f) {this.currentRadius2 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0, 0.3f, 0.f, maxInnerDotsRadius);} else {this.currentRadius2 = maxInnerDotsRadius;}if (currentProgress < 0.2) {this.currentDotSize2 = maxDotSize;} else if (currentProgress < 0.5) {this.currentDotSize2 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.2f, 0.5f, maxDotSize, 0.3 * maxDotSize);} else {this.currentDotSize2 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.5f, 1f, maxDotSize * 0.3f, 0);}}private void updateOuterDotsPosition() {if (currentProgress < 0.3f) {this.currentRadius1 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.0f, 0.3f, 0, maxOuterDotsRadius * 0.8f);} else {this.currentRadius1 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.3f, 1f, 0.8f * maxOuterDotsRadius, maxOuterDotsRadius);}if (currentProgress < 0.7) {this.currentDotSize1 = maxDotSize;} else {this.currentDotSize1 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.7f, 1f, maxDotSize, 0);}}private void updateDotsPaints() {if (currentProgress < 0.5f) {float progress = (float) Utils.mapValueFromRangeToRange(currentProgress, 0f, 0.5f, 0, 1f);circlePaints[0].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_1, COLOR_2));circlePaints[1].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_2, COLOR_3));circlePaints[2].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_3, COLOR_4));circlePaints[3].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_4, COLOR_1));} else {float progress = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.5f, 1f, 0, 1f);circlePaints[0].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_2, COLOR_3));circlePaints[1].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_3, COLOR_4));circlePaints[2].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_4, COLOR_1));circlePaints[3].setColor((Integer) argbEvaluator.evaluate(progress, COLOR_1, COLOR_2));}}private void updateDotsAlpha() {float progress = (float) Utils.clamp(currentProgress, 0.6f, 1f);int alpha = (int) Utils.mapValueFromRangeToRange(progress, 0.6f, 1f, 255, 0);circlePaints[0].setAlpha(alpha);circlePaints[1].setAlpha(alpha);circlePaints[2].setAlpha(alpha);circlePaints[3].setAlpha(alpha);}public static final Property<DotsView, Float> DOTS_PROGRESS = new Property<DotsView, Float>(Float.class, "dotsProgress") {@Overridepublic Float get(DotsView object) {return object.getCurrentProgress();}@Overridepublic void set(DotsView object, Float value) {object.setCurrentProgress(value);}};
}

四:LikeButtonView

最终的ViewGroup是由CircleView, ImageView 以及DotsView组成的。

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><frogermcs.io.likeanimation.DotsView
        android:id="@+id/vDotsView"android:layout_width="200dp"android:layout_height="200dp"android:layout_gravity="center"/><frogermcs.io.likeanimation.CircleView
        android:id="@+id/vCircle"android:layout_width="80dp"android:layout_height="80dp"android:layout_gravity="center"/><ImageView
        android:id="@+id/ivStar"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:src="@drawable/ic_star_rate_off"/></merge>

我们使用 Merge 标签帮助消除多余的ViewGroup。LikeButtonView本身就是一个FrameLayout,因此没有必要出现两次。

我们最终的动画是由更小的动画组成的,通过AnimatorSet一起播放:
LikeButtonView完整代码

package com.loveta.umengexample;import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;import butterknife.Bind;
import butterknife.ButterKnife;/*** Created by Miroslaw Stanek on 20.12.2015.*/
public class LikeButtonView extends FrameLayout implements View.OnClickListener {private static final DecelerateInterpolator DECCELERATE_INTERPOLATOR = new DecelerateInterpolator();private static final AccelerateDecelerateInterpolator ACCELERATE_DECELERATE_INTERPOLATOR = new AccelerateDecelerateInterpolator();private static final OvershootInterpolator OVERSHOOT_INTERPOLATOR = new OvershootInterpolator(4);@Bind(R.id.ivStar)ImageView ivStar;@Bind(R.id.vDotsView)DotsView vDotsView;@Bind(R.id.vCircle)CircleView vCircle;private boolean isChecked;private AnimatorSet animatorSet;public LikeButtonView(Context context) {super(context);init();}public LikeButtonView(Context context, AttributeSet attrs) {super(context, attrs);init();}public LikeButtonView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}@TargetApi(Build.VERSION_CODES.LOLLIPOP)public LikeButtonView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);init();}private void init() {LayoutInflater.from(getContext()).inflate(R.layout.view_like_button, this, true);ButterKnife.bind(this);setOnClickListener(this);}@Overridepublic void onClick(View v) {
//        Log.e("==tjj======",v.getId()+"");isChecked = !isChecked;ivStar.setImageResource(isChecked ? R.drawable.smart11 : R.drawable.smart1);if (animatorSet != null) {animatorSet.cancel();}if (isChecked) {ivStar.animate().cancel();ivStar.setScaleX(0);ivStar.setScaleY(0);Log.e("====1111=======", "setCurrentProgress");vCircle.setInnerCircleRadiusProgress(0);vCircle.setOuterCircleRadiusProgress(0);vDotsView.setCurrentProgress(0);animatorSet = new AnimatorSet();ObjectAnimator outerCircleAnimator = ObjectAnimator.ofFloat(vCircle, CircleView.OUTER_CIRCLE_RADIUS_PROGRESS, 0.1f, 1f);outerCircleAnimator.setDuration(250);outerCircleAnimator.setInterpolator(DECCELERATE_INTERPOLATOR);ObjectAnimator innerCircleAnimator = ObjectAnimator.ofFloat(vCircle, CircleView.INNER_CIRCLE_RADIUS_PROGRESS, 0.1f, 1f);innerCircleAnimator.setDuration(200);innerCircleAnimator.setStartDelay(200);innerCircleAnimator.setInterpolator(DECCELERATE_INTERPOLATOR);ObjectAnimator starScaleYAnimator = ObjectAnimator.ofFloat(ivStar, ImageView.SCALE_Y, 0.2f, 1f);starScaleYAnimator.setDuration(350);starScaleYAnimator.setStartDelay(250);starScaleYAnimator.setInterpolator(OVERSHOOT_INTERPOLATOR);ObjectAnimator starScaleXAnimator = ObjectAnimator.ofFloat(ivStar, ImageView.SCALE_X, 0.2f, 1f);starScaleXAnimator.setDuration(350);starScaleXAnimator.setStartDelay(250);starScaleXAnimator.setInterpolator(OVERSHOOT_INTERPOLATOR);ObjectAnimator dotsAnimator = ObjectAnimator.ofFloat(vDotsView, DotsView.DOTS_PROGRESS, 0, 1f);dotsAnimator.setDuration(900);dotsAnimator.setStartDelay(50);dotsAnimator.setInterpolator(ACCELERATE_DECELERATE_INTERPOLATOR);animatorSet.playTogether(outerCircleAnimator,innerCircleAnimator,starScaleYAnimator,starScaleXAnimator,dotsAnimator);animatorSet.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationCancel(Animator animation) {vCircle.setInnerCircleRadiusProgress(0);vCircle.setOuterCircleRadiusProgress(0);
//                    Log.e("====2222=======", "setCurrentProgress");vDotsView.setCurrentProgress(0);ivStar.setScaleX(1);ivStar.setScaleY(1);}});animatorSet.start();}}//LikeButtonView还会响应触摸事件(缩放动画):@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:ivStar.animate().scaleX(0.7f).scaleY(0.7f).setDuration(150).setInterpolator(DECCELERATE_INTERPOLATOR);setPressed(true);break;case MotionEvent.ACTION_MOVE:float x = event.getX();float y = event.getY();boolean isInside = (x > 0 && x < getWidth() && y > 0 && y < getHeight());if (isPressed() != isInside) {setPressed(isInside);}break;case MotionEvent.ACTION_UP:ivStar.animate().scaleX(1).scaleY(1).setInterpolator(DECCELERATE_INTERPOLATOR);if (isPressed()) {performClick();setPressed(false);}break;}return true;}}

源代码可以在github上面得到:https://github.com/frogermcs/LikeAnimation/

Twitter的like动画安卓版 - 备选方案相关推荐

  1. 小学数学动画 android,小学数学动画教学下载-小学数学动画 安卓版v5.0-pc6手机下载...

    小学数学动画教学软件是一款能让孩子爱上数学的客户端应用,小学数学动画app以动画的形式带领孩子学习数学知识以及各类公式原理,测底掌握数学方法. 功能介绍 小学数学动画通过形象.生动.清楚.易懂的触摸动 ...

  2. 定格动画android,定格动画安卓版下载-定格动画最新版下载v2.2.2-一听下载站

    定格动画是一个针对喜欢剪辑的用户们创作的软件,各种的是一个制作定格动画,这个软件让喜欢制作定格动画的用户们在手机上也能够进行使用,是所有需要制作定格动画那一个必备助手,整个的操作也是非常的简单,只需要 ...

  3. android霓虹灯跑的动画,霓虹灯赛跑安卓版

    霓虹灯赛跑是一款十分独特的跑酷类手游.游戏中拥有非常炫酷的画面场景,内容极其丰富,海量关卡等你来玩!不过要小心!这里四处都隐藏着危险,你要做的就是躲避他们,最终到达终点!超多有趣的道具能够帮助你更加顺 ...

  4. android微信个人界面设计,安卓版微信主界面的再设计

    聊天界面 前段时间刚刚上手Axure,因为某些契机,根据自己主观需求重新设计了安卓版微信界面.与现在版本区别主要是更加符合安卓的设计原则,同时对"发现"以及"我" ...

  5. 首款AI看球机器人亮相北京,已上线IOS版和安卓版

    3月9日,魔方元科技在北京举办"机器人陪你看世界杯"为主题的产品沟通分享会,发布了其自主研发的产品"AI球".据悉,"AI球"是首款立足于足 ...

  6. Facebook 开源安卓版 React Native,开发者可将相同代码用于网页和 iOS 应用开发

    转自:http://mt.sohu.com/20150915/n421177212.shtml Facebook 创建了React Java 库,这样,Facebook 的工程团队就可以用相同的代码给 ...

  7. live2dviewer android,live2dviewerex安卓版

    live2dviewerex安卓版收集了非常丰富的壁纸,可以让用户每天都用上与众不同的壁纸,不仅如此,还附带全新的玩法,好玩又好看,让你的手机变得更加美观,各位用户赶紧来试试看吧! live2dvie ...

  8. 连点器安卓手机版_鼠大侠手机版下载-鼠大侠鼠标连点器手机版下载 v1.4 安卓版...

    鼠大侠app是个正宗的免root的安卓连点器,让各位摆脱双手的束缚,直接挂机,就可以按照自己设定的步骤,来进行屏幕的点击,例如fgo的刷无限池.抽无限池,以及一些有公式化模板的刷图方案的游戏,或者常见 ...

  9. 下载keep运动软件_keep app下载-keep安卓版(运动健身) - 超好玩

    keep是非常有效的一款运动健身平台,用户在这里能够轻松进行身体各个部位的锻炼方式学习.keep安卓版(运动健身)提供了非常全面丰富的健身课程内容,不论是腰腹,双腿,胸腹肌都能针对性的进行练习.感兴趣 ...

最新文章

  1. 开源 java CMS - FreeCMS2.7 移动端首页静态化
  2. 算法自动化测试的挑战与思考
  3. 零基础python必背代码-30个Python常用极简代码,拿走就用
  4. 用IIS怎样在局域网内建网站
  5. android studio中error,ERROR在Android Studio中
  6. 《Redis核心技术与实战》学习总结(1)
  7. 【转】DELPHI 对DICOM中的窗宽、窗位调整
  8. java设计一个bank类实现银行_AOS公链推出“隐私Bank”,彻底打破资产发行门槛!...
  9. 记录console的使用
  10. jQuery插件实现的页面功能介绍引导页效果
  11. Arena4D点云数据处理软件简介
  12. MP算法与OMP算法
  13. 利用html模板发送邮件
  14. 操作系统经典书籍推荐
  15. Sping aop XML与注解方式
  16. 问题:you-get能下载百度网盘的资源吗?
  17. 7z格式、LZMA压缩算法和7-Zip详细介绍
  18. (自兴人工智能)python猜数字游戏
  19. 永久免费的抠图软件分享
  20. 【GAMES-202实时渲染】3、预计算环境光照(球谐函数(SH)、IBL、Split Sum、环境光阴影计算(PRT))

热门文章

  1. Worms W.M.D for Mac v1.0.0.193 (百战天虫:战争武器)
  2. 2017年诺奖:百年现代物理学,今天做了个了断!
  3. macos 共享屏幕_如何在macOS上仅使用Private Me卡共享某些联系方式
  4. 【强化学习论文合集】三十.2021AAAI人工智能大会论文(AAAI2021)
  5. 我们一起来学Shell - 初识shell
  6. 列表推导式_Python教程
  7. 洛克王国服务器维修,洛克王国怨声载道 修复植物园 8月24日服务器公告
  8. Android 动画实现方式以及对比(GIF和WebP,Lottie和SVGA,原生动画)
  9. css3+jquery+js做的翻翻乐小游戏
  10. win7 ftp服务器修改读写权限,局域网ftp服务器设置不同权限