先看一下我的效果图吧:
好大的图啊!!!
百度音乐由一个很酷的功能,当前的UI可以滑动,然后看见上一个活动的UI,当时顿时觉得百度的牛人好多啊,能将如此前沿的技术应用到app上。当然如果你熟悉了Android的框架,熟知Activity的布局原理,那么实现起来还是很简单的。本人粗略的实现过,用的是View.layout(l, t, r, b)方法移动布局,总觉得有点山寨,但终究还是实现了嘛。好了不多说了,看我自己实现的方式吧。
首先准备创建两个Activity,至于布局xml文件怎么写,大家随便了,两个活动用一个xml布局即可。
为了方便大家copy(PS:本来我不想全部代码包括xml文件都粘贴在这里的,但是鉴于有些人实在太懒,连布局文件都不愿意随便写一个,然后在评论里喊,楼主,源码!我还是贴出来吧!)
一个简单的布局xml文件:layout_value_animation_layout
第一个活动:LayoutValueAnimationExampleA
package org.mrchen.commlib.example;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
public class LayoutValueAnimationExampleA extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View root = getLayoutInflater().inflate(R.layout.layout_value_animation_layout, null);
setContentView(root);
//
root.setBackgroundColor(Color.parseColor("#978856"));
TextView text = (TextView) findViewById(R.id.layout_value_animation_layout_text);
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 25);
text.setText("A\n Click Me go to Activity LayoutValueAnimationExampleB");
text.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
startActivity(new Intent(LayoutValueAnimationExampleA.this, LayoutValueAnimationExampleB.class));
}
});
}
}
package org.mrchen.commlib.example;
import org.mrchen.commlib.animation.LayoutValueAnimation;
import org.mrchen.commlib.animation.LayoutValueAnimation.LayoutValueParams;
import org.mrchen.commlib.animation.SmoothInterpolator;
import org.mrchen.commlib.helper.LogHelper;
import org.mrchen.commlib.helper.ScreenHelper;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.TextView;
/**
* 注意当前的Activity在AndroidManifest.xml中的注册信息,主题应用了透明主题:AppTheme.Transparent
*
* @author chenjianli
*
*/
public class LayoutValueAnimationExampleB extends Activity {
private final String TAG = "LayoutValueAnimationExampleB";
private View mRootView;
private LayoutValueAnimation mLayoutValueAnimation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mRootView = getLayoutInflater().inflate(R.layout.layout_value_animation_layout, null);
setContentView(mRootView);
//
mRootView.setBackgroundColor(Color.parseColor("#46b525"));
TextView text = (TextView) findViewById(R.id.layout_value_animation_layout_text);
text.setText("B");
mRootView.setOnTouchListener(movingEventListener);
}
private OnTouchListener movingEventListener = new OnTouchListener() {
int lastX, lastY;
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
lastX = (int) event.getRawX();
lastY = (int) event.getRawY();
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
int dx = (int) event.getRawX() - lastX;
int dy = (int) event.getRawY() - lastY;
// TODO
dy = 0;
int left = v.getLeft() + dx;
int top = v.getTop() + dy;
int right = v.getRight() + dx;
int bottom = v.getBottom() + dy;
v.layout(left, top, right, bottom);
lastX = (int) event.getRawX();
lastY = (int) event.getRawY();
}
if (event.getAction() == MotionEvent.ACTION_UP) {
int left = v.getLeft();
int top = v.getTop();
int right = v.getRight();
int bottom = v.getBottom();
LogHelper.d(TAG, "l:" + left + ",t:" + top + ",r:" + right + ",b:" + bottom);
finishActivity(v, ScreenHelper.mScreenWidth - left);
}
return true;
}
};
public boolean onKeyDown(int keyCode, android.view.KeyEvent event) {
if (!isFinishing()) {
finishActivity(mRootView, ScreenHelper.mScreenWidth);
return true;
}
return super.onKeyDown(keyCode, event);
};
private void finishActivity(View v, int size) {
int duration = 666;
if (mLayoutValueAnimation == null) {
ScreenHelper.initialize(LayoutValueAnimationExampleB.this);
mLayoutValueAnimation = new LayoutValueAnimation(new LayoutValueParams(size,
LayoutValueParams.DIRECTION_RIGHT), duration);
mLayoutValueAnimation.setInterpolator(new SmoothInterpolator());
}
mLayoutValueAnimation.startAnimation(v);
v.postDelayed(new Runnable() {
@Override
public void run() {
LayoutValueAnimationExampleB.this.finish();
}
}, duration);
}
}
initialize(context)
完成初始化的工作,就可以拿来用了。
这里由必要贴一下
LayoutValueAnimation
类,这个类是拜一位大侠的文章所赐,产生出的属于我自己的衍生品,大家拿来用就好,具体的注释我已经卸载类里面了。
package org.mrchen.commlib.animation;
import org.mrchen.commlib.animation.LayoutValueAnimation.LayoutValueParams;
import org.mrchen.commlib.helper.LogHelper;
import android.view.View;
import android.view.animation.AnimationUtils;
/**
* 促使调用mTargetView.layout(l,t,r,b);
* 参数配置类:{@link LayoutValueParams}
*
* @author chenjianli
*
*/
public class LayoutValueAnimation extends AbstractAnimation {
private final static String TAG = "LayoutValueAnimation";
private int mCurrSize;
private int originalLeft;
private int originalRight;
private int originalTop;
private int originalBottom;
private int targetLeft;
private int targetRight;
private int targetTop;
private int targetBottom;
/**
* {@link LayoutValueAnimation} 的辅助参数类,用于指定位移和移动方向
*
* @author chenjianli
*
*/
public static class LayoutValueParams {
public int size;
public static final int DIRECTION_LEFT = 1;
public static final int DIRECTION_RIGHT = 2;
public static final int DIRECTION_TOP = 3;
public static final int DIRECTION_BOTTOM = 4;
public int direction;
/**
*
* @param size
* 只能是正整数
* @param direction
* 指明运动方向,有
* {@link LayoutValueParams.DIRECTION_LEFT},
* {@link LayoutValueParams.DIRECTION_RIGHT},
* {@link LayoutValueParams.DIRECTION_TOP},
* {@link LayoutValueParams.DIRECTION_BOTTOM},
*/
public LayoutValueParams(int size, int direction) {
this.size = size;
this.direction = direction;
}
}
private LayoutValueParams mParams;
public LayoutValueAnimation(LayoutValueParams params, int duration) {
mParams = params;
mDuration = duration;
}
// 启动动画
public void startAnimation(View view) {
if (view != null) {
mTargetView = view;
} else {
LogHelper.e(TAG, "view 不能为空");
return;
}
if (isFinished) {
mDurationReciprocal = 1.0f / (float) mDuration;
isFinished = false;
// 记录下动画开始的时间
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDSize = mParams.size;
LogHelper.d(TAG, "mDSize=" + mDSize);
int l = mTargetView.getLeft();
int t = mTargetView.getTop();
int r = mTargetView.getRight();
int b = mTargetView.getBottom();
LogHelper.d(TAG, "startAnimation >原始的> l = " + l + ", t = " + t + ", r = " + r + ", b = " + b);
originalLeft = l;
originalRight = r;
originalTop = t;
originalBottom = b;
mHandler.start();
}
}
@Override
public boolean computeSize() {
// TODO Auto-generated method stub
if (isFinished) {
return isFinished;
}
int timePassed = (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed <= mDuration) {
float x = timePassed * mDurationReciprocal;
if (mInterpolator != null) {
x = mInterpolator.getInterpolation(x);
}
switch (mParams.direction) {
case LayoutValueParams.DIRECTION_LEFT:
case LayoutValueParams.DIRECTION_TOP:
mCurrSize = -Math.round(x * mDSize);
break;
case LayoutValueParams.DIRECTION_RIGHT:
case LayoutValueParams.DIRECTION_BOTTOM:
mCurrSize = Math.round(x * mDSize);
break;
}
} else {
isFinished = true;
switch (mParams.direction) {
case LayoutValueParams.DIRECTION_LEFT:
case LayoutValueParams.DIRECTION_TOP:
mCurrSize = -mDSize;
break;
case LayoutValueParams.DIRECTION_RIGHT:
case LayoutValueParams.DIRECTION_BOTTOM:
mCurrSize = mDSize;
break;
}
}
// 计算最终目标坐标
switch (mParams.direction) {
case LayoutValueParams.DIRECTION_LEFT:
case LayoutValueParams.DIRECTION_RIGHT:
targetLeft = originalLeft + mCurrSize;
targetRight = originalRight + mCurrSize;
targetTop = originalTop;
targetBottom = originalBottom;
break;
case LayoutValueParams.DIRECTION_TOP:
case LayoutValueParams.DIRECTION_BOTTOM:
targetTop = originalTop + mCurrSize;
targetBottom = originalBottom + mCurrSize;
targetLeft = originalLeft;
targetRight = originalRight;
break;
}
LogHelper.d(TAG, "computeSize >目标> l = " + targetLeft + ", t = " + targetTop + ", r = " + targetRight
+ ", b = " + targetBottom);
applySize();
return isFinished;
}
@Override
public void applySize() {
// TODO Auto-generated method stub
if (mTargetView != null && mTargetView.getVisibility() != View.GONE) {
mTargetView.layout(targetLeft, targetTop, targetRight, targetBottom);
}
}
}
package org.mrchen.commlib.animation;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.animation.Interpolator;
public abstract class AbstractAnimation implements AbstractAnimationImpl {
private final int FRAME_TIME = 20;// 一帧的时间 毫秒
protected View mTargetView;// 执行"动画"的目标View
protected Interpolator mInterpolator;// 插值器
protected boolean isFinished = true;// 动画结束标识;
protected int mDuration; // 动画运行的时间
protected long mStartTime;// 动画开始时间
protected float mDurationReciprocal;// Reciprocal:相互的,倒数的
protected int mDSize; // 需要改变view大小的增量
private AnimationListener mAnimationListener;
public interface AnimationListener {
public void animationEnd(View v);
}
public void setOnAnimationListener(AnimationListener listener) {
mAnimationListener = listener;
}
public void setInterpolator(Interpolator interpolator) {
mInterpolator = interpolator;
}
public boolean isFinished() {
return isFinished;
}
public void setDuration(int duration) {
mDuration = duration;
}
protected AnimationHandler mHandler = new AnimationHandler();
class AnimationHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1) {
if (!computeSize()) {
mHandler.sendEmptyMessageDelayed(1, FRAME_TIME);
} else {
if (mAnimationListener != null) {
mAnimationListener.animationEnd(mTargetView);
}
}
}
super.handleMessage(msg);
}
public void start() {
sendEmptyMessage(1);
}
}
}
package org.mrchen.commlib.animation;
import android.view.View;
public interface AbstractAnimationImpl {
public boolean computeSize();// 计算变量
public void applySize();// 应用计算的变量
public void startAnimation(View v);// 启动动画
}
看来还有主题没贴出来: