2020年分省(区、市)万元地区生产总值能耗降低率等指标公报

国家统计局 国家发展和改革委员会 国家能源局 2021年8月23日   现将2020年各省、自治区、直辖市万元地区生产总值能耗降低率等指标数据公布如下:  地 区 万元地区生产总值能耗上升或下降(±%) 能源消费总量增速 (%) 万元地区生产总值电耗上升或下降(±%) 北 京 -9.18 -8.1 -3.39 天 津 -3.07 -1.7 -1.87 河 北 -3.01 0.7 -1.77 山 西 -2.88 0.6 -0.03 内蒙古 6.89 7.1 6.61 辽 宁 3.96 4.6 0.27 吉 林 -1.57 0.8 0.83 黑龙江 -1.70 -0.8 0.92 上 海 -6.64 -5.1 -1.16 江 苏 -3.10 0.5 -1.84 浙 江 6.34 10.1 -0.90 安 徽 2.03 6.0 1.59 福 建 -1.83 1.4 0.11 江 西 -2.27 1.5 2.01 山 东 -2.41 1.1 -1.67 河 南 0.76 2.0 -0.43 湖 北 -1.17 -6.1 1.97 湖 南 -1.98 1.7 -0.27 广 东 -1.16 1.1 1.16 广 西 1.05 4.8 2.42 海 南 -3.12 0.3 -1.43 重 庆 -3.88 -0.2 -1.54 四 川 -1.79 1.9 4.76 贵 州 -2.45 1.9 -1.45 云 南 2.71 6.8 7.52 西 藏       陕 西 -1.89 0.3 1.24 甘 肃 -0.20 3.7 2.81 青 海 -3.47 -2.0 2.02 宁 夏 -0.16 3.7 -7.82 新 疆 -0.70 2.7 2.26    附注:   1.指标解释   万元地区生产总值能耗是指一个地区生产每万元地区生产总值所消费的能源总量。   能源消费总量是指一定地域内国民经济各行业和居民家庭在一定时间消费的各种能源的总和。   万元地区生产总值电耗是指一个地区生产每万元地区生产总值所消费的电力。   2.计算方法   其中,地区生产总值按照2015年价格计算。   3.初步核算,2020年全国万元国内生产总值能耗比上年下降0.1%,能源消费总量增长2.2%。   4.西藏自治区数据暂缺。   5.公报不含香港特别行政区、澳门特别行政区和台湾省。 

基于Rebound制造绚丽的动画效果-入门篇

基于Rebound制造绚丽的动画效果-入门篇


Rebound是什么?

Rebound是一个来自 Facebook 公司的 Java物理和动画库。Rebound spring 模型可用于创建动画,让你感觉很自然。

Rebound的运作原理是什么?

Rebound拥有两个参数:tensionfriction

  • tension是张力,拉力。
  • friction是摩擦力。

演示:

  • tension:50,friction:1
    %title插图%num

    rebound_t50_f1.gif

拉力为50时,摩擦为1。摩擦对拉力的损耗十分小,可以看出图片是经历了弹簧式的来回放大缩小,直到拉力耗尽到停止。同理,当摩擦力为0的时候,力不会被损耗,将会一直运动下去。

  • tension:50,friction:15
    %title插图%num

    rebound_t50_f15.gif

拉力为50时,摩擦为15。我们模拟将图片缩小到*小,在某一个瞬间释放。会看到摩擦对拉力的损耗十分大,甚至没有回弹。

代码编写

MainActivity关键代码

/**
 * 弹簧动画
 *
 * @param v        动画View
 * @param from     开始参数
 * @param to       结束参数
 * @param tension  拉力系数
 * @param friction 摩擦力系数
 */
private void animateViewDirection(final View v, float from, float to, int tension, int friction) {
    //从弹簧系统创建一个弹簧
    Spring spring = springSystem.createSpring();
    //设置弹簧的开始参数
    spring.setCurrentValue(from);
    //查看源码可知
    //public static SpringConfig defaultConfig = fromOrigamiTensionAndFriction(40.0D, 7.0D);
    //弹簧的默认拉力是40,摩擦是7。这里设置为我们seekBar选择的拉力和摩擦参数
    spring.setSpringConfig(SpringConfig.fromOrigamiTensionAndFriction(tension, friction));
    //给弹簧添加监听,动态设置控件的状态
    spring.addListener(new SimpleSpringListener() {
        @Override
        public void onSpringUpdate(Spring spring) {
            //设置图片的X,Y的缩放
            //还可以设置setAlpha,setTranslationX...综合形成复杂的动画
            v.setScaleX((float) spring.getCurrentValue());
            v.setScaleY((float) spring.getCurrentValue());
        }
    });
    //设置结束时图片的参数
    spring.setEndValue(to);
}

Demo展示

%title插图%num

rebound_demo.gif

然而你们觉得

%title插图%num

image

看看github大牛用rebound做的动画

%title插图%num

rebound_demo_2.gif

Rebound只是一个简单的物理模型,*终想法和效果由我们自己控制

参考资料

Github:Rebound Github
官网:Rebound

Rebound-Android的弹簧动画库

Rebound-Android的弹簧动画库

简介

 

Rebound是facebook出品的一个弹簧动画库,与之对应的iOS版本有一个pop动画库,也是非常的强大给力。Facebook真是互联网企业中的楷模,开源了很多的实用开源库,大赞一个!!!

讲解Rebound之前,先看看我们根据Rebound高仿的新浪微博弹出菜单,看效果图:

%title插图%num

话说回来,facebook为啥推出这个库呢?主要就是现有的动画离真实物理世界差别比较明显,为了让动画看起来真实自然,带有力量的效果,Rebound应运而生。两个比较重要的参数,一个是拉力Tension,一个是摩擦力Friction,拉力越大,弹簧效果越明显;摩擦力越大,弹框效果阻力越大、越不明显。如果这个摩擦力的值设置为0,就像真实世界中处于真空状态,一点摩擦力都没有,这个弹簧效果会一直无限制重复下去,根本停不下来,不信看效果图:

%title插图%num

我们把摩擦力的值改成1试试:

%title插图%num

初级用法

Rebound提供了非常简洁的api方法来供我们调用,我们只需要配置一些简单的参数就可以使用了,下面看看官网给出的例子:

// Create a system to run the physics loop for a set of springs.
SpringSystem springSystem = SpringSystem.create();

// Add a spring to the system.
Spring spring = springSystem.createSpring();

// Add a listener to observe the motion of the spring.
spring.addListener(new SimpleSpringListener() {

  @Override
  public void onSpringUpdate(Spring spring) {
    // You can observe the updates in the spring
    // state by asking its current value in onSpringUpdate.
    float value = (float) spring.getCurrentValue();
    float scale = 1f - (value * 0.5f);
    myView.setScaleX(scale);
    myView.setScaleY(scale);
  }
});

// Set the spring in motion; moving from 0 to 1
spring.setEndValue(1);

我们看下对应的效果图:

%title插图%num

你们发现,好像弹簧效果不明显,Rebound默认的拉力和摩擦力参数分别是40和7,我们看下Rebound里面有个defaultConfig

public static SpringConfig defaultConfig = SpringConfig.fromOrigamiTensionAndFriction(40, 7);

为了让弹簧效果更明显,我们修改下SpringConfig的值,代码如下:

spring.setSpringConfig(SpringConfig.fromOrigamiTensionAndFriction(100,1));

我们将拉力值改成100,摩擦力值改成1,效果图如下:

%title插图%num

效果很赞了吧!

高级用法:多个view连锁动画

如果想要做很多view的连锁动画怎么办?Rebound也提供了SpringChain这个接口。直接看代码吧:

SpringChain springChain = SpringChain.create(40,6,50,7);

int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
    final View view = viewGroup.getChildAt(i);

    springChain.addSpring(new SimpleSpringListener() {
        @Override
        public void onSpringUpdate(Spring spring) {
            view.setTranslationY((float) spring.getCurrentValue());
        }
    });
}

List<Spring> springs = springChain.getAllSprings();
for (int i = 0; i < springs.size(); i++) {
    springs.get(i).setCurrentValue(400);
}

springChain.setControlSpringIndex(2).getControlSpring().setEndValue(0);

效果图如下:

%title插图%num

我们来看看SpringChain这个类,创建它有两个create方法:

  1. 默认无参数create()
  2. 有参数的create(int mainTension,int mainFriction,int attachmentTension,int attachmentFriction)

其中带参数的*个参数表示起主导作用spring的拉力系数,第二个参数表示起主导作用Spring的摩擦力系数,第三个和第四个表示附属的拉力和摩擦力系数

SpringChain需要设置一个起主导控制作用的Spring,通过setControlSpringIndex方法来设置

高仿新浪弹出菜单

先看下高仿后的效果图:

%title插图%num

这里给出示例代码:

PopMenu popMenu = new PopMenu.Builder().attachToActivity(MainActivity.this)
                .addMenuItem(new PopMenuItem("文字", getResources().getDrawable(R.drawable.tabbar_compose_idea)))
                .addMenuItem(new PopMenuItem("照片/视频", getResources().getDrawable(R.drawable.tabbar_compose_photo)))
                .addMenuItem(new PopMenuItem("头条文章", getResources().getDrawable(R.drawable.tabbar_compose_headlines)))
                .addMenuItem(new PopMenuItem("签到", getResources().getDrawable(R.drawable.tabbar_compose_lbs)))
                .addMenuItem(new PopMenuItem("点评", getResources().getDrawable(R.drawable.tabbar_compose_review)))
                .addMenuItem(new PopMenuItem("更多", getResources().getDrawable(R.drawable.tabbar_compose_more)))
                .setOnItemClickListener(new PopMenuItemListener() {
                    @Override
                    public void onItemClick(PopMenu popMenu, int position) {
                        Toast.makeText(MainActivity.this, "你点击了第" + position + "个位置", Toast.LENGTH_SHORT).show();
                    }
                })
                .build();   
popMenu.show();

这里由于篇幅原因,就暂时先不讲解实现原理了

Rebound动画框架简单介绍

Rebound动画框架简单介绍

Android菜鸟一枚,有不对的地方希望大家指出,谢谢。
*近在接手了一个老项目,发现里面动画框架用的是facebook中的Rebound框架,由于以前没听说过,放假时闲得蛋痛,看看了源码,就顺手写这一篇吧。
写了一个小Demo,具体效果如下:
这是图片啊
代码很简单,这是xml布局:

<RelativeLayout 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: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" >

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/image"
        android:src="@drawable/a1" />

</RelativeLayout>

这是MainActivity:

package com.micro.mytest_button;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;

import com.facebook.rebound.Spring;
import com.facebook.rebound.SpringListener;
import com.facebook.rebound.SpringSystem;
import com.nineoldandroids.view.ViewHelper;

public class MainActivity extends Activity {

    private ImageView image;
    private Spring spring;

    private final float mScale = 1.0f;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        image = (ImageView) findViewById(R.id.image);

        SpringSystem springSystem = SpringSystem.create();
        spring = springSystem.createSpring();       
        spring.addListener(new SpringListener() {

            @Override
            public void onSpringUpdate(Spring spring) {
                float value = (float) spring.getCurrentValue();
                float scale = 1f - (value * mScale);

                System.out.println("the value is " + value + "--the scale is --" + scale);

                ViewHelper.setScaleX(image, scale);
                ViewHelper.setScaleY(image, scale);
            }

            @Override
            public void onSpringEndStateChange(Spring spring) {
            }

            @Override
            public void onSpringAtRest(Spring spring) {
            }

            @Override
            public void onSpringActivate(Spring spring) {
            }
        });

        image.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    spring.setEndValue(1.0f);
                    break;

                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    spring.setEndValue(0.0f);
                    break;
                }

                return true;
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}

现在主要是来分析下Rebound的源码,看看里面到底怎么走的,用法很简单(这是*简单的用法),就三句话:

SpringSystem springSystem = SpringSystem.create();
spring = springSystem.createSpring();
spring.addListener(new SpringListener() {}
  • 1

主要是创建了三个对象,SpringSystem/spring/SpringListener,对于动画的效果,就是这三个玩意搞出来的。具体的模式如下:
SpringSystem:继承自BaseSpringSystem,其中包含了Spring对象引用的容器,控制Spring对象的操作,存在一个SpringLooper(是一个抽象方法,只有start()/stop()方法),这也是facebook自定义的类,与android.os.Looper无关,只是模拟了android.os.Looper的方法,内存start(),end()方法。其构造方法:

 public static SpringSystem create() {
    return new SpringSystem(AndroidSpringLooperFactory.createSpringLooper());
  }
  • 1

打开SpringSystem(SpringLooper sl)源码:

  public BaseSpringSystem(SpringLooper springLooper) {
    if (springLooper == null) {
      throw new IllegalArgumentException("springLooper is required");
    }
    mSpringLooper = springLooper;
    mSpringLooper.setSpringSystem(this);
  }
  • 1

现在再看AndroidSpringLooperFactory.createSpringLooper()源码:

 public static SpringLooper createSpringLooper() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
      return ChoreographerAndroidSpringLooper.create();
    } else {
      return LegacyAndroidSpringLooper.create();
    }
  }
  • 1

可以看到为了兼容JDK,高低版本创建的SpringLooper容器不经相同,这个不是考虑的重点。

现在来看第二句:

spring = springSystem.createSpring();
  • 1

 

翻开SpringSytem.createSpring()源码:

 public Spring createSpring() {
    Spring spring = new Spring(this);
    registerSpring(spring);
    return spring;
  }
  • 1
  • 2

可以看到Spring类是一个对立的Java类,持有SpringSystem的引用,来看看Spring(SpringSystem ss)构造函数:

  Spring(BaseSpringSystem springSystem) {
    if (springSystem == null) {
      throw new IllegalArgumentException("Spring cannot be created outside of a BaseSpringSystem");
    }
    mSpringSystem = springSystem;
    mId = "spring:" + ID++;
    setSpringConfig(SpringConfig.defaultConfig);
  }

再看setSpringConfig(SpringConfig.defaultConfig)源码:

public Spring setSpringConfig(SpringConfig springConfig) {
    if (springConfig == null) {
      throw new IllegalArgumentException("springConfig is required");
    }
    mSpringConfig = springConfig;
    return this;
  }
  • 1

也只是初始化一些参数,并没有有我们想要的源码(这里的意思就是我点击图片时,它为什么会有动画的意思),再看第三个方法吧:

spring.addListener(new SpringListener() {}
  • 1

 

添加监听器,用的多的人就会有感觉,这个也不会有关键代码可以使我们图片有缩放效果,那么问题来了,看到facebook写的三句话,我们并没有找到动画缩放的想过函数啊,是不是有配置文件或者静态/动态代码块呢??找了很久,没有啊。那只有接下来再看image的代码了,在看到Image的绑定的onTouch事件上,看到了这句话:

    spring.setEndValue(1.0f);
  • 1

 

啥也不说,进去看看再说吧:

  public Spring setEndValue(double endValue) {
    if (mEndValue == endValue && isAtRest()) {
      return this;
    }
    mStartValue = getCurrentValue();
    mEndValue = endValue;
    mSpringSystem.activateSpring(this.getId());
    for (SpringListener listener : mListeners) {
      listener.onSpringEndStateChange(this);
    }
    return this;
  }
  • 1

其他没啥好看的,看到了这句话:

 mSpringSystem.activateSpring(this.getId());
  • 1

活了二十几年,立马感觉这句话有问题,进入看看:

  void activateSpring(String springId) {
    Spring spring = mSpringRegistry.get(springId);
    if (spring == null) {
      throw new IllegalArgumentException("springId " + springId + " does not reference a registered spring");
    }
    mActiveSprings.add(spring);
    if (getIsIdle()) {
      mIdle = false;
      mSpringLooper.start();
    }
  }

解释一下,刚才说的SpringSystem包含了Spring对象的引用,mSpringRegistry.get(springId)是找到SpringSystem中注册的Spring, mActiveSprings.add(spring)是将该spring添加到活动的spring队列中,那现在就看看这句话吧: mSpringLooper.start(),刚才说SpringLooper是一个抽象类,那好随便找个其子类看看里面的方法吧,

private static class LegacyAndroidSpringLooper extends SpringLooper {}
  • 1

刚才的兼容性代码中,JELLY_BEAN(也就是4.1之前的代码),那我们就看看mSpringLooper.start()是个什么鬼了吧:

private static class LegacyAndroidSpringLooper extends SpringLooper {

    private final Handler mHandler;
    private final Runnable mLooperRunnable;
    private boolean mStarted;
    private long mLastTime;

    /**
     * @return an Android spring looper using a new {@link Handler} instance
     */
    public static SpringLooper create() {
      return new LegacyAndroidSpringLooper(new Handler());
    }

    public LegacyAndroidSpringLooper(Handler handler) {
      mHandler = handler;
      mLooperRunnable = new Runnable() {
        @Override
        public void run() {
          if (!mStarted || mSpringSystem == null) {
            return;
          }
          long currentTime = SystemClock.uptimeMillis();
          mSpringSystem.loop(currentTime - mLastTime);
          mHandler.post(mLooperRunnable);
        }
      };
    }

    @Override
    public void start() {
      if (mStarted) {
        return;
      }
      mStarted = true;
      mLastTime = SystemClock.uptimeMillis();
      mHandler.removeCallbacks(mLooperRunnable);
      mHandler.post(mLooperRunnable);
    }

    @Override
    public void stop() {
      mStarted = false;
      mHandler.removeCallbacks(mLooperRunnable);
    }
  }

对,没错我们看到了 mHandler.post(mLooperRunnable);金典的方法啊,那我们看看这mLooperRunnable方法里面在干嘛吧,

 long currentTime = SystemClock.uptimeMillis();
 mSpringSystem.loop(currentTime - mLastTime);

废话不多说,立马去找我们想要看到的SpringSystem.loop方法了:

public void loop(double ellapsedMillis) {
    for (SpringSystemListener listener : mListeners) {
      listener.onBeforeIntegrate(this);
    }
    advance(ellapsedMillis);
    if (mActiveSprings.isEmpty()) {
      mIdle = true;
    }
    for (SpringSystemListener listener : mListeners) {
      listener.onAfterIntegrate(this);
    }
    if (mIdle) {
      mSpringLooper.stop();
    }
  }

对了,我们好像看到了advance(ellapsedMillis)啊,这个看起来比较吊,不说了进去看看再说啊:

 void advance(double deltaTime) {
    for (Spring spring : mActiveSprings) {
      // advance time in seconds
      if (spring.systemShouldAdvance()) {
        spring.advance(deltaTime / 1000.0);
      } else {
        mActiveSprings.remove(spring);
      }
    }
  }

尼玛啊,我看到了

 spring.advance(deltaTime / 1000.0);

哈哈,我感觉快要找到了,不说,赶快进去找:

void advance(double realDeltaTime) {

    boolean isAtRest = isAtRest();

    if (isAtRest && mWasAtRest) {
      /* begin debug
      Log.d(TAG, "bailing out because we are at rest:" + getName());
      end debug */
      return;
    }

    // clamp the amount of realTime to simulate to avoid stuttering in the UI. We should be able
    // to catch up in a subsequent advance if necessary.
    double adjustedDeltaTime = realDeltaTime;
    if (realDeltaTime > MAX_DELTA_TIME_SEC) {
      adjustedDeltaTime = MAX_DELTA_TIME_SEC;
    }

    /* begin debug
    long startTime = System.currentTimeMillis();
    int iterations = 0;
    end debug */

    mTimeAccumulator += adjustedDeltaTime;

    double tension = mSpringConfig.tension;
    double friction = mSpringConfig.friction;

    double position = mCurrentState.position;
    double velocity = mCurrentState.velocity;
    double tempPosition = mTempState.position;
    double tempVelocity = mTempState.velocity;

    double aVelocity, aAcceleration;
    double bVelocity, bAcceleration;
    double cVelocity, cAcceleration;
    double dVelocity, dAcceleration;

    double dxdt, dvdt;

    // iterate over the true time
    while (mTimeAccumulator >= SOLVER_TIMESTEP_SEC) {
      /* begin debug
      iterations++;
      end debug */
      mTimeAccumulator -= SOLVER_TIMESTEP_SEC;

      if (mTimeAccumulator < SOLVER_TIMESTEP_SEC) {
        // This will be the last iteration. Remember the previous state in case we need to
        // interpolate
        mPreviousState.position = position;
        mPreviousState.velocity = velocity;
      }

      // Perform an RK4 integration to provide better detection of the acceleration curve via
      // sampling of Euler integrations at 4 intervals feeding each derivative into the calculation
      // of the next and taking a weighted sum of the 4 derivatives as the final output.

      // This math was inlined since it made for big performance improvements when advancing several
      // springs in one pass of the BaseSpringSystem.

      // The initial derivative is based on the current velocity and the calculated acceleration
      aVelocity = velocity;
      aAcceleration = (tension * (mEndValue - tempPosition)) - friction * velocity;

      // Calculate the next derivatives starting with the last derivative and integrating over the
      // timestep
      tempPosition = position + aVelocity * SOLVER_TIMESTEP_SEC * 0.5;
      tempVelocity = velocity + aAcceleration * SOLVER_TIMESTEP_SEC * 0.5;
      bVelocity = tempVelocity;
      bAcceleration = (tension * (mEndValue - tempPosition)) - friction * tempVelocity;

      tempPosition = position + bVelocity * SOLVER_TIMESTEP_SEC * 0.5;
      tempVelocity = velocity + bAcceleration * SOLVER_TIMESTEP_SEC * 0.5;
      cVelocity = tempVelocity;
      cAcceleration = (tension * (mEndValue - tempPosition)) - friction * tempVelocity;

      tempPosition = position + cVelocity * SOLVER_TIMESTEP_SEC;
      tempVelocity = velocity + cAcceleration * SOLVER_TIMESTEP_SEC;
      dVelocity = tempVelocity;
      dAcceleration = (tension * (mEndValue - tempPosition)) - friction * tempVelocity;

      // Take the weighted sum of the 4 derivatives as the final output.
      dxdt = 1.0/6.0 * (aVelocity + 2.0 * (bVelocity + cVelocity) + dVelocity);
      dvdt = 1.0/6.0 * (aAcceleration + 2.0 * (bAcceleration + cAcceleration) + dAcceleration);

      position += dxdt * SOLVER_TIMESTEP_SEC;
      velocity += dvdt * SOLVER_TIMESTEP_SEC;
    }

    mTempState.position = tempPosition;
    mTempState.velocity = tempVelocity;

    mCurrentState.position = position;
    mCurrentState.velocity = velocity;

    if (mTimeAccumulator > 0) {
      interpolate(mTimeAccumulator / SOLVER_TIMESTEP_SEC);
    }

    // End the spring immediately if it is overshooting and overshoot clamping is enabled.
    // Also make sure that if the spring was considered within a resting threshold that it's now
    // snapped to its end value.
    if (isAtRest() || (mOvershootClampingEnabled && isOvershooting())) {
      // Don't call setCurrentValue because that forces a call to onSpringUpdate
      mStartValue = mEndValue;
      mCurrentState.position = mEndValue;
      setVelocity(0);
      isAtRest = true;
    }

    /* begin debug
    long endTime = System.currentTimeMillis();
    long elapsedMillis = endTime - startTime;
    Log.d(TAG,
        "iterations:" + iterations +
            " iterationTime:" + elapsedMillis +
            " position:" + mCurrentState.position +
            " velocity:" + mCurrentState.velocity +
            " realDeltaTime:" + realDeltaTime +
            " adjustedDeltaTime:" + adjustedDeltaTime +
            " isAtRest:" + isAtRest +
            " wasAtRest:" + mWasAtRest);
    end debug */

    // NB: do these checks outside the loop so all listeners are properly notified of the state
    //     transition
    boolean notifyActivate = false;
    if (mWasAtRest) {
      mWasAtRest = false;
      notifyActivate = true;
    }
    boolean notifyAtRest = false;
    if (isAtRest) {
      mWasAtRest = true;
      notifyAtRest = true;
    }
    for (SpringListener listener : mListeners) {
      // starting to move
      if (notifyActivate) {
        listener.onSpringActivate(this);
      }

      // updated
      listener.onSpringUpdate(this);

      // coming to rest
      if (notifyAtRest) {
        listener.onSpringAtRest(this);
      }
    }
  }
  • 1

代码精简一下,就变成这样了:

void advance(double realDeltaTime) {

    boolean isAtRest = isAtRest();

    if (isAtRest && mWasAtRest) {
      return;
    }

    double adjustedDeltaTime = realDeltaTime;
    if (realDeltaTime > MAX_DELTA_TIME_SEC) {
      adjustedDeltaTime = MAX_DELTA_TIME_SEC;
    }

    mTimeAccumulator += adjustedDeltaTime;

    while (mTimeAccumulator >= SOLVER_TIMESTEP_SEC) {
        /**
            inner change 
        **/
    }

    mTempState.position = tempPosition;
    mTempState.velocity = tempVelocity;

    mCurrentState.position = position;
    mCurrentState.velocity = velocity;

    for (SpringListener listener : mListeners) {
      // starting to move
      if (notifyActivate) {
        listener.onSpringActivate(this);
      }

      // updated
      listener.onSpringUpdate(this);

      // coming to rest
      if (notifyAtRest) {
        listener.onSpringAtRest(this);
      }
    }
  }
  • 1

这下看得十分清楚了吧,在while循环里面变换之后,我们得到了 :

  mCurrentState.position = position;
 mCurrentState.velocity = velocity;

然后在接口回调中我们使用到了这些参数,

spring.addListener(new SpringListener() {

            @Override
            public void onSpringUpdate(Spring spring) {
                float value = (float) spring.getCurrentValue();
                float scale = 1f - (value * mScale);

                ViewHelper.setScaleX(image, scale);
                ViewHelper.setScaleY(image, scale);
            }

            @Override
            public void onSpringEndStateChange(Spring spring) {}
            @Override
            public void onSpringAtRest(Spring spring) {}
            @Override
            public void onSpringActivate(Spring spring) {}
        });

就这样我么的变换终于就成功了,我们也就走通了相关的流程了,是不是很好玩呢???主要是facebook这个Rebound框架很小,类很少,我们可以在很短的时间对它通读,是不是感觉到分析源码很好玩呢?我也是一名android菜鸟,很多时候不敢去分析源码,很为很多的东西都看不懂看不透,那就慢慢来吧,先从简单的开始吧!

让动画不再僵硬:Facebook Rebound Android动画库介绍

让动画不再僵硬:Facebook Rebound Android动画库介绍

Rebound是facebook推出的一个弹性动画库,可以让动画看起来真实自然,像真实世界的物理运动,带有力的效果,使用的参数则是facebook的origami中使用的。

 

官网上有一个简单的JS版本来做demo,如果说到evernote、LinkedIn、flow等应用也在使用这个动画库,是不是会显得更厉害些呢。

Spring spring = mSpringSystem
        .createSpring()
        .setSpringConfig(SpringConfig.fromOrigamiTensionAndFriction(86, 7))
        .addListener(new SimpleSpringListener() {
            @Override
            public void onSpringUpdate(Spring spring) {
                float value = (float) spring.getCurrentValue();
                ViewHelper.setTranslationX(view, value);
            }
        });

上面的短短代码就可以给一个view加上自然的从左向右进入回弹效果。

类似地

Spring spring = mSpringSystem
        .createSpring()
        .setSpringConfig(SpringConfig.fromOrigamiTensionAndFriction(86, 7))
        .addListener(new SimpleSpringListener() {
            @Override
            public void onSpringUpdate(Spring spring) {
                float value = (float) spring.getCurrentValue();
                float scale = 1f - value;
                ViewHelper.setScaleX(mItemIconViewList.get(index), scale);
                ViewHelper.setScaleY(mItemIconViewList.get(index), scale);
            }
        });

就可以给view加上一个从小变大然后略有回弹的效果。

如果想要做很多view的连锁动画怎么办?Rebound也提供了SpringChain这个接口。

for (int i = 0; i < viewCount; i++) {
    final View view = new View(context);
    view.setLayoutParams(
            new TableLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    1f));
    mSpringChain.addSpring(new SimpleSpringListener() {
        @Override
        public void onSpringUpdate(Spring spring) {
            float value = (float) spring.getCurrentValue();
            view.setTranslationX(value);
        }
    });
    int color = (Integer) evaluator.evaluate((float) i / (float) viewCount, startColor, endColor);
    view.setBackgroundColor(color);
    view.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            return handleRowTouch(v, event);
        }
    });
    mViews.add(view);
    rootView.addView(view);
}

getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        getViewTreeObserver().removeOnGlobalLayoutListener(this);
        List<Spring> springs = mSpringChain.getAllSprings();
        for (int i = 0; i < springs.size(); i++) {
            springs.get(i).setCurrentValue(-mViews.get(i).getWidth());
        }
        postDelayed(new Runnable() {
            @Override
            public void run() {
                mSpringChain
                        .setControlSpringIndex(0)
                        .getControlSpring()
                        .setEndValue(0);
            }
        }, 500);
    }
});

就做出了一个view和view的牵引位移动画效果。

Facebook Rebound 弹性动画库 源码分析

Rebound源码分析

让动画不再僵硬:Facebook Rebound Android动画库介绍一文中介绍了rebound这个库。

对于想体验一下rebound的效果,又懒得clone和编译代码的,这里提供一个demo apk。

今天看到了tumblr发布了基于rebound的Backboard,本想直接分析一下Backboard对rebound做了些什么,不过考虑到rebound还没有仔细分析过,所以这里做一下源码分析。

对外部来说,首先接触的就是SpringSystem了,但在说它之前,先让我们看看spring是什么。

Spring

Spring通过可设置的摩擦力(Friction)和张力(tension)实现了胡克定律,通过代码模拟了物理场景:

private static class PhysicsState {
  double position;
  double velocity;
}

private final PhysicsState mCurrentState = new PhysicsState();
private final PhysicsState mPreviousState = new PhysicsState();
private final PhysicsState mTempState = new PhysicsState();
private double mStartValue;
private double mEndValue;

 

每个spring从mStartValuemEndValue进行运动,内部维护了当前状态、前值状态,以及临时状态,每个状态由通过位置和速度来描述,而运动的推进逻辑则在

void advance(double realDeltaTime)
  • 1

advance方法中,SpringSystem会遍历由其管理的所有Spring实例,对它们进行advance

SpringListener

每个Spring内部都维护着一个SpringListener数组,这也是我们经常会需要去实现的一个接口:

public interface SpringListener {
  void onSpringUpdate(Spring spring);
  void onSpringAtRest(Spring spring);
  void onSpringActivate(Spring spring);
  void onSpringEndStateChange(Spring spring);
}

可以看到create方法里面默认给了一个SpringLooper的工厂类创建实例(内部根据系统版本是否>=3.0返回了不同的子类实例),而SpringLooper顾名思义是一个Looper,做的就是不断地更新SpringSystem的状态,实际调用了BaseSpringSystemloop方法:

/**
 * loop the system until idle
 * @param elapsedMillis elapsed milliseconds
 */
public void loop(double elapsedMillis) {
  for (SpringSystemListener listener : mListeners) {
    listener.onBeforeIntegrate(this);
  }
  advance(elapsedMillis);
  if (mActiveSprings.isEmpty()) {
    mIdle = true;
  }
  for (SpringSystemListener listener : mListeners) {
    listener.onAfterIntegrate(this);
  }
  if (mIdle) {
    mSpringLooper.stop();
  }
}

 

即通过每次elapse的时间,来把system往前advance(有点类似游戏里,每一帧的运动,如果不够快就会掉帧,这里对应地,elapsedMillis则可能会很大)。

大部分的逻辑其实在BaseSpringSystem:

public class BaseSpringSystem {

  private final Map<String, Spring> mSpringRegistry = new HashMap<String, Spring>();
  private final Set<Spring> mActiveSprings = new CopyOnWriteArraySet<Spring>();
  private final SpringLooper mSpringLooper;
  private final CopyOnWriteArraySet<SpringSystemListener> mListeners = new CopyOnWriteArraySet<SpringSystemListener>();
  private boolean mIdle = true;
  • 1

mSpringRegistry保存了所有由该SpringSystem管理的Spring实例,键值String则是Spring内的一个自增id,每个Spring实例的id都会不同。通过createSpring创建的Spring实例都会直接被加到该HashMap。

mActiveSprings内放的是被激活的Spring,实际在调用Spring.Java:

public Spring setCurrentValue(double currentValue, boolean setAtRest);
public Spring setEndValue(double endValue);
public Spring setVelocity(double velocity);

 

三个方法的时候才会进行激活,且在实际loop过程中,也只会对激活的Spring进行advance。

mSpringLooper是该SpringSystem绑定的Looper。

mListeners是注册在该SpringSystem上的SpringSystemListener

public interface SpringSystemListener {
  void onBeforeIntegrate(BaseSpringSystem springSystem);
  void onAfterIntegrate(BaseSpringSystem springSystem);
}

会在SpringSystemloop方法开始和结束时候调用onBeforeIntegrate以及onAfterIntegrate,比如可以在所有Spring loop完之后检查它们的值,并进行速度限制,暂停等操作,相对于绑定到SpringSpringListener,这个更全局一些。

SpringChain

顾名思义,SpringChain就是连锁Spring,由数个Spring结合而成,且两两相连,可以用来做一些连锁的效果,比如数个图片之间的牵引效果。

每个SpringChain都会有一个control spring来作为带头大哥,在链中前后的Spring都会被他们的前任所拉动。比如我们有 1 2 3 4 5五个Spring,选择3作为带头大哥,则3开始运动后,会分别拉动2和4,然后2会拉1,4则去拉动5。

  private SpringChain(
      int mainTension,
      int mainFriction,
      int attachmentTension,
      int attachmentFriction) {
    mMainSpringConfig = SpringConfig.fromOrigamiTensionAndFriction(mainTension, mainFriction);
    mAttachmentSpringConfig =
        SpringConfig.fromOrigamiTensionAndFriction(attachmentTension, attachmentFriction);
    registry.addSpringConfig(mMainSpringConfig, "main spring " + id++);
    registry.addSpringConfig(mAttachmentSpringConfig, "attachment spring " + id++);
  }
  • 1
  • 2

即ControlSpring摩擦力和张力都会相对小一些。

SpringChain本身实现了SpringListener,并使用那些接口来进行整个chain的更新。

@Override
public void onSpringUpdate(Spring spring) {
    // 获得control spring的索引,并更新前后Spring的endValue,从而触发连锁影响
    int idx = mSprings.indexOf(spring);
    SpringListener listener = mListeners.get(idx);
    int above = -1;
    int below = -1;
    if (idx == mControlSpringIndex) {
        below = idx - 1;
        above = idx + 1;
    } else if (idx < mControlSpringIndex) {
        below = idx - 1;
    } else if (idx > mControlSpringIndex) {
        above = idx + 1;
    }
    if (above > -1 && above < mSprings.size()) {
        mSprings.get(above).setEndValue(spring.getCurrentValue());
    }
    if (below > -1 && below < mSprings.size()) {
        mSprings.get(below).setEndValue(spring.getCurrentValue());
    }
    listener.onSpringUpdate(spring);
}

@Override
public void onSpringAtRest(Spring spring) {
    int idx = mSprings.indexOf(spring);
    mListeners.get(idx).onSpringAtRest(spring);
}

@Override
public void onSpringActivate(Spring spring) {
    int idx = mSprings.indexOf(spring);
    mListeners.get(idx).onSpringActivate(spring);
}

@Override
public void onSpringEndStateChange(Spring spring) {
    int idx = mSprings.indexOf(spring);
    mListeners.get(idx).onSpringEndStateChange(spring);
}
  • 1

通常我们想要这个SpringChain进行运动会调用mSpringChain.setControlSpringIndex(0).getControlSpring().setEndValue(1);

ControlSpring便会开始运动,并调用到SpringChain作为SpringListener的那些方法,进而整个系统作为一个链开始运动。

SpringConfiguratorView

SpringConfiguratorView继承了FrameLayout,如果体验过demo apk的同学,应该注意到屏幕底下上拉可以对Spring的参数进行配置,这就是由SpringConfiguratorView做的了。

AnimationQueue

同样是用来做连锁动画的,不过Backboard没有用到这个,Facebook自己的例子也没有用过该类,以前做动画的时候用过这个,结果貌似是有什么坑,*后改成了SpringChain去实现。

AnimationQueue本身和Rebound没有任何关系,内部定义了接口

public interface Callback {
    void onFrame(Double value);
}

 

原理倒是有点像rebound。由于和rebound本身没关系,这里就不多说了。

自建ios超级签过程

自建超级签

https://blog.csdn.net/LiaoQuesg/article/details/101219984

签名的时候需要安装ruby,踩了无数坑

#下载

$ wget https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.0.tar.gz

#解压

$ tar -zxvf ruby-2.5.0.tar.gz

#进入目录

$ cd ruby-2.5.0

#创建安装目录

$ mkdir -p /usr/local/ruby

#配置并制定安装位置

$ ./configure –prefix=/usr/local/ruby

#编译与安装,这个过程很慢,你可以去吃顿饭或者抽一盒烟

$ make && make install

#建立软链接

使用命令ln -s 文件所在位置 目的位置
命令这么写。(ln -s /usr/local/ruby/bin/ruby /usr/local/bin/ruby)
是LN 不是in

ln -s /usr/local/ruby/bin/ruby /usr/bin/ruby

ln -s /usr/local/ruby/bin/gem /usr/bin/gem

#查看ruby版本

$ ruby -v

完成!!!!!!!!!!!

然后是gem安装依赖

gem install fastlane

出错

ERROR: Loading command: install (LoadError) cannot load such file — zlib

ERROR: While executing gem … (NoMethodError) undefined method `invoke_with_build_args’ for nil:NilClass

ERROR: Loading command: install (LoadError) cannot load such file — zlib

需要安装 ruby 本身对 zlib 的支持,瞎猜的,本身不会 ruby,下面是步骤

yum -y install zlib-devel
cd ruby-2.6.4/ext/zlib
ruby ./extconf.rb
make
make install
若执行 make 是抛出错误:make: *** No rule to make target/include/ruby.h’, needed by zlib.o’. Stop. 则执行以下命令

vim Makefile
将 zlib.o: $(top_srcdir)/include/ruby.h 这一行加 # 注释之后添加 zlib.o: ../../include/ruby.h 即可成功。
第二个错误和*个则是大同小异,是 openssl 抛出的错误 openssl library could not be found. you might want to use –with-openssl-dir=<dir> option to specify 巨恶心!

yum install openssl-devel -y
cd ruby-2.6.4/ext/openssl
ruby ./extconf.rb
make
make install
我这里在执行第二步时一直抛出上面的问题,*后我只能认为是我的 openssl 的版本低引起的问题,无奈之下只能更换了 openssl 的版本为 OpenSSL 1.1.1d 10 Sep 2019,如果有朋友发生同样的问题,可以尝试更换一下 openssl 的版本。

然后执行 make 接着抛出问题:ruby make: *** No rule to make target/include/ruby.h’, needed by ossl.o’,一个道理

vim Makefile
这次是在 Makefile 中添加一行 top_srcdir = ../..,如下图

%title插图%num

这次是终于成功了~~~
然后是提示

make提示:g++命令未找到
通常情况下在centos下安装软件就用yum.

关键是,使用yum你的知道安装包的名字是什么。

如果直接键入yum install g++,会告诉你没有这个包。

所以你应该使用gcc-c++,这才是这个包的名字。

所以应该是执行:yum install gcc-c++

Ubuntu 下安装g++

sudo apt install g++

iOS 超级签名详解

一、原理

把安装设备当做开发设备进行分发。说的明白一些,开发者可以在开发者后台添加手机的UDID,然后重新打包一个IPA文件,分发平台,然后被添加的UDID就可以下载。

二、优缺点

优势:

直接分发,安装即可运行,不需要用户做企业证书的信任操作

目前稳定,不会有证书吊销导致的业务风险(后续苹果政策风险非常高)

缺点:

单开发者账号的iPhone设备数量只有100个,导致分发成本非常高(99美元/1年/100个设备)

开发者账号需要预先写入安装设备的UDID,在工具链不通的情况下,获取用户的UDID相对困难和繁琐,而且手动写入UDID不存在商用可行性,当然目前这个缺点被解决了

三、整体运行流程

1605558-fe383e054c661575.png

1.用户手机安装预留的描述文件,获取本机udid后,向服务器返回用户的udid

2.服务器收到UDID后,将UDID添加到开发者账号下,下载此udid签名用的证书描述文件。

3.然后用此udid签名用的证书描述文件,把预留的ipa重签。

4.重签后的iPA上传分发服务器,通过分发链接让用户下载。

下面是这四个步骤的技术细节:

一、使用配置文件获取UDID

苹果公司允许开发者通过IOS设备和Web服务器之间的某个操作,来获得IOS设备的UDID(包括其他的一些参数)。这里的一个概述:

1.在你的Web服务器上创建一个.mobileconfig的XML格式的描述文件;

2.用户在所有操作之前必须通过某个点击操作完成.mobileconfig描述文件的安装;

3.服务器需要的数据,比如:UDID,需要在.mobileconfig描述文件中配置好,以及服务器接收数据的URL地址;

4.当用户设备安装描述文件后,设备会回调你设置的URL,如果你的URL返回302跳转的话,Safari浏览器会跳转到你所给的地址;服务器做一些301跳转,存储UDID等操作

XML代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>PayloadContent</key>
        <dict>
            <key>URL</key>
            <string>一个接收UDID的网址</string>
            <key>DeviceAttributes</key>
            <array>
                <string>UDID</string>
                <string>IMEI</string>
                <string>ICCID</string>
                <string>VERSION</string>
                <string>PRODUCT</string>
            </array>
        </dict>
        <key>PayloadOrganization</key>
        <string>GuangdongQi</string><!--组织名称-->
        <key>PayloadDisplayName</key>
        <string>AppFree</string>
        <key>PayloadVersion</key>
        <integer>1</integer>
        <key>PayloadUUID</key>
        <string>9CF421B3-9853-4454-BC8A-982CBD3C907C</string><!--自己随机填写的唯一字符串,http://www.guidgen.com/ 可以生成-->
        <key>PayloadIdentifier</key>
        <string>com.gpon.profile-service</string>
        <key>PayloadDescription</key>
        <string>This temporary profile will be used to find and display your current device's UDID.</string>
        <key>PayloadType</key>
        <string>Profile Service</string>
    </dict>
</plist>

 

注意

①、接收网址的地址需要SSL签名,自签名的不行,这也是苹果强制https的一种方式吧

②、mobileconfig下载时设置文件内容类型Content Type为:application/x-apple-aspen-config(遇到问题的都是因为这个),或者像这里用一个简单页面做好下载mobileconfig文件,引导用户安装

二、服务器收到UDID后,将UDID注册到某个开发者账号下,并下载新的描述文件

接下来的关键点就是如何在获取到用户的UDID之后,秒级完成注册新的开发者设备+更新Provisioning Profile的。 这里我们需要借助开源工具(Spaceship):

spaceship公开了Apple Developer Center的API,而且执行速度比解析开发者Web页面快两个数量级,从而在非常短的时间内搞定 Provisioning Profile。 这个框架解决了整套机制的关键问题,成为整个工具链的基石。其实某平台早就完成了UDID获取和应用签名分发的技术储备,只差这套API。

spaceship 是Fastlane的一个框架,安装spaceship

sudo gem install fastlane -n /usr/local/bin
sudo gem install pry  -n /usr/local/bin 
fastlane spaceship

具体细节:使用spaceship批量添加设备并更新profile

三、用此udid签名用的证书描述文件,把预留的ipa重签

这里推荐使用 Sigh这个框架来解决这个问题。

安装sigh:
sudo gem install sigh

对IPA包重签名:

 fastlane sigh resign

此时会显示:

输入你的IPA路径,点击回车,此时会显示你电脑中可用的证书:

输入你要更改的证书名,和你要更改的证书路径,回车:

ipa包重签名成功

参考:iOS重签名–Fastlane/sign脚本

四、重签后的iPA上传分发服务器,通过分发链接让用户下载

参考: 阿里云运维部署工具AppDeploy详细教程

转载于:https://www.cnblogs.com/huadeng/p/11557679.html

相关资源:IOS超级签名+APP超级签名分发系统完美运营版+一键超级签名系统+…

requests 的 post 中的 data 如何按顺序进行封装?

requests 的 post 中的 data 如何按顺序进行封装?

假设 data 如下 data={

a = ’11’,

b = ’22’,

c = 某个变量,

d = 另一个变量,

e = ’33’

} 然后我 requests.post 理论来说 他 data 的顺序应该是 a=11&b=22…..依次按顺序,但是我测试抓包发现,他顺序是乱的,特别是变量,他可能会封装成 a=11&c=某个变量&d=另一个变量值&b=22….. 我想让他就按照从上往下的顺序进行封装起来,百度了下也没人问这类的问题,请问有解吗?

Pycharm 使用 Anaconda 解释器,每次都 indexing

Pycharm 使用 Anaconda 解释器,每次都 indexing

如题,安装 Anaconda,每次启动 Pycharm 都会 indexing Python SDK (耗费 3 到 4 分钟) (我的 Base 环境是 Python3.8 ),google 百度搜了一通,发现都是说怎么 exclude project files,可是这是 Python SDK 的 index,exclude 之后就没得代码提示了,有没有解决方案,难道 JB 对于 Anaconda 的环境没有索引缓存吗?每次都要重新索引?