参考:
Matrix的原理:http://blog.csdn.net/pathuang68/article/details/6991867
最近发现了一个谷歌的ApiDemos源码,感觉像是一本武林秘籍一样。。沉迷其中,不能自拔(呵呵,我不信~)
先看下实现的效果,36D过渡动画,咳咳,口误((●ˇ∀ˇ●)),3D过度动画,还是很酷炫的,接下来,就一步一步的分析吧。
android:persistentDrawingCache
详见:http://blog.csdn.net/love_xsq/article/details/43524103
首先介绍下ViewGroup的android:persistentDrawingCache属性
说明:设置ViewGroup的绘制缓存策略。保存缓存会消耗更多的内存,但可能会阻止频繁的垃圾回收是反复创建缓存。默认为PERSISTENT_SCROLLING_CACHE
可取的值:
- PERSISTENT_NO_CACHE 说明不在内存中保存绘图缓存
- PERSISTENT_ANIMATION_CACHE 说明只保存动画绘图缓存
- PERSISTENT_SCROLLING_CACHE 说明只保存滚动效果绘图缓存
- PERSISTENT_ALL_CACHES 说明所有的绘图缓存都应该保存在内存中
设置方式:
- 代码里设置:(貌似不可以设置多个)
mContainer.setPersistentDrawingCache(ViewGroup.PERSISTENT_ANIMATION_CACHE);
- xml中设置:(可取的值:none、animation、scorlling、all)
android:persistentDrawingCache="animation|scrolling"
layout animation
详见:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0619/3090.html
此属性也是ViewGroup的属性,先看下谷歌的注释
Sets the layout animation controller used to animate the group's children after the first layout.
谷歌翻译:设置用于在第一次布局之后为viewgroup的子view设置动画的布局动画控制器。翻译的有点生硬,简单来说,就是控制子view的进场(第一次)动画
配置方式:
- 1.xml配置:
viewgroup中加入此属性:
android:layoutAnimation="@anim/layout_bottom_to_top_slide"
layout_bottom_to_top_slide:
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="30%"
android:animationOrder="reverse"
android:animation="@anim/slide_right" />
其中:
- android:delay表示动画播放的延时,既可以是百分比,也可以是float小数。eg:若动画时间为1秒,delay为0.2,子viewA先进,子viewB在A之后进入,A进入动画一秒,再等200毫秒后,B进入动画一秒
- android:animationOrder表示动画的播放顺序,有三个取值normal(顺序)、reverse(反序)、random(随机)
android:animation指向了子控件所要播放的动画,这里暂不关注animation,下面会涉及
2.代码配置:
Animation animation = AnimationUtils.loadAnimation(this, R.anim.slide_right);//引用动画的资源
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.3f);//同上
controller.setOrder(LayoutAnimationController.ORDER_REVERSE);//同上
//设置插值器
controller.setInterpolator(new DecelerateInterpolator());
mPhotosList.setLayoutAnimation(controller);
translate
上文中的slide_right
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator">
<translate
android:duration="@android:integer/config_shortAnimTime"
android:fromXDelta="-100%p"
android:toXDelta="0" />
</set>
interpolator为修饰动画效果的插值器,可控制动画效果为线性、先快后慢、先慢后快等
其中translate的属性:
- duration动画的持续时间
- fromXDelta 属性为动画起始时 X坐标上的位置
- toXDelta 属性为动画结束时 X坐标上的位置
- fromYDelta 属性为动画起始时 Y坐标上的位置
- toYDelta 属性为动画结束时 Y坐标上的位置
eg:android:toXDelta=”100%”,表示自身的100%,也就是从View自己的位置开始
android:toXDelta=”80%p”,表示父层View的80%,是以它父层View为参照的
Rotate3dAnimation
首先介绍下camera的坐标系
Camera的坐标系是左手坐标系。当手机平整的放在桌面上,X轴是手机的水平方向,Y轴是手机的竖直方向,Z轴是垂直于手机向里的那个方向。
终于到了,3d动画最核心的部分了
eg:camera.translate(10,50,-180)的意思是把观察物体右移(+x)10,上移(+y)50,向-z轴移180(即让物体接近camera,这样物体将会变大);
详见:http://www.jianshu.com/p/34e0fe5f9e31
Animation的applyTransformation方法
详见:https://www.cnblogs.com/kross/p/4087780.html
我也是从上文现学现卖,此方法大致概括为动画具体的实现,系统会以一个比较高的频率来调用这个方法
终于要说动画了吧,动画组成:
- 上半部分动画: listview显示,imageview为gone,点击条目listview沿着Y轴从0度旋转到90度,旋转过程Z轴向屏幕朝里的方向移动,从0逐渐到一定深度
- 转到90度节点时,listview为gone,imageview显示
- 下半部分动画: imageview沿着Y轴从0度旋转到90度,旋转过程中Z轴向屏幕朝外的方向移动,从一定深度到0
此3d动画为一个Animation的子类
分析的差不多了,上代码吧,里面注释还是挺多的。
/**
* An animation that rotates the view on the Y axis between two specified angles.
* This animation also adds a translation on the Z axis (depth) to improve the effect.
* 沿着Y轴在两个指定角度之间旋转的动画。
* 此动画还在Z轴(深度)上添加变换以提高效果。
*/
public class Rotate3dAnimation extends Animation {
private final float mFromDegrees;
private final float mToDegrees;
private final float mCenterX;
private final float mCenterY;
private final float mDepthZ;
private final boolean mReverse;
private Camera mCamera;
/**
* Creates a new 3D rotation on the Y axis. The rotation is defined by its
* start angle and its end angle. Both angles are in degrees. The rotation
* is performed around a center point on the 2D space, definied by a pair
* of X and Y coordinates, called centerX and centerY. When the animation
* starts, a translation on the Z axis (depth) is performed. The length
* of the translation can be specified, as well as whether the translation
* should be reversed in time.
*
* @param fromDegrees the start angle of the 3D rotation 起始角度
* @param toDegrees the end angle of the 3D rotation 终止角度
* @param centerX the X center of the 3D rotation 3d旋转的X轴中心
* @param centerY the Y center of the 3D rotation 3d旋转的Y轴中心
* @param reverse true if the translation should be reversed, false otherwise
* 为true,则从当前视角远离屏幕,为false,从一定的深度,靠近正常视角
*/
public Rotate3dAnimation(float fromDegrees, float toDegrees,
float centerX, float centerY, float depthZ, boolean reverse) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mCenterX = centerX;
mCenterY = centerY;
mDepthZ = depthZ;
mReverse = reverse;
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float fromDegrees = mFromDegrees;
float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
final float centerX = mCenterX;
final float centerY = mCenterY;
final Camera camera = mCamera;
final Matrix matrix = t.getMatrix();
camera.save();
//改变Z轴的值,若reverse为true,从正常视角,向屏幕朝里,逐渐改变深度
if (mReverse) {
camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
} else {
camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
}
//沿着Y轴旋转
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
MainActivity
public class MainActivity extends AppCompatActivity implements
AdapterView.OnItemClickListener, View.OnClickListener {
private ListView mPhotosList;
private ViewGroup mContainer;
private ImageView mImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initListener();
}
private void initListener() {
mPhotosList.setOnItemClickListener(this);
mImageView.setOnClickListener(this);
}
private void initView() {
mPhotosList = findViewById(android.R.id.list);
mImageView = findViewById(R.id.picture);
mContainer = findViewById(R.id.container);
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, PHOTOS_NAMES);
mPhotosList.setAdapter(adapter);
mImageView.setClickable(true);
mImageView.setFocusable(true);
//设置布局时子view动画缓存策略
mContainer.setPersistentDrawingCache(ViewGroup.PERSISTENT_ANIMATION_CACHE);
}
/**
* 设置3d旋转
*
* @param position 根据position判断是要进行imageview(position=-1)的动画,还是listview的动画
* @param start 旋转的起始角度
* @param end 旋转的终止角度
*/
private void applyRotation(int position, float start, float end) {
// Find the center of the container
final float centerX = mContainer.getWidth() / 2.0f;
final float centerY = mContainer.getHeight() / 2.0f;
//创建一个3d动画,远离屏幕
final Rotate3dAnimation rotation =
new Rotate3dAnimation(start, end, centerX, centerY, 310.0f, true);
rotation.setDuration(500);
rotation.setFillAfter(true);
//设置插值器,效果为逐渐加速
rotation.setInterpolator(new AccelerateInterpolator());
//设置监听是为了在动画完成后触发下一个动画
rotation.setAnimationListener(new DisplayNextView(position));
mContainer.startAnimation(rotation);
}
//点击listview的条目
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
// 先设置imageview的资源
mImageView.setImageResource(PHOTOS_RESOURCES[position]);
applyRotation(position, 0, 90);
}
//点击imageview
public void onClick(View v) {
applyRotation(-1, 180, 90);
}
/**
* 监听动画结束,进行下半部分的动画
*/
private final class DisplayNextView implements Animation.AnimationListener {
private final int mPosition;
private DisplayNextView(int position) {
mPosition = position;
}
public void onAnimationStart(Animation animation) {
}
public void onAnimationEnd(Animation animation) {
mContainer.post(new SwapViews(mPosition));
}
public void onAnimationRepeat(Animation animation) {
}
}
/**
* 开始下半部分的动画
*/
private final class SwapViews implements Runnable {
private final int mPosition;
public SwapViews(int position) {
mPosition = position;
}
public void run() {
final float centerX = mContainer.getWidth() / 2.0f;
final float centerY = mContainer.getHeight() / 2.0f;
Rotate3dAnimation rotation;
if (mPosition > -1) {
mPhotosList.setVisibility(View.GONE);
mImageView.setVisibility(View.VISIBLE);
mImageView.requestFocus();
rotation = new Rotate3dAnimation(90, 180, centerX, centerY, 310.0f, false);
} else {
mImageView.setVisibility(View.GONE);
mPhotosList.setVisibility(View.VISIBLE);
mPhotosList.requestFocus();
rotation = new Rotate3dAnimation(90, 0, centerX, centerY, 310.0f, false);
}
rotation.setDuration(500);
rotation.setFillAfter(true);
//逐渐减速
rotation.setInterpolator(new DecelerateInterpolator());
mContainer.startAnimation(rotation);
}
}
//照片名称
private static final String[] PHOTOS_NAMES = new String[]{
"Lyon",
"Livermore",
"Tahoe Pier",
"Lake Tahoe",
"Grand Canyon",
"Bodie"
};
//照片资源
private static final int[] PHOTOS_RESOURCES = new int[]{
R.drawable.photo1,
R.drawable.photo2,
R.drawable.photo3,
R.drawable.photo4,
R.drawable.photo5,
R.drawable.photo6
};
}
翻转过后,图片其实是反过来的,修改翻转的起止角度就可以了(如可以把第一次的下半程动画角度修改为270-360度)
源码点我