大家好我是akira 今天咱们玩一个非常好玩的东西 来先看下面一段短片
SO 这是什么 想必大家在上下班或者出入商场的时候太常见了 他就是一个旋转门而已
我们人跟着旋转门进去 然后再出来 就这么简单 可能还有更简单的 就是只有一个翻转的门
这种在我们看一些电影里面都有一些类似的几关或者密道都是采用这种门 只不过后者更为简单 只是将两扇门精简成了一扇
但是效果一样 当然咱们今天说的不是研究什么门的问题 咱们既然是移动开发人员 自然会思考这玩意在我们的
andriod客户端会怎么实现
咱们今天介绍一种非常简单的办法 用普通的动画就能实现 有人可能会惊讶 因为这个东西是涉及到3D的一些效果 那么你用2D的
动画是可以实现的么 答案是yes。因为我们知道我们的肉眼是容易被欺骗的 一个简单的例子就是我们大家都看过魔术 什么光影魔术
近景魔术 我们大家都非常清楚 魔术是假的 但是我们的肉眼却非常清晰的看到这个东西确实是非常神奇的消失在了魔术师手中 那么我们就可以得到一个结论 有时候肉眼是靠不住的 他反而会给我带来一种欺骗的感觉 下面的一个例子就是最好的证明
怎么样 上图的柱子是圆的还是方的 你究竟看出来了么 或者说是有三个柱子还是有两个柱子呢
OK 既然肉眼是可以欺骗的 那么我们就可以做一个伪3D效果 核心类就是我们动画中的缩放动画
我们根据经验得知 人在看远处物体时会发现物体变小了 而看近时 发现物体变大
这个正符合我们的ScaleAnimation的特点
下面 就用下这个ScaleAnimation
对于ScaleAnimation最模糊的一点无非是他的一个构造 来解释下这里的问题
/** * Constructor to use when building a ScaleAnimation from code * * @param fromX Horizontal scaling factor to apply at the start of the * animation * @param toX Horizontal scaling factor to apply at the end of the animation * @param fromY Vertical scaling factor to apply at the start of the * animation * @param toY Vertical scaling factor to apply at the end of the animation * @param pivotXType Specifies how pivotXValue should be interpreted. One of * Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or * Animation.RELATIVE_TO_PARENT. * @param pivotXValue The X coordinate of the point about which the object * is being scaled, specified as an absolute number where 0 is the * left edge. (This point remains fixed while the object changes * size.) This value can either be an absolute number if pivotXType * is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise. * @param pivotYType Specifies how pivotYValue should be interpreted. One of * Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or * Animation.RELATIVE_TO_PARENT. * @param pivotYValue The Y coordinate of the point about which the object * is being scaled, specified as an absolute number where 0 is the * top edge. (This point remains fixed while the object changes * size.) This value can either be an absolute number if pivotYType * is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise. */ public ScaleAnimation(float fromX, float toX, float fromY, float toY, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) { mResources = null; mFromX = fromX; mToX = toX; mFromY = fromY; mToY = toY; mPivotXValue = pivotXValue; mPivotXType = pivotXType; mPivotYValue = pivotYValue; mPivotYType = pivotYType; initializePivotPoint(); }
这个是ScaleAnimation的构造源码 上面的注释已经很清楚的说明了 我们下面的参数都代表了什么
先看第一个 fromX 显然是从 后面的是to 显然是到 加上X就是X坐标
OK 这个理解了 后面两个Y自然也就不难 那么问题来了 后面的那四个是什么呢
我们发现 后面的那苏哥都对应一个type和value 而且是一个X和Y 显然也就是x和y轴 知道了这点之后
而看上面注释我们不难发现 这里的X和Y实际上是根据什么进行缩放 也就是我们经常说的参照物 Ok
简单了 接下来我们只需要知道有什么type这个事就搞定了
回到分析问题的轨迹上来 我们既然知道了缩放动画 我们不难得知旋转实际上就是一个缩进和展开的过程
只不过这里的缩进和展开进行了切换欺骗了我们的肉眼 让我们认为它是旋转着的
看下布局的代码
<LinearLayout 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" tools:context=".MainActivity"> <!--<TextView android:text="@string/hello_world" android:layout_width="wrap_content" android:layout_height="wrap_content" />--> <ImageView android:id="@+id/img_ad" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/bg" android:scaleType="matrix" /> </LinearLayout>
就是一张图 很简单 这里你会发现 我指定了一个matrix 这是因为下面我们要使其在转的时候有一个镜像的效果
代码如下
public class MainActivity extends Activity { private ImageView mImageView; private ScaleAnimation out; private ScaleAnimation in; private boolean ismirror= false;//是否为镜像 private MediaPlayer mPlayer; String path = "";//播放路径 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); if(mPlayer==null) mPlayer = new MediaPlayer(); // MediaPlayer mp = MediaPlayer.create(this,R.m);//这时就不用 try { mPlayer.setDataSource(path); mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mPlayer.prepare(); mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mPlayer.start(); } }); } catch (IOException e) { e.printStackTrace(); } /**/ mImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Matrix matrix = new Matrix(); if(!ismirror){ out = new ScaleAnimation(-1.0f,1.0f,1.0f,1.0f, Animation.RELATIVE_TO_PARENT,0.5f,Animation.RELATIVE_TO_PARENT,0.5f); out.setDuration(1200); out.setFillAfter(true); mImageView.startAnimation(out); matrix.postScale(-1, 1);//-1代表取相反数, 1 代表保持不变 matrix.postTranslate(mImageView.getMeasuredWidth(), 0); mImageView.setImageMatrix(matrix); ismirror = true; }else{ in = new ScaleAnimation(1.0f,-1.0f,1.0f,1.0f,Animation.RELATIVE_TO_PARENT,0.5f,Animation.RELATIVE_TO_PARENT,0.5f); in.setDuration(1200); in.setFillAfter(true); mImageView.startAnimation(in); // Matrix matrix = mImageView.getMatrix(); // Matrix matrix = new Matrix(); matrix.postScale(1, 1);//-1代表取相反数, 1 代表保持不变 matrix.postTranslate(-mImageView.getMeasuredWidth(),0); mImageView.setImageMatrix(matrix); ismirror = false; } } }); } private void initView() { mImageView = (ImageView) findViewById(R.id.img_ad); } @Override protected void onDestroy() { mPlayer.stop(); mPlayer=null; super.onDestroy(); } }
这里的代码比较简单 核心的旨在两个动画中 这里我们用一个布尔值做判断 是否为镜像
然后利用 postScale取反设置镜面效果 然后去平移这里都是用的post 不能用set和pre除非你是第一次
还有一点要注意的是postTranslate 这里平移的是位移而不是坐标 Ok 如果你还想使用音乐的话
可以利用mediaplayer进行一个播放 这里我没有做
PS:如果要播放最好建议利用service去做 正好也符合mvc思想
效果如下
下面还有一种情况是我们可能会遇到真的想变戏法一样的情况 就是说我转进去和转出来的不一样
其实这种也是利用了scaleanimation的特性 只不过 我们改变了参数而已
布局还是一张图
<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" tools:context=".MainActivity"> <ImageView android:id="@+id/image" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="fitXY" android:background="@drawable/bg" /> </RelativeLayout>
代码却不是前面的代码
代码如下
public class MainActivity extends Activity { private ImageView mImageViwe; private ScaleAnimation inTwo; private ScaleAnimation outTwo; private boolean zero = true;//是否是第一张 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); mImageViwe.setOnClickListener(new MyOnImageCilckListener()); } private void init() { mImageViwe = (ImageView) findViewById(R.id.image); inTwo = new ScaleAnimation(1.0f,0.0f,1.0f,1.0f, Animation.RELATIVE_TO_PARENT,0.5f, Animation.RELATIVE_TO_PARENT,0.5f); inTwo.setDuration(500); inTwo.setFillAfter(true); outTwo = new ScaleAnimation(0.0f,1.0f,1.0f,1.0f, Animation.RELATIVE_TO_PARENT,0.5f, Animation.RELATIVE_TO_PARENT,0.5f); outTwo.setDuration(500); outTwo.setFillAfter(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.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); }*/ class MyOnImageCilckListener implements View.OnClickListener{ @Override public void onClick(View v) { if(zero){ mImageViwe.startAnimation(inTwo); inTwo.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { mImageViwe.startAnimation(outTwo); Bitmap map2 = BitmapFactory.decodeResource(getResources(), R.drawable.bg2); mImageViwe.setImageBitmap(map2); } @Override public void onAnimationRepeat(Animation animation) { } }); zero = false; }else{ mImageViwe.startAnimation(inTwo); inTwo.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { mImageViwe.startAnimation(outTwo); Bitmap map = BitmapFactory.decodeResource(getResources(),R.drawable.bg); mImageViwe.setImageBitmap(map); } @Override public void onAnimationRepeat(Animation animation) { } }); zero = true; } } } }
这个周期什么的都是你自己去定义 相反如果你不定义周期 那么你的动画将不会有任何效果
这里还是用布尔值做判断 我们发现由于是绕着中心去转动 所以当转没了 也就是动画结束后出现白屏之前 我们得要把下一张展现出来
这里就利用了animationListener这个监听 在onAnimationEnd结束的回调中去展示一张新的
好了 这样我们的第二种就做好了
看下效果
哈哈 买demo送零式 已经成了目前国内主机玩家的一个梗 不过个人还是非常期待FF新作的
本期咱们就到这 下期我和各位不见不散