先看一下我的效果图吧:
好大的图啊!!!
百度音乐由一个很酷的功能,当前的UI可以滑动,然后看见上一个活动的UI,当时顿时觉得百度的牛人好多啊,能将如此前沿的技术应用到app上。当然如果你熟悉了Android的框架,熟知Activity的布局原理,那么实现起来还是很简单的。本人粗略的实现过,用的是View.layout(l, t, r, b)方法移动布局,总觉得有点山寨,但终究还是实现了嘛。好了不多说了,看我自己实现的方式吧。
首先准备创建两个Activity,至于布局xml文件怎么写,大家随便了,两个活动用一个xml布局即可。
为了方便大家copy(PS:本来我不想全部代码包括xml文件都粘贴在这里的,但是鉴于有些人实在太懒,连布局文件都不愿意随便写一个,然后在评论里喊,楼主,源码!我还是贴出来吧!)
一个简单的布局xml文件:layout_value_animation_layout
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" /> <ToggleButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="ToggleButton" /> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="CheckBox" /> <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="RadioButton" /> <SeekBar android:layout_width="match_parent" android:layout_height="wrap_content" /> <RatingBar android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/layout_value_animation_layout_text" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:gravity="center" android:text="A" android:textAppearance="?android:attr/textAppearanceLarge" android:textSize="80sp" /></LinearLayout>
第一个活动: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)); } }); }}
第二个活动:LayoutValueAnimationExampleB
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); }}
许多人可能要崩溃了,怎么回事,这么多类没有,由一些类是我自己库中常用的工具类,比如ScreenHelper,这个类没什么功能,就是得到屏幕的宽度啥的,调用初始化方法
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); <br> * 参数配置类:[email protected] 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; /** * [email protected] LayoutValueAnimation} 的辅助参数类,用于指定位移和移动方向 <br> * * @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 * 指明运动方向,有<br> * [email protected] LayoutValueParams.DIRECTION_LEFT},<br> * [email protected] LayoutValueParams.DIRECTION_RIGHT},<br> * [email protected] LayoutValueParams.DIRECTION_TOP},<br> * [email protected] LayoutValueParams.DIRECTION_BOTTOM},<br> */ 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); } }}
卧槽,又有一个类:AbstractAnimation
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); } }}
没完了:AbstractAnimationImpl
package org.mrchen.commlib.animation;import android.view.View;public interface AbstractAnimationImpl { public boolean computeSize();// 计算变量 public void applySize();// 应用计算的变量 public void startAnimation(View v);// 启动动画}
好了,代码基本上差不多了,最后贴一下AndroidManifest.xml文件就打工告成了,大家可以运行看下效果。
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.mrchen.commlib.example" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="14" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="org.mrchen.commlib.example.LayoutValueAnimationExampleA" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="org.mrchen.commlib.example.LayoutValueAnimationExampleB" android:label="@string/app_name" android:theme="@style/AppTheme.Transparent" > </activity> </application></manifest>看来还有主题没贴出来:
<resources> <!-- Base application theme, dependent on API level. This theme is replaced by AppBaseTheme from res/values-vXX/styles.xml on newer devices. --> <style name="AppBaseTheme" parent="android:Theme.Black.NoTitleBar"> <!-- Theme customizations available in newer API levels can go in res/values-vXX/styles.xml, while customizations related to backward-compatibility can go here. --> </style> <!-- Application theme. --> <style name="AppTheme" parent="AppBaseTheme"> <!-- All customizations that are NOT specific to a particular API-level can go here. --> <item name="android:windowContentOverlay">@null</item> </style> <style name="AppTheme.Transparent" parent="AppTheme"> <!-- All customizations that are NOT specific to a particular API-level can go here. --> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:colorBackgroundCacheHint">@null</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowAnimationStyle">@android:style/Animation</item> </style></resources>