Android高级应用开发(深入篇) stage3(下)- 高级商用界面开发 学习笔记
2014-03-03 15:14
806 查看
动画
动画实质是什么 ?
Android动画实现机制
Android动画分类及编写方式
Property Animation属性动画是通过修改元素的属性值产生动画效果,需要3.0以上才能支持
View Animtion视图动画:只能作用在视图上,也称为Tween Animation
Drawable Animation:一帧一帧的显示图片资源,也称为Frame Animation
动画可不影响其他控件
动画有一个很重要的类,Transformation
我们换算,总要知道换算的是什么东西,所以还有一个重要的类Animation
通过Animation这个类定义动画模型或动画参数,传入到Transformation中进行运算,再由View呈现出来
View类中也有startAnimation和setAnimation方法设置动画
使用配置文件也能实现相同的效果(TweenAnimation)
在res\anim文件夹下配置xml
使用xml文件
FrameAnimation也可以使用配置文件进行设定
但是不是放在res\anim文件夹下,而是在drawable文件夹下
注意根节点是<animation-list>
使用时:
AnimationDrawable间接继承自Drawable,实际上是一个Drawable容器
还能实现3D的旋转
平面旋转是以X轴和Y轴进行的旋转
而3D的旋转是Z轴的旋转
可以找一些例子看一下
Interpolator
是一个用来改变动画的速度比率的接口(针对TweenAnimation动画)
Interpolator定义了动画的执行过程中会如何改变。
下面的实现子类可以实现不同的速率模式
使用的方法也很简单
可以在配置文件中配置
也可以在代码中实现
AnimationSet构造器的参数是本动画集合下的动画是否共享定义的Interpolator
如果设为true,则共享同一个Interpolator,如果设为false,则需要各自定义各自的Interpolator
总结使用方法
1、创建动画
(1)在xm中定义
动画效果的定义应放在 res\anim目录下。
以下这两段段代码,通过在x轴连续7次的横向移动,实现了一个震动效果的动画:
cycle_7.xml
shake.xml
(2)通过代码创建
这段代码同样实现了一个震动效果。
2、为View指定动画
通过调用View.startAnimation, 即可立即启动动画效果。
3、监控动画的执行状态
通过为动画设定AnimationListener,我们可以知道动画的执行状况:开始、完成、重复执行。
Android动画实现原理
图形变换通过矩阵实现。图形变换是图形学中的基本知识。简单来说就是,每种变换都是一次矩阵运算。
在 Android 中,Canvas 类中包含当前矩阵,当调用 Canvas.drawBitmap (bmp, x, y, Paint) 绘制时,
android 会先把 bmp 做一次矩阵运算,然后将运算的结果显示在 Canvas 上。
这样,编程人员只需不断修改 Canvas 的矩阵并刷新屏幕,
View 里的对象就会不停的做图形变换,动画就形成了。
实际上变换的是画布Canvas
动画运行模式
1、独占模式
即程序主线程进入一个循环,根据动画指令不断刷新屏幕,直到动画结束
2、中断模式
即有单独一个线程对时间计数,每隔一定的时间向主线程发通知,主线程接到通知后更新屏幕
Animation类
每个动画都重载了父类的 applyTransformation 方法,这个方法会被父类的 getTransformation 方法调用。
另外每个动画还有个 initialize 方法,完成初始化工作。
Interpolator类
Transformation类
Transformation 记录了仿射矩阵 Matrix,动画每触发一次,会对原来的矩阵做一次运算,
View 的 Bitmap 与这个矩阵相乘就可实现相应的操作(旋转、平移、缩放等)。
Transformation 类封装了矩阵和 alpha 值,它有两个重要的成员,一是 mMatrix,二是 mAlpha。
View中实现动画的过程
1、view 创建动画对象,设置动画属性,调用 invalidate 刷新屏幕,启动动画;
2、invalidate 方法触发了 onDraw 函数;
3、在 onDraw 函数中:
4、调用动画的 getTransformation 方法,得到当前时间点的矩阵
5、将该矩阵设置成 Canvas 的当前矩阵
6、调用 canvas 的 drawBitmap 方法,绘制屏幕。
7、判断 getTransformation 的返回值,若为真,调用 invalidate 方法,刷新屏幕进入下一桢;若为假,说明动画完成。
为Activity指定动画效果
修改Activity Theme
1. 在styles.xml中输入以下代码:
看一下定义的动画文件
2. 然后在themes.xml中
3. 在AndroidManifest.xml中为Activity指定theme.
或者也可以使用代码设定
通过调用 overridePendingTransition() 可以实时修改Activity的切换动画。
但需注意的是:该函数必须在调用startActivity()或finish()后立即调用,且只有效一次。
Drag
如果想要按住时有一些效果,比如变大,等等,需要附加一个层,隐藏原来的View,这就需要用到shadow(DragShadow?)
还有如果想要一种吸附边缘的效果,就需要一些类似于单元格的容器(类似于GridView)
图片切换效果思路:
几个View叠在一起,切换时,上面的View在横向轴左移,-100%p(left-out),同时alpha值从1降到0
同时下一层View出现,从100%p进到主页面(right-in),同时alpha从0加到1,就实现了切换效果
系统也提供给我们一些相关的类,比如ViewAnimator
ViewAnimator有两个子类,ViewFlipper和ViewSwitcher
Sample:ViewFlipper实现最常见应用
1,定义四个动画 , fade_left_in fade_left_out fade_right_in fade_right_out
fade_left_in
fade_left_out
fade_right_in
fade_right_out
2,定义layout文件 。 <ViewAnimator> or <ViewFlipper>
说明:android:flipInterval 显示下一个视图的时间间隔
3,写代码 , onCreate() 取得Flipper对象设置好属性 。
4,事件 , GestureDetector .
5,onFling() , 设置 viewFlipper设置动画以及调用下一个ViewGroup
Activity的onTouchEvent一定要与GestureDetector绑定
当然也可以用某控件与GestureDetector绑定,仅对改控件手势有效
案例(模仿Apple程序列表的抖动效果)
Steps:
1, 通过PackageManager 加载应用图标到GridView里
2,定义好item的shake动画 , longclick的时候 , 启动动画
3,随着鼠标的移动 , 判断进入到了哪个Dock (哪个单元格区域)PointToPosition,从区域转换为index , 然后交换单元格视图(Swap)(先交换adapter ,绑定视图,视图就交换了)
4,Drop的时候把原来的图标放到当前单元格
具体看代码,有个ResolveInfo类是PackageManager中应用信息的结果
一共有五个类
ItemContainner
ItemContainerAdapter
ItemMover
ApplicationInfoContainer
ShakeDesktopActivity
动画实质是什么 ?
Android动画实现机制
Android动画分类及编写方式
Property Animation属性动画是通过修改元素的属性值产生动画效果,需要3.0以上才能支持
View Animtion视图动画:只能作用在视图上,也称为Tween Animation
Drawable Animation:一帧一帧的显示图片资源,也称为Frame Animation
动画可不影响其他控件
动画有一个很重要的类,Transformation
我们换算,总要知道换算的是什么东西,所以还有一个重要的类Animation
通过Animation这个类定义动画模型或动画参数,传入到Transformation中进行运算,再由View呈现出来
View类中也有startAnimation和setAnimation方法设置动画
public class MainActivity extends Activity { Button mButtonAlpha; Button mButtonRotate; Button mButtonScale; Button mButtonTranslate; Button mButtonComplex; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mButtonAlpha = (Button)findViewById(R.id.button_alpha); mButtonRotate = (Button)findViewById(R.id.button_rotate); mButtonScale = (Button)findViewById(R.id.button_scale); mButtonTranslate = (Button)findViewById(R.id.button_translate); mButtonComplex = (Button)findViewById(R.id.button_complex); mButtonAlpha.setOnClickListener(new AnimationListener(AnimationType.Alpha)); mButtonRotate.setOnClickListener(new AnimationListener(AnimationType.Rotate)); mButtonScale.setOnClickListener(new AnimationListener(AnimationType.Scale)); mButtonTranslate.setOnClickListener(new AnimationListener(AnimationType.Translate)); mButtonComplex.setOnClickListener(new AnimationListener(AnimationType.conplex)); } enum AnimationType{ Alpha, Rotate, Scale, Translate, conplex } class AnimationListener implements OnClickListener{ private AnimationType mAnimationType; public AnimationListener(AnimationType animationType){ mAnimationType = animationType; } @Override public void onClick(View view) { switch (mAnimationType){ case Alpha: Log.i("Animation demo", "alpha"); // 定义动画 /** * public AlphaAnimation(float fromAlpha, float toAlpha) * fromAlpha: 开始时的alpha值 * toAlpha: 结束时的alpha值 */ Animation alphaAnimation = new AlphaAnimation(1.0f, 0.1f); alphaAnimation.setDuration(1000); alphaAnimation.setFillBefore(true); // 动画结束停止在起始的样子 // alphaAnimation.setFillAfter(true); // 动画结束停止在结束的样子 alphaAnimation.setRepeatCount(1); // 动画重复次数 alphaAnimation.setRepeatMode(Animation.REVERSE); // 重复模式 alphaAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } }); // 启动动画 mButtonAlpha.startAnimation(alphaAnimation); break; case Rotate: Log.i("Animation demo", "rotate"); // 定义动画 /** * public RotateAnimation(float fromDegrees, float toDegrees, * int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) * * fromDegrees: 起始时的角度 * toDegrees:结束时的角度 * pivotXType、pivotYValue:有Animation.ABSOLUTE、Animation.RELATIVE_TO_SELF、Animation.RELATIVE_TO_PARENT,轴的参照物 * pivotXValue、pivotYType:轴距离参照物的坐标,值在0~1之间,1表示100% */ Animation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 0); rotateAnimation.setDuration(1000); rotateAnimation.setRepeatCount(1); rotateAnimation.setRepeatMode(Animation.REVERSE); // 启动动画 // mButtonRotate.setAnimation(rotateAnimation); // 只是设置了动画,不会立即启动 mButtonRotate.startAnimation(rotateAnimation); break; case Scale: // 定义动画 /** * public ScaleAnimation(float fromX, float toX, float fromY, float toY, * int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) */ Animation scaleAnimation = new ScaleAnimation(1, 1.5f, 1, 1.5f, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0); scaleAnimation.setDuration(1000); scaleAnimation.setRepeatCount(1); scaleAnimation.setRepeatMode(Animation.REVERSE); // 启动动画 mButtonScale.startAnimation(scaleAnimation); break; case Translate: // 定义动画 /** * public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue, * int fromYType, float fromYValue, int toYType, float toYValue) */ Animation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 2, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 2); translateAnimation.setDuration(1000); translateAnimation.setRepeatCount(1); translateAnimation.setRepeatMode(Animation.REVERSE); // 启动动画 mButtonTranslate.startAnimation(translateAnimation); break; case conplex: // 动画集,能综合Tween Animation的效果,参数为是否共享Interpolator AnimationSet sets = new AnimationSet(false); Animation alpha = new AlphaAnimation(1.0f, 0.1f); alpha.setDuration(1000); alpha.setRepeatCount(1); // 动画重复次数 alpha.setRepeatMode(Animation.REVERSE); // 重复模式 Animation rotate = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 0); rotate.setDuration(1000); rotate.setRepeatCount(1); rotate.setRepeatMode(Animation.REVERSE); Animation scale = new ScaleAnimation(1, 1.5f, 1, 1.5f, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0); scale.setDuration(1000); scale.setRepeatCount(1); scale.setRepeatMode(Animation.REVERSE); Animation translate = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 2, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 2); translate.setDuration(1000); translate.setRepeatCount(1); translate.setRepeatMode(Animation.REVERSE); sets.addAnimation(alpha); sets.addAnimation(rotate); sets.addAnimation(scale); sets.addAnimation(translate); mButtonComplex.startAnimation(sets); break; default: break; } } } private OnClickListener onTextClickListener = new OnClickListener() { @Override public void onClick(View view) { } }; }
使用配置文件也能实现相同的效果(TweenAnimation)
在res\anim文件夹下配置xml
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <alpha android:fromAlpha="1" android:toAlpha="0.1" android:duration="1000" android:repeatCount="1" android:repeatMode="reverse"/> </set>
使用xml文件
// from XML AnimationSet animationSet = (AnimationSet)AnimationUtils.loadAnimation(MainActivity.this, R.anim.my_animation); mButtonXml.startAnimation(animationSet);
FrameAnimation也可以使用配置文件进行设定
但是不是放在res\anim文件夹下,而是在drawable文件夹下
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/drawable1" android:duration="200" /> <item android:drawable="@drawable/drawable2" android:duration="200" /> <item android:drawable="@drawable/drawable3" android:duration="200" /> <item android:drawable="@drawable/drawable4" android:duration="200" /> <item android:drawable="@drawable/drawable5" android:duration="200" /> <item android:drawable="@drawable/drawable6" android:duration="200" /> </animation-list>
注意根节点是<animation-list>
使用时:
public class AnimationListActivity extends Activity { private ImageView mImageView; private AnimationDrawable animationDrawable; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.frame_animation); mImageView = (ImageView)findViewById(R.id.imageView); // 设置背景为动画列表 mImageView.setBackgroundResource(R.drawable.frame_drawable); animationDrawable = (AnimationDrawable)mImageView.getBackground(); mImageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { animationDrawable.setOneShot(true); // 只运行一次,不循环 animationDrawable.start(); } }); } }
AnimationDrawable间接继承自Drawable,实际上是一个Drawable容器
还能实现3D的旋转
平面旋转是以X轴和Y轴进行的旋转
而3D的旋转是Z轴的旋转
可以找一些例子看一下
Interpolator
是一个用来改变动画的速度比率的接口(针对TweenAnimation动画)
Interpolator定义了动画的执行过程中会如何改变。
下面的实现子类可以实现不同的速率模式
使用的方法也很简单
可以在配置文件中配置
也可以在代码中实现
AnimationSet构造器的参数是本动画集合下的动画是否共享定义的Interpolator
如果设为true,则共享同一个Interpolator,如果设为false,则需要各自定义各自的Interpolator
总结使用方法
1、创建动画
(1)在xm中定义
动画效果的定义应放在 res\anim目录下。
以下这两段段代码,通过在x轴连续7次的横向移动,实现了一个震动效果的动画:
cycle_7.xml
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android" android:cycles="7" />
shake.xml
<translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="0" android:toXDelta="10" android:duration="1000" android:interpolator="@anim/cycle_7" />
(2)通过代码创建
Animation anim = new TranslateAnimation(0, 10, 0, 0); anim.setDuration(1000); anim.setInterpolator(new CycleInterpolator(7));
这段代码同样实现了一个震动效果。
2、为View指定动画
通过调用View.startAnimation, 即可立即启动动画效果。
3、监控动画的执行状态
通过为动画设定AnimationListener,我们可以知道动画的执行状况:开始、完成、重复执行。
Android动画实现原理
图形变换通过矩阵实现。图形变换是图形学中的基本知识。简单来说就是,每种变换都是一次矩阵运算。
在 Android 中,Canvas 类中包含当前矩阵,当调用 Canvas.drawBitmap (bmp, x, y, Paint) 绘制时,
android 会先把 bmp 做一次矩阵运算,然后将运算的结果显示在 Canvas 上。
这样,编程人员只需不断修改 Canvas 的矩阵并刷新屏幕,
View 里的对象就会不停的做图形变换,动画就形成了。
实际上变换的是画布Canvas
动画运行模式
1、独占模式
即程序主线程进入一个循环,根据动画指令不断刷新屏幕,直到动画结束
2、中断模式
即有单独一个线程对时间计数,每隔一定的时间向主线程发通知,主线程接到通知后更新屏幕
Animation类
每个动画都重载了父类的 applyTransformation 方法,这个方法会被父类的 getTransformation 方法调用。
另外每个动画还有个 initialize 方法,完成初始化工作。
Interpolator类
Transformation类
Transformation 记录了仿射矩阵 Matrix,动画每触发一次,会对原来的矩阵做一次运算,
View 的 Bitmap 与这个矩阵相乘就可实现相应的操作(旋转、平移、缩放等)。
Transformation 类封装了矩阵和 alpha 值,它有两个重要的成员,一是 mMatrix,二是 mAlpha。
View中实现动画的过程
1、view 创建动画对象,设置动画属性,调用 invalidate 刷新屏幕,启动动画;
2、invalidate 方法触发了 onDraw 函数;
3、在 onDraw 函数中:
4、调用动画的 getTransformation 方法,得到当前时间点的矩阵
5、将该矩阵设置成 Canvas 的当前矩阵
6、调用 canvas 的 drawBitmap 方法,绘制屏幕。
7、判断 getTransformation 的返回值,若为真,调用 invalidate 方法,刷新屏幕进入下一桢;若为假,说明动画完成。
为Activity指定动画效果
修改Activity Theme
1. 在styles.xml中输入以下代码:
<style name="AnimationActivity" parent="@android:style/Animation.Activity" > <item name="android:activityOpenEnterAnimation">@anim/push_left_in</item> <item name="android:activityOpenExitAnimation">@anim/push_left_out</item> <item name="android:activityCloseEnterAnimation">@anim/push_right_in</item> <item name="android:activityCloseExitAnimation">@anim/push_right_out</item> </style>
看一下定义的动画文件
2. 然后在themes.xml中
<style name="ThemeActivity"> <item name="android:windowAnimationStyle">@style/AnimationActivity</item> <item name="android:windowNoTitle">true</item> </style>
3. 在AndroidManifest.xml中为Activity指定theme.
或者也可以使用代码设定
通过调用 overridePendingTransition() 可以实时修改Activity的切换动画。
但需注意的是:该函数必须在调用startActivity()或finish()后立即调用,且只有效一次。
Drag
public class SimpleDragSample extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.layout_simpledrag) ; DisplayMetrics dm = getResources().getDisplayMetrics() ; final int screenWidth = dm.widthPixels ; final int screenHeight = dm.heightPixels ; final Button _Button = (Button)findViewById(R.id.buttonDrag) ; _Button.setOnTouchListener(new OnTouchListener() { int lastX , lastY ; boolean isDraging = false ; @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub int ea = event.getAction() ; switch (ea) { case MotionEvent.ACTION_DOWN: Log.i("sundylog","Down") ; isDraging = true ; lastX = (int) event.getRawX() ; lastY = (int) event.getRawY() ; break; case MotionEvent.ACTION_MOVE: if(isDraging) { int dx = (int)event.getRawX() - lastX ; int dy = (int)event.getRawY() - lastY ; int l = v.getLeft() + dx ; int b = v.getBottom() + dy ; int r = v.getRight() + dx ; int t = v.getTop() + dy ; //判断超出屏幕 if(l<0) { l = 0 ; r = l + v.getWidth() ; } if(t<0) { t = 0 ; b = t + v.getHeight() ; } if(r> screenWidth) { r = screenWidth ; l = r - v.getWidth() ; } if(b > screenHeight) { b = screenHeight ; t = b - v.getHeight() ; } v.layout(l, t, r, b) ; lastX = (int) event.getRawX() ; lastY = (int) event.getRawY() ; v.postInvalidate() ; } break ; case MotionEvent.ACTION_UP: isDraging = false ; break ; default: break; } return false; } }) ; } }
如果想要按住时有一些效果,比如变大,等等,需要附加一个层,隐藏原来的View,这就需要用到shadow(DragShadow?)
还有如果想要一种吸附边缘的效果,就需要一些类似于单元格的容器(类似于GridView)
图片切换效果思路:
几个View叠在一起,切换时,上面的View在横向轴左移,-100%p(left-out),同时alpha值从1降到0
同时下一层View出现,从100%p进到主页面(right-in),同时alpha从0加到1,就实现了切换效果
系统也提供给我们一些相关的类,比如ViewAnimator
ViewAnimator有两个子类,ViewFlipper和ViewSwitcher
Sample:ViewFlipper实现最常见应用
1,定义四个动画 , fade_left_in fade_left_out fade_right_in fade_right_out
fade_left_in
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:shareInterpolator="true"> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="500" /> <translate android:fromXDelta="-100%p" android:toXDelta="0" android:duration="500"/> </set>
fade_left_out
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:shareInterpolator="true"> <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="500" /> <translate android:fromXDelta="0" android:toXDelta="-100%p" android:duration="500"/> </set>
fade_right_in
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:shareInterpolator="true"> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="500" /> <translate android:fromXDelta="100%p" android:toXDelta="0" android:duration="500"/> </set>
fade_right_out
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:shareInterpolator="true"> <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="500" /> <translate android:fromXDelta="0" android:toXDelta="100%p" android:duration="500"/> </set>
2,定义layout文件 。 <ViewAnimator> or <ViewFlipper>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ViewFlipper android:id="@+id/viewFlipper1" android:layout_width="match_parent" android:layout_height="match_parent" android:flipInterval="1000" android:inAnimation="@android:anim/fade_in" android:outAnimation="@android:anim/fade_out"> <FrameLayout android:layout_width="match_parent" android:id="@+id/frameLayout1" android:layout_height="match_parent"> <ImageView android:src="@drawable/gesture1" android:layout_height="match_parent" android:layout_width="match_parent"/> </FrameLayout> <FrameLayout android:id="@+id/frameLayout2" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:src="@drawable/gesture2" android:layout_height="match_parent" android:layout_width="match_parent"/> </FrameLayout> <FrameLayout android:id="@+id/frameLayout2" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:src="@drawable/gesture3" android:layout_height="match_parent" android:layout_width="match_parent"/> </FrameLayout> <FrameLayout android:id="@+id/frameLayout2" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:src="@drawable/gesture4" android:layout_height="match_parent" android:layout_width="match_parent"/> </FrameLayout> </ViewFlipper> </LinearLayout>
说明:android:flipInterval 显示下一个视图的时间间隔
3,写代码 , onCreate() 取得Flipper对象设置好属性 。
4,事件 , GestureDetector .
5,onFling() , 设置 viewFlipper设置动画以及调用下一个ViewGroup
public class ViewFlipperActivity extends Activity implements GestureDetector.OnGestureListener { ViewFlipper viewFlipper ; GestureDetector gestureDetector ; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); gestureDetector = new GestureDetector(this) ; setContentView(R.layout.layout_viewflipper) ; viewFlipper = (ViewFlipper)findViewById(R.id.viewFlipper1) ; } @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub //一定要加上 return gestureDetector.onTouchEvent(event) ; } @Override public boolean onDown(MotionEvent e) { // TODO Auto-generated method stub return false; } @Override public void onShowPress(MotionEvent e) { // TODO Auto-generated method stub } @Override public boolean onSingleTapUp(MotionEvent e) { // TODO Auto-generated method stub return false; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // TODO Auto-generated method stub return false; } @Override public void onLongPress(MotionEvent e) { // TODO Auto-generated method stub } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { // TODO Auto-generated method stub Log.i("sundylog","onFling...") ; if(e1.getX() >e2.getX()) { //如果是往左边滑动 //设置动画效果 viewFlipper.setOutAnimation(this, R.anim.fade_left_out) ; viewFlipper.setInAnimation(this,R.anim.fade_right_in) ; //上一张 viewFlipper.showPrevious() ; }else if(e1.getX() < e2.getX()) { //如果是往右边滑动 viewFlipper.setOutAnimation(this,R.anim.fade_right_out) ; viewFlipper.setInAnimation(this,R.anim.fade_left_in) ; viewFlipper.showNext() ; }else { return false ; } return true; } }
Activity的onTouchEvent一定要与GestureDetector绑定
当然也可以用某控件与GestureDetector绑定,仅对改控件手势有效
案例(模仿Apple程序列表的抖动效果)
Steps:
1, 通过PackageManager 加载应用图标到GridView里
2,定义好item的shake动画 , longclick的时候 , 启动动画
3,随着鼠标的移动 , 判断进入到了哪个Dock (哪个单元格区域)PointToPosition,从区域转换为index , 然后交换单元格视图(Swap)(先交换adapter ,绑定视图,视图就交换了)
4,Drop的时候把原来的图标放到当前单元格
具体看代码,有个ResolveInfo类是PackageManager中应用信息的结果
一共有五个类
ItemContainner
public class ItemContainer<T> extends FrameLayout implements AdapterView.OnItemLongClickListener, View.OnTouchListener, ItemMover.OnImageMovedListener { private GridView mGridView = null; private ItemMover mItemMover = null; private ItemContainerAdapter<T> mAdapter = null; public ItemContainer(Context context) { this(context, null); } public ItemContainer(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ItemContainer(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); mGridView = new GridView(getContext()); mGridView.setGravity(Gravity.CENTER); mGridView.setNumColumns(4); addView(mGridView, lp); mItemMover = new ItemMover(getContext()); addView(mItemMover, lp); mGridView.setOnItemLongClickListener(this); mGridView.setOnTouchListener(this); mItemMover.setOnImageMoveListener(this); } protected void setNumColumns(int num) { mGridView.setNumColumns(num); } public void setAdapter(ItemContainerAdapter<T> adapter) { mAdapter = adapter; mGridView.setAdapter(adapter); } public void setOnItemClickListener(OnItemClickListener listener) { mGridView.setOnItemClickListener(listener); } public ItemContainerAdapter<T> getAdapter() { return mAdapter; } @Override public boolean onItemLongClick(AdapterView<?> parent, View b, int pos, long id) { mAdapter.setHideItem(pos); View v = mAdapter.createSuspensionView(pos); mItemMover.startMove(v); return true; } @Override public void onMoveStart(View v) { mGridView.invalidateViews(); } @Override public void onMoving(View v, float x, float y) { int targetPos = mGridView.pointToPosition((int) x, (int) y); // 通过坐标点转换为GridView单元格位置 int hiddenPos = mAdapter.getHideItemPosition(); if (hiddenPos != targetPos && targetPos >= 0) { if (mAdapter.swap(hiddenPos, targetPos)) { mAdapter.setHideItem(targetPos); refreshItem(targetPos); onItemMoved(refreshItem(hiddenPos), hiddenPos, targetPos); } } } @Override public void onMoveEnd(View v) { mAdapter.unhideAllItem(); mGridView.invalidateViews(); } protected void onItemMoved(final View v, int oldPos, int newPos) { int oldCol = oldPos % 5; int oldRow = oldPos / 5; int newCol = newPos % 5; int newRow = newPos / 5; int w = v.getWidth(); int h = v.getHeight(); TranslateAnimation anim = new TranslateAnimation((newCol - oldCol) * w, 0.0f, (newRow - oldRow) * h, 0.0f); anim.setDuration(300); anim.setFillAfter(true); anim.setAnimationListener(new Animation.AnimationListener(){ @Override public void onAnimationEnd(Animation animation) { Animation mainAnim = AnimationUtils.loadAnimation(getContext(), R.anim.shake); v.setAnimation(mainAnim); } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationStart(Animation animation) { }}); v.setAnimation(anim); } @Override public boolean onTouch(View v, MotionEvent event) { mItemMover.onTouchEvent(event); return v.onTouchEvent(event); } private View refreshItem(int position) { int firstPos = mGridView.getFirstVisiblePosition(); int lastPos = mGridView.getLastVisiblePosition(); if (position >= firstPos && position <= lastPos ) { View v = mGridView.getChildAt(position - firstPos); mAdapter.bindView(v, position); return v; } return null; } }
ItemContainerAdapter
public abstract class ItemContainerAdapter<T> extends BaseAdapter { private Context mContext = null; protected List<T> mItemList = null; private int mHiddenPosition = -1; private final Random mRandom = new Random(SystemClock.currentThreadTimeMillis()); public ItemContainerAdapter(Context c, List<T> list) { mContext = c; mItemList = list; } public void setHideItem(int pos) { int size = getCount(); if (pos >= 0 && pos < size) { mHiddenPosition = pos; } } public int getHideItemPosition() { return mHiddenPosition; } public void unhideAllItem() { mHiddenPosition = -1; } public boolean swap(int a, int b) { if (a == b) { return false; } if (a < 0 || b < 0) { return false; } int size = getCount(); if (a >= size || b >= size) { return false; } T t = mItemList.get(a); mItemList.set(a, mItemList.get(b)); mItemList.set(b, t); return true; } public int getCount() { return mItemList.size(); } @Override public T getItem(int position) { return mItemList.get(position); } public long getItemId(int position) { return position; } abstract protected View onCreateItemView(int position, ViewGroup parent); abstract public void onBindView(View v, int position); abstract public void onHideView(View v); public final void bindView(View v, int position) { if (position == mHiddenPosition) { onHideView(v); } else { v.setVisibility(View.VISIBLE); v.clearAnimation(); onBindView(v, position); } } public final View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = onCreateItemView(position, parent); } bindView(convertView, position); if (mHiddenPosition >= 0 && position != mHiddenPosition) { Animation anim = AnimationUtils.loadAnimation(mContext, R.anim.shake); anim.setStartOffset(mRandom.nextInt() % anim.getDuration()); convertView.startAnimation(anim); } else { convertView.clearAnimation(); } return convertView; } public final View createSuspensionView(int position) { View v = onCreateItemView(position, null); onBindView(v, position); return v; } }
ItemMover
public class ItemMover extends FrameLayout { private OnImageMovedListener mOnImageMovedListener = null; LayoutParams mCurrentLayout = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); public ItemMover(Context context) { super(context); } public ItemMover(Context context, AttributeSet attrs) { super(context, attrs); } public void setOnImageMoveListener(OnImageMovedListener l) { mOnImageMovedListener = l; } public void startMove(View v) { if (v == null) { return; } setVisibility(View.VISIBLE); removeAllViews(); v.setVisibility(View.INVISIBLE); v.clearAnimation(); addView(v); if (mOnImageMovedListener != null) { mOnImageMovedListener.onMoveStart(v); } } @Override public boolean onTouchEvent(MotionEvent event) { View v = getChildAt(0); if (v == null) { return false; } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // mDrawing = true; break; case MotionEvent.ACTION_UP: // mDrawing = false; if (getVisibility() == VISIBLE) { setVisibility(GONE); if (mOnImageMovedListener != null) { mOnImageMovedListener.onMoveEnd(v); } } break; case MotionEvent.ACTION_MOVE: break; default: break; } if (getVisibility() == VISIBLE) { v.setVisibility(View.VISIBLE); float x = event.getX() - 32; float y = event.getY() - 32; mCurrentLayout.setMargins((int)x, (int)y, 0, 0); updateViewLayout(v, mCurrentLayout); mOnImageMovedListener.onMoving(v, x, y); return true; } else { mCurrentLayout.setMargins(0, 0, 0, 0); return false; } } public interface OnImageMovedListener { public void onMoveStart(View v); public void onMoving(View v, float x, float y); public void onMoveEnd(View v); } }
ApplicationInfoContainer
public class ApplicationInfoContainer extends ItemContainer<ResolveInfo> { public ApplicationInfoContainer(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.myAttr); int numColumns = a.getInt(R.styleable.myAttr_numColumns, 0); if (numColumns > 0) { setNumColumns(numColumns); } } }
ShakeDesktopActivity
public class ShakeDesktopActivity extends Activity implements OnItemClickListener { private LayoutInflater mInflater; private PackageManager mPackageManager; private ApplicationContainerAdapter mAdapter; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); mPackageManager = getPackageManager(); requestWindowFeature(Window.FEATURE_NO_TITLE); ApplicationInfoContainer container = new ApplicationInfoContainer(this, null, 0); container.setAdapter(new ApplicationContainerAdapter(this)); container.setOnItemClickListener(this); setContentView(container); } private class ApplicationContainerAdapter extends ItemContainerAdapter<ResolveInfo> { ApplicationContainerAdapter(Context context) { super(context, getAppInfoList(context)); } @Override public void onBindView(View v, int position) { ResolveInfo info = mItemList.get(position); ImageView image = (ImageView)v.findViewById(android.R.id.icon); TextView text = (TextView)v.findViewById(android.R.id.text1); image.setImageDrawable(info.activityInfo.loadIcon(mPackageManager)); text.setText(info.activityInfo.loadLabel(mPackageManager)); } @Override public void onHideView(View v) { ImageView image = (ImageView)v.findViewById(android.R.id.icon); TextView text = (TextView)v.findViewById(android.R.id.text1); image.setImageResource(android.R.color.transparent); text.setText(""); } @Override protected View onCreateItemView(int position, ViewGroup parent) { View v = mInflater.inflate(R.layout.app_container_item, null); return v; } }; private static List<ResolveInfo> getAppInfoList(Context context) { PackageManager manager = context.getPackageManager(); Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); List<ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0); Collections.sort(apps, new ResolveInfo.DisplayNameComparator(manager)); return apps; } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //Launch application ResolveInfo info = mAdapter.getItem(position); String packageName = info.activityInfo.packageName; String activityName = info.activityInfo.name; // Intent intent = new Intent(Intent.ACTION_MAIN); // intent.addCategory(Intent.CATEGORY_LAUNCHER); // intent.setComponent(new ComponentName(packageName, activityName)); // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK // | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); // mContext.startActivity(intent); } }
相关文章推荐
- Android高级应用开发(深入篇) stage3(上)- 高级商用界面开发 学习笔记
- Android高级应用开发(基础篇) - stage4 - 学习笔记
- Android高级应用开发(基础篇) - stage3 - 学习笔记
- Android高级应用开发(基础篇) - stage1 - 学习笔记
- Android应用界面开发_学习笔记_第三周
- Android应用界面开发_学习笔记_第二周
- Android高级应用开发(基础篇) - stage6 - 学习笔记(上)
- android移动应用开发学习笔记(五)——模拟qq登陆界面
- Android高级应用开发(基础篇) - stage6 - 学习笔记(下)
- Android开发学习笔记(七)Android应用界面编程 Radio/Check/DataPicker学习
- Android开发学习笔记(八)Android应用界面编程 ImageView学习
- Android高级应用开发(基础篇) - stage2 - 学习笔记
- Android高级应用开发(基础篇) - stage5 - 学习笔记
- Android高级应用开发(基础篇) - stage9 - 学习笔记
- Android高级应用开发(基础篇) - stage6 - 学习笔记(中)
- Android应用界面开发_学习笔记_第四周
- Android开发学习笔记(九)Android应用界面编程 AutoCompleteTextView学习
- Android应用界面开发_学习笔记_第一周
- Android高级应用开发(基础篇) - stage10 - 学习笔记
- Android应用开发学习笔记之列表选择框