- 帧动画
- 补间动画Tween Animation
- 对话框以及面试中的注意点
帧动画
帧动画很简单,我们首先看一下Google官方解释This is a traditional animation in the sense that it is created with a sequence of different images
。
意思表达的很明了,一个传统的动画是由一组不同的图片组成的。帧动画,就像GIF图片,通过一系列Drawable依次显示来模拟动画的效果。
- 创建一帧动画分为一下几步(支持4.0以上):
- ①在项目工程下,创建
res/drawable
目录; -
②创建一个以<animation-list>
为根元素的xml文件,并在根节点下面,添加一组<item>
结点,用来加载图片。<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@drawable/girl_1" android:duration="200" /> <item android:drawable="@drawable/girl_2" android:duration="200" /> <item android:drawable="@drawable/girl_3" android:duration="200" /> <item android:drawable="@drawable/girl_4" android:duration="200" /> <item android:drawable="@drawable/girl_5" android:duration="200" /> <item android:drawable="@drawable/girl_6" android:duration="200" /> <item android:drawable="@drawable/girl_7" android:duration="200" /> <item android:drawable="@drawable/girl_8" android:duration="200" /> <item android:drawable="@drawable/girl_9" android:duration="200" /> <item android:drawable="@drawable/girl_10" android:duration="200" /> <item android:drawable="@drawable/girl_11" android:duration="200" /> </animation-list>
- ③将一组图片放置在
res/drawable
目录下 -
④在layout
布局文件中,添加一个<ImageView>
并在代码中找到,并且设置相关的资源。// 找到ImageView控件 ImageView iv = (ImageView) findViewById(R.id.iv); // 为IV设置背景资源 iv.setBackgroundResource(R.drawable.girl); // 创建帧动画并开始 AnimationDrawable rocketAnimation = (AnimationDrawable) iv.getBackground(); rocketAnimation.start();
补间动画(Tween Animation)
补间动画的名称来源是flash,在做flash动画时,在两个关键帧中间需要做“补间动画”,才能实现图画的运动;插入补间动画后两个关键帧之间的插补帧是由计算机自动运算而得到的。
- 先介绍一些关键的方法:
- Animation:
★ setDuration 设置动画的执行时间
★ setRepeatCount 设置动画的重复次数
★ setRepeatMode 指定重复的模式(如:反转)
★ setFillAfter 指示动画指定完毕之后控件的状态是否停留在动画停
止的时候
★ setAnimationListener 设置动画的事件监听器
- ImageView:
★ startAnimation(Animation a) 让ImageView执行某动画
- 补间动画分为(透明、旋转、缩放、位移)等四种:
-
①透明(Alpha),动画涉及到很多操作,详细的注释已经添加到代码中了// 1.0f代表完全不透明 // 0.0f代表完全透明 float fromAlpha = 1.0f; float toAlpha = 0.0f; AlphaAnimation aa = new AlphaAnimation(fromAlpha, toAlpha); // 动画的配置 aa.setDuration(2 * 1000); // 动画持续多久 aa.setFillAfter(true); // 设为true之后,界面会停留在动画播放完时的界面 aa.setRepeatCount(1); // 设置动画重复的次数 aa.setRepeatMode(Animation.RESTART); // 动画重启或逆转,默认值为重启动画 // 启动动画 iv.startAnimation(aa);
-
②旋转(roate),涉及到很多概念,代码下面也会以图例解释的// 旋转的起始角度 float fromDegrees = 0; // 旋转的目标角度 float toDegrees = 360; // 旋转的X轴中心点类型 // 分为三种类型:Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF,Animation.RELATIVE_TO_PARENT. // X轴的原点的类型(相对于自己而言还是相对于父容器而言) int pivotXType = Animation.RELATIVE_TO_SELF; // 旋转的X轴坐标的值,一般被指定为一个百分比数;1.0代表着100% // 开始伸缩时的X轴的原点(例:0.5就是指以图片宽度的二分之一的位置作为X轴的原点) float pivotXValue = 0.0f; // Y轴的原点的类型 int pivotYType = Animation.RELATIVE_TO_SELF; // 开始伸缩时的Y轴的原点 float pivotYValue = 0.0f; /* *这里对pivotXValue和pivotYValue的值详细说明一下 *在Android中的坐标轴Y轴向下是增张的,X轴向右是增长的; *pivotXValue在API中被说明为:旋转的X轴坐标的值,一般被指定为一个百分比数;1.0代表着100% *也就是说,当我们指定为0.0f时,代表作是图片的X轴的0点;当指定为0.5f时,代表图片的width/2的位置;当被指定为1.0f时,代表图片的width位置; */ RotateAnimation ra = new RotateAnimation(fromDegrees, toDegrees, pivotXType, pivotXValue, pivotYType, pivotYValue); // 动画的配置 ra.setDuration(2 * 1000); // 动画持续多久 ra.setRepeatCount(1); // 设置动画重复的次数 ra.setStartOffset(2 * 1000); // 设置动画启动时间的偏移量,简单来说就是多长时间后启动动画 // 启动动画 iv.startAnimation(ra);
-
大家肯定会对pivotXValue
和pivotYValue
很困惑,pivot是中心点的意思,也就是代表旋转动画X轴的坐标和旋转动画Y轴的坐标,亲,请看图吧。 - 我觉得画的还是挺清晰的。
-
③缩放(scale)// 0.0f代表0点的位置;0.5f代表图片宽度的一半的位置;1.0f代表图片整个图片宽度的位置;2.0f代表整个图片宽度的2倍; // 水平方向比例尺的起始值 float fromX = 0.0f; // 水平方向比例尺的终值 float toX = 2.0f; // 垂直方向比例尺的起始值 float fromY = 0.0f; // 垂直方向比例尺的终值 float toY = 2.0f; // X轴的原点的类型(相对于自己而言还是相对于父容器而言) int pivotXType = Animation.RELATIVE_TO_SELF; // 开始伸缩时的X轴的原点(例:0.5就是指以图片宽度的二分之一的位置作为X轴的原点) float pivotXValue = 0.0f; int pivotYType = Animation.RELATIVE_TO_SELF; float pivotYValue = 0.0f; ScaleAnimation sa = new ScaleAnimation(fromX, toX, fromY, toY, pivotXType, pivotXValue, pivotYType, pivotYValue); sa.setDuration(2 * 1000); // 整个动画会呈现,从左上角向右下加放大2倍 iv.startAnimation(sa);
图片示例如下:
-
④位移(translate),下面是位移的代码,参数很多,但是不太好解释,所以还是用图片来说说明// 位移的x轴起始坐标的类型(相对于自己还是相对父容器) int fromXType = Animation.RELATIVE_TO_PARENT; // x轴起点 float fromXValue = -0.5f; int toXType = Animation.RELATIVE_TO_PARENT; // X轴的终点 float toXValue = 0.5f; int fromYType = Animation.RELATIVE_TO_PARENT; // Y轴的起始坐标 float fromYValue = -0.5f; int toYType = Animation.RELATIVE_TO_PARENT; // Y轴的终点坐标 float toYValue = 0.5f; TranslateAnimation ta = new TranslateAnimation(fromXType, fromXValue, toXType, toXValue, fromYType, fromYValue, toYType, toYValue); ta.setDuration(2 * 1000); iv.startAnimation(ta);
位移示意图:
我们可以看到,在位移中,屏幕中央是
(0.0f,0.0f)
,左上角的坐标为(-0.5f,-0.5f)
,右下角为(0.5f,0.5f)
;当以上面的代码运行时,图片会从左上角平移到右下角。 -
⑤动画的集合;顾名思义,即使把许多动画对象,添加到AnimationSet
的集合中,然后交给控件执行,执行动画顺序就是动画添加的顺序。代码如下:AnimationSet set = new AnimationSet(false); ScaleAnimation sa = new ScaleAnimation(0.1f, 2.0f, 0.1f, 2.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); sa.setDuration(3000); //设置动画的时间 sa.setRepeatCount(1); //设置动画的显示次数 sa.setRepeatMode(AlphaAnimation.REVERSE);//设置播放模式 RotateAnimation ra = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); ra.setDuration(3000); //设置动画的时间 ra.setRepeatCount(1); //设置动画的显示次数 ra.setRepeatMode(AlphaAnimation.REVERSE);//设置播放模式 TranslateAnimation ta = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, -0.5f, Animation.RELATIVE_TO_PARENT, 0.5f, Animation.RELATIVE_TO_PARENT, -0.5f, Animation.RELATIVE_TO_PARENT, 0.5f); ta.setDuration(3000); //设置动画的时间 ta.setRepeatCount(1); //设置动画的显示次数 ta.setRepeatMode(AlphaAnimation.REVERSE);//设置播放模式 set.addAnimation(sa); set.addAnimation(ta); set.addAnimation(ra); //播放动画 iv_icon.startAnimation(set);
- ⑤ 对于补间动画,它只是改变了View对象绘制的位置,而没有改变View对象本身。
对话框以及面试中的注意点
Android中常用的对话框有通知对话框、列表对话框、单选对话框、多选对话框以及进度对话框。其中,通知对话框、列表对话框、单选、多选对话框由AlertDialog.Builder
创建,进度对话框由ProgressDialog
创建。
- 常用的方法:
- ①setIcon:设置对话框标题栏左侧的那个图标
- ②setTitle:设置对话框标题栏的提示信息
- ③setMessage:设置对话框主体部分的提示信息
- ④setPositiveButton:设置确定按钮
- ⑤setNegativeButton:设置取消按钮
- ⑥setCancelable:设置对话框在点击返回键时是否会关闭
- ⑦show:显示对话框
getApplicationContext()
和MainActivity.this
的区别:- ①对于
getApplicationContext()
,我们可以从API看到这样一句话Return the context of the single, global Application object of the current process.
,意思是:返回当前进程的一个单例的全局应用程序上下文对象,它代表的是Android系统中这个应用本身。 - ②对于
MainActivity.this
,代表的是一个具体的Activity
对象;当我们在使用对话框时,它需要被绑定在某一个具体的界面上,因此也就不能够使用getApplicationContext()
作为Context
传入到构造函数中。 - ③当我们在对话框中传入
getApplicationContext()
时,会报一个异常:Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
- 通知对话框
-
通知对话框使用Builder创建,一般都会有个确认和取消按钮。当通知对话框提示的信息是要求用户必须观看且必须做出确定或者取消的选择的时候,需要设置setCancelable属性为false(默认true),以防止用户直接使用返回键关闭对话框。Builder builder = new Builder(this); builder.setIcon(R.drawable.ic_launcher); builder.setTitle("通知"); builder.setMessage("您看到的是通知对话框!"); //设置对话框点击返回键不关闭 builder.setCancelable(false); //设置确定按钮的点击事件 builder.setPositiveButton("确定", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "您点击了确定按钮!", 0).show(); } }); //设置取消按钮的点击事件 builder.setNegativeButton("取消", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "您点击了取消按钮!", 0).show(); } }); //将对话框显示出来 builder.show();
- 列表对话框
-
列表对话框使用Builder来创建,只需调用Builder对象的setItems方法设置要展示的列表项即可。Builder builder = new Builder(this); builder.setIcon(R.drawable.ic_launcher); builder.setTitle("请选择要去的城市"); final String[] cities = new String[]{"北京","上海","广州","深圳","杭州"}; builder.setItems(cities, new OnClickListener() { /* * 第一个参数代表对话框对象 * 第二个参数是点击对象的索引 */ @Override public void onClick(DialogInterface dialog, int which) { String city = cities[which]; Toast.makeText(MainActivity.this, "您选择的是:"+city, 0).show(); } }); builder.show();
- 普通对话框
-
还是比较简单的,涉及到了AlertDialog.Builder
和一些方法,请看一下代码和注释吧。// 创建一个对话框,并打算在`MainActivity`界面上展示 Builder builder = new AlertDialog.Builder(MainActivity.this); // 设置标题和内容 builder.setTitle("我是普通对话框标题"); builder.setMessage("我是普通对话框内容"); // 设置确定和取消按钮 builder.setPositiveButton("我是确定按钮", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(getApplicationContext(), "确定按钮被点击了", Toast.LENGTH_SHORT).show(); // 隐藏对话框 dialog.dismiss(); } }); builder.setNegativeButton("我是取消按钮", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(getApplicationContext(), "取消按钮被点击了", Toast.LENGTH_SHORT).show(); // 隐藏对话框 dialog.dismiss(); } }); // 显示对话框 builder.show();
- 单选对话框
-
也是非常简单,与普通对话框相比,它不能够设置setMessage()
,需要使用setSingleItemChoiceItems()
方法。Builder builder = new AlertDialog.Builder(MainActivity.this); // 设置标题和内容 builder.setTitle("我是普通对话框标题"); // 单选对话框 // 要展示的单选选项列表 final String[] items = { "你是二货", "我是二货", "大家都是二货" }; // 默认选中项的索引 int checkedItem = 0; // 选中选项触发的点击事件 OnClickListener listener = new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String res = items[which]; Toast.makeText(getApplicationContext(), res, Toast.LENGTH_SHORT).show(); dialog.dismiss(); } }; builder.setSingleChoiceItems(items, checkedItem, listener); // 显示对话框 builder.show();
- 多选对话框
-
多选对话框使用Builder来创建,只需调用Builder对象的setMultiChoiceItems方法,设置要展示的列表项即可。Builder builder = new AlertDialog.Builder(MainActivity.this); // 设置标题和内容 builder.setTitle("我是多选对话框标题"); // 要展示的多选列表项 final String[] items = { "你是二货", "我是二货", "大家都是二货" }; // 对应每个列表项的选中状态 final boolean[] checkedItems = { false, false, false }; // 设置点击事件 OnMultiChoiceClickListener listener = new OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { // 修改被选中的条目 checkedItems[which] = isChecked; } }; builder.setMultiChoiceItems(items, checkedItems, listener); // 设置确定和取消按钮 builder.setPositiveButton("我是确定按钮", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < checkedItems.length; i++) { if (checkedItems[i]) { sb.append(items[i] + ","); } } Toast.makeText(getApplicationContext(), sb.toString(), Toast.LENGTH_SHORT).show(); // 隐藏对话框 dialog.dismiss(); } }); // 显示对话框 builder.show();
- 进度对话框
-
进度对话框不同于之前几种对话框,它是由ProgressDialog对象来创建的,而且进度对话框内部使用了消息机制Handler来进行处理,所以它可以直接在子线程中进行修改,无需再单独设置Handler来修改UI。final ProgressDialog dialog = new ProgressDialog(MainActivity.this); // 设置图标 dialog.setIcon(R.drawable.ic_launcher); dialog.setTitle("进度对话框"); dialog.setMessage("玩命加载中..."); // 设置对话框的样式为水平 dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); // 设置进度条的最大值 dialog.setMax(100); // 显示对话框 dialog.show(); new Thread() { public void run() { while (true) { // 休眠500ms SystemClock.sleep(500); // 进度条每次增加一个单位 dialog.incrementProgressBy(1); // 进度条到头是退出 if (dialog.getMax() == dialog.getProgress()) { dialog.dismiss(); Toast.makeText(MainActivity.this, "下载完成!", 0).show(); } } }; }.start();
- 状态栏通知
-
新版本API的通知代码// 通知管理器 NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); Notification noti = new Notification.Builder(this)// .setContentTitle("通知栏标题")// .setContentText("通知栏内容")// .setSmallIcon(R.drawable.ic_launcher)// .setAutoCancel(true)// .build(); notificationManager.notify(0, noti);
旧版本的通知:
// 通知管理器 Notification n = new Notification(R.drawable.feq, "下载完成", System.currentTimeMillis()); // 创建PendingIntent以供点击时发送,延迟意图,点击时发送 PendingIntent pi = PendingIntent.getActivity(this, 100, new Intent(this, ResultActivity.class), PendingIntent.FLAG_ONE_SHOT); // 设置通知点击事件 n.setLatestEventInfo(this, "下载完成", "FeiQ.exe下载完成", pi); // 设置通知点击后清除 n.flags = Notification.FLAG_AUTO_CANCEL; // 获取系统通知服务 NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); // 发送消息 manager.notify(0, n);
- 自定义对话框步骤
- ①创建对话框构建器
- ②创建出一个空的对话框
- ③实例化自定义布局
- ④给对话框设置自定义布局
- ⑤给按钮设置点击事件
- 自定义布局的代码
-
测试结果是:<?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="wrap_content" android:orientation="vertical" > <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入账号" /> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入密码" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <Button android:id="@+id/btn_login" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="登陆" /> <Button android:id="@+id/btn_cancel" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="取消" /> </LinearLayout> </LinearLayout>
创建自定义对话框的代码是:
// 对话框构建器 Builder builder = new AlertDialog.Builder(this); // 创建出一个空的对话框 final AlertDialog dialog = builder.create(); // 加载自定义View布局 View view = View.inflate(this, R.layout.custom_dialog, null); int viewSpacingLeft = 0; int viewSpacingTop = 0; int viewSpacingRight = 0; int viewSpacingBottom = 0; // 给对话框指定自定义的layout文件,并且上下左右边框为0:不然对话框会出现黑框,因为低版本的对话框,不指定背景的话就是黑色的。 dialog.setView(view, viewSpacingLeft, viewSpacingTop, viewSpacingRight, viewSpacingBottom); // 找到button,设置点击事件 Button btn_login = (Button) view.findViewById(R.id.btn_login); Button btn_cancel = (Button) view.findViewById(R.id.btn_cancel); btn_login.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getApplicationContext(), "确定按钮被点击了", Toast.LENGTH_SHORT).show(); dialog.dismiss(); } }); btn_cancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getApplicationContext(), "取消按钮被点击了", Toast.LENGTH_SHORT).show(); dialog.dismiss(); } }); // 显示对话框 dialog.show();
★★★结合工作和面试★★★
- 一个对话框弹出,Activity会执行什么生命周期方法?
- Activity不会执行任何方法
- 如何解决Toast重复弹出问题?这个工作中也会碰到这样的需求。
- 在我们做项目时,有时会用到Toast来给用户提示某些信息,可当用户视而不见时,连续重复的操作就会让Toast不停的在页面上显示,很是影响用户体验。
-
解决思路:不用计算Toast的时间之类的,就是定义一个全局的成员变量Toast, 这个Toast不为null的时候才去make,否则直接setText.为了按返回键后立即使Toast不再显示,重写父类Activity的 onBackPressed()方法里面去cancel你的Toast即可.private Toast mToast; public void showToast(String text) { if (mToast == null) { mToast = Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT); } else { mToast.setText(text); mToast.setDuration(Toast.LENGTH_SHORT); } mToast.show(); } public void cancelToast() { if (mToast != null) { mToast.cancel(); } } public void onBackPressed() { cancelToast(); super.onBackPressed(); }