如何在Android中自定义动画

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在Android中自定义动画相关的知识,希望对你有一定的参考价值。

    在android中自定义动画按照下面的步骤。

1、首先定义一个动画,如下图:

在上面代码中定义了一个旋转动画,包括了开始旋转角度(0°)和最后旋转结束角度(360°),和相对于自己的位置(控件的中心点)。

2、定义动画的简单属性,如下图:

上面代码定义了动画的持续时间和结束后书否保持原状。

3、选择执行的控件,如下图:

参考技术A Android 在XML文件中定义动画:
① 打开Eclipse,新建Android工程
② 在res目录中新建anim文件夹
③ 在anim目录中新建一个myanim.xml(注意文件名小写)
④ 加入XML的动画代码
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha/>
<scale/>
<translate/>
<rotate/>
</set>
Android动画解析--XML
<alpha>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<alpha
android:fromAlpha="0.1"
android:toAlpha="1.0"
android:duration="3000"/>
<!-- 透明度控制动画效果 alpha
浮点型值:
fromAlpha 属性为动画起始时透明度
toAlpha 属性为动画结束时透明度
说明:
0.0表示完全透明
1.0表示完全不透明
以上值取0.0-1.0之间的float数据类型的数字
长整型值
duration 属性为动画持续时间
说明:
时间以毫秒为单位
</set>
<scale>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:interpolator= "@android:anim/accelerate_decelerate_interpolator"
android:fromXScale="0.0"
android:toXScale="1.4"
android:fromYScale="0.0"
android:toYScale="1.4"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="false"
android:duration="700" />
</set>
<!-- 尺寸伸缩动画效果 scale
属性:interpolator 指定一个动画的插入器
在我试验过程中,使用android.res.anim中的资源时候发现
有三种动画插入器:
accelerate_decelerate_interpolator 加速-减速 动画插入器
accelerate_interpolator 加速-动画插入器
decelerate_interpolator 减速- 动画插入器
其他的属于特定的动画效果
浮点型值:
fromXScale 属性为动画起始时 X坐标上的伸缩尺寸
toXScale 属性为动画结束时 X坐标上的伸缩尺寸
fromYScale 属性为动画起始时Y坐标上的伸缩尺寸
toYScale 属性为动画结束时Y坐标上的伸缩尺寸
说明:
以上四种属性值
0.0表示收缩到没有
1.0表示正常无伸缩
值小于1.0表示收缩
值大于1.0表示放大
pivotX 属性为动画相对于物件的X坐标的开始位置
pivotY 属性为动画相对于物件的Y坐标的开始位置
说明:
以上两个属性值 从0%-100%中取值
50%为物件的X或Y方向坐标上的中点位置
长整型值:
duration 属性为动画持续时间
说明: 时间以毫秒为单位
布尔型值:
fillAfter 属性 当设置为true ,该动画转化在动画结束后被应用
<translate>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="30"
android:toXDelta="-80"
android:fromYDelta="30"
android:toYDelta="300"
android:duration="2000"
/>
<!-- translate 位置转移动画效果
整型值:
fromXDelta 属性为动画起始时 X坐标上的位置
toXDelta 属性为动画结束时 X坐标上的位置
fromYDelta 属性为动画起始时 Y坐标上的位置
toYDelta 属性为动画结束时 Y坐标上的位置
注意:
没有指定fromXType toXType fromYType toYType 时候,
默认是以自己为相对参照物
长整型值:
duration 属性为动画持续时间
说明: 时间以毫秒为单位
</set>
<rotate>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromDegrees="0"
android:toDegrees="+350"
android:pivotX="50%"
android:pivotY="50%"
android:duration="3000" />
<!-- rotate 旋转动画效果
属性:interpolator 指定一个动画的插入器
在我试验过程中,使用android.res.anim中的资源时候发现
有三种动画插入器:
accelerate_decelerate_interpolator 加速-减速 动画插入器
accelerate_interpolator 加速-动画插入器
decelerate_interpolator 减速- 动画插入器
其他的属于特定的动画效果
浮点数型值:
fromDegrees 属性为动画起始时物件的角度
toDegrees 属性为动画结束时物件旋转的角度 可以大于360度
说明:
当角度为负数——表示逆时针旋转
当角度为正数——表示顺时针旋转
(负数from——to正数:顺时针旋转)
(负数from——to负数:逆时针旋转)
(正数from——to正数:顺时针旋转)
(正数from——to负数:逆时针旋转)
pivotX 属性为动画相对于物件的X坐标的开始位置
pivotY 属性为动画相对于物件的Y坐标的开始位置
说明: 以上两个属性值 从0%-100%中取值
50%为物件的X或Y方向坐标上的中点位置 长整型值:
duration 属性为动画持续时间
说明: 时间以毫秒为单位
</set>
Java 文件调用
Button button=(Button)findViewById(R.id.ok)
button.setOnClickListener(new OnClickListener()
public void onClick(View v)
Animation animation=AnimationUtils.loadAnimation(HelloActivity.this,R.anim.buttonain);
findViewById(R.id.listView).startAnimation(animation);

);

如何Android中自定义Navigationbar

在如何控制android系统中NavigationBar 的显示与隐藏文章里简要地介绍了Navigationbar的背景知识,
NavigationBar的代码是放在... rameworksasepackagesSystemUI路径下面的。该路径下的工程主要负责手机中系统级UI的显示部分,如下图框中选中部分(包含其中的通知栏的显示),USB的连接,截屏等等。

NavigationBar的创建
navigationbar
的代码是在SystemUI工程SystemUI/src/com/android/systemui/statusbar/phone的路径下,其中
navigationbar是由PhoneStatusBar.java类创建的。在该类的makeStatusBarView()方法下,可以看到创建
Navigationbar的过程:

try
boolean showNav = mWindowManagerService.hasNavigationBar();
/// M: Support Smartbook Feature.
if (true) Log.v(TAG, "hasNavigationBar=" + showNav);
if (showNav)
mNavigationBarView =
(NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);

mNavigationBarView.setDisabledFlags(mDisabled);
mNavigationBarView.setBar(this);
mNavigationBarView.setOnTouchListener(new View.OnTouchListener()
@Override
public boolean onTouch(View v, MotionEvent event)
checkUserAutohide(v, event);
return false;
);

catch (RemoteException ex)
// no window manager? good luck with that

WindowManagerService通过判断是否需要显示NavigationBar来决定是否需要创建NavigationBarView,
NavigationBarView即为我们看到视图的view了,navigation_bar即为NavigationBarView实例化的
layout,你可以在SystemUI工程下的layout文件夹下找到。

通过修改navigation_bar布局的方式来自定义NavigationBar的UI。在该layout文件中有这样一个类。
com.android.systemui.statusbar.policy.KeyButtonView,它是系统定义的在
NavigationBar上的按钮类(后面会讲到),点击会产生波纹的效果。
NavigationBarView主负责UI的初始化工作,实例化布局,根据屏幕方向先取正确的图片。
NavigationBar按钮的事件绑定
NavigationBar按钮上的事件绑定并不是在NavigationBarView里实现,而是在SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java类中完成的。
通过NavigationBarView对外提供的获取按钮接口来完成按钮的绑定:
Recent, Home, SearchLight按钮事件的绑定

private void prepareNavigationBarView()
mNavigationBarView.reorient();

mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
mNavigationBarView.getSearchLight().setOnTouchListener(mHomeSearchActionListener);
updateSearchPanel();


Menu, Home, Back按钮事件的绑定:

上面三个按钮都是KeyButtonView类,它们的事件响应过程都是在类本身里面完成的。它们通过onTouchEvent()方法来响应点击事件,

public boolean onTouchEvent(MotionEvent ev)
final int action = ev.getAction();
int x, y;

switch (action)
case MotionEvent.ACTION_DOWN:
//Slog.d("KeyButtonView", "press");
mDownTime = SystemClock.uptimeMillis();
setPressed(true);
if (mCode != 0)
sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
else
// Provide the same haptic feedback that the system offers for virtual keys.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);

if (mSupportsLongpress)
removeCallbacks(mCheckLongPress);
postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());

break;
case MotionEvent.ACTION_MOVE:
x = (int)ev.getX();
y = (int)ev.getY();
setPressed(x >= -mTouchSlop
&& x < getWidth() + mTouchSlop
&& y >= -mTouchSlop
&& y < getHeight() + mTouchSlop);
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
if (mCode != 0)
sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);

if (mSupportsLongpress)
removeCallbacks(mCheckLongPress);

break;
case MotionEvent.ACTION_UP:
final boolean doIt = isPressed();
setPressed(false);
if (mCode != 0)
if (doIt)
sendEvent(KeyEvent.ACTION_UP, 0);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
playSoundEffect(SoundEffectConstants.CLICK);
else
sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);

else
// no key code, just a regular ImageView
if (doIt)
performClick();


if (mSupportsLongpress)
removeCallbacks(mCheckLongPress);

break;


return true;


mCode是用来判断该触摸是来自于哪个button,表示不同button的keycode在KeyEvent中类都有定义。该值在布局文件中通过获取
navigationbar_view中的systemui:keycode属性来获得,下面是layout布局文件中back相应代码段:

<com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
android:src="@drawable/ic_sysbar_back"
systemui:keyCode="4"
android:layout_weight="0"
android:scaleType="center"
systemui:glowBackground="@drawable/ic_sysbar_highlight"
android:contentDescription="@string/accessibility_back"
/>

在onTouch中方法通过sendEvent()方法来执行不同的keycode响应事件,该方法会创建一个包含keycode的KeyEvent对象封装,然后通过injectInputEvent()向InputManager插入一个事件,再发送出去。
参考技术A 我们使用的大多数android手机上的Home键,返回键以及menu键都是实体触摸感应按键。如果你用Google的Nexus4或Nexus5话,
你会发现它们并没有实体按键或触摸感应按键,取而代之的是在屏幕的下方加了一个小黑条,在这个黑条上有3个按钮控件,这种设置无疑使得手机的外观的设计更
加简约。但我遇到身边用Nexus 4手机的人都吐槽这种设计,原因很简单:好端端的屏幕,被划出一块区域用来显示3个按钮(如下图所示):Back,
Home, Recent。并且它一直用在那里占用着。

  在android源码中,那一块区域被叫做NavigationBar。同
时,google在代码中也预留了标志,用来控制它的显示与隐藏。NavigationBar的显示与隐藏的控制是放在SystemU中的,具体的路径
是:\frameworks\base\packages\SystemUI。对android4.0以上的手机而言,SystemUi包含两部
分:StatusBar和NavigationBar。在SystemUI的工程下有一个类PhoneStatusBar.java,在该类中可以发现关
于控制NavigationBar的相关代码:

  在start()方法里可以看到NavigationBar是在那时候被添加进来,但只是添加,决定它显示还是隐藏是在后面控制的。

  <span style="font-size:18px;">@Override
  public void start()
  mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
  .getDefaultDisplay();
  updateDisplaySize();

  /// M: Support Smartbook Feature.
  if (SIMHelper.isMediatekSmartBookSupport())
  /// M: [ALPS01097705] Query the plug-in state as soon as possible.
  mIsDisplayDevice = SIMHelper.isSmartBookPluggedIn(mContext);
  Log.v(TAG, "start, mIsDisplayDevice=" + mIsDisplayDevice);
  

  super.start(); // calls createAndAddWindows()

  addNavigationBar();

  // Lastly, call to the icon policy to install/update all the icons.
  mIconPolicy = new PhoneStatusBarPolicy(mContext);

  mHeadsUpObserver.onChange(true); // set up
  if (ENABLE_HEADS_UP)
  mContext.getContentResolver().registerContentObserver(
  Settings.Global.getUriFor(SETTING_HEADS_UP), true,
  mHeadsUpObserver);
  
  </span>
  其中的addNavigationBar()具体的实现方法如下:

  <span style="font-size:18px;"> // For small-screen devices (read: phones) that lack hardware navigation buttons
  private void addNavigationBar()
  if (DEBUG) Slog.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
  if (mNavigationBarView == null) return;

  prepareNavigationBarView();

  mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
  </span>
 
 可以看到Navigationbar实际上windowmanager向window窗口里添加一个view。在调用
addNavigationBar()方法之前会回调start()的父方法super.start()来判断是否要添加NavigationBar。在
super.start()的调用父类方法里会调用createAndAddWindows(),该方法内会判断是否需要添加显示
NavigationBar,然后决定是否要实例化NavigationBarView.
  <span style="font-size:18px;">try
  boolean showNav = mWindowManagerService.hasNavigationBar();
  if (DEBUG) Slog.v(TAG, "hasNavigationBar=" + showNav);
  if (showNav)
  mNavigationBarView =
  (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);

  mNavigationBarView.setDisabledFlags(mDisabled);
  mNavigationBarView.setBar(this);
  
   catch (RemoteException ex)
  // no window manager? good luck with that
  </span>
  WindowManagerService类实现了WindowManagerPolicy的接口,所以WindowManagerService会回调WindowManagerPolicy 的hasNavigationBar()接口,

  <span style="font-size:18px;"> @Override
  public boolean hasNavigationBar()
  return mPolicy.hasNavigationBar();
  </span>

  Policy向下调用实际上调用的是PhoneWindowManager实现的hasNavigationBar方法,下面代码是PhoneWindowManager中的hasNavigationBar()方法。
  <span style="font-size:18px;">// Use this instead of checking config_showNavigationBar so that it can be consistently
  // overridden by qemu.hw.mainkeys in the emulator.
  public boolean hasNavigationBar()
  return mHasNavigationBar;
  </span>
  而mHasNavigationBar的赋值可以在PhoneWindowManager中的setInitialDisplaySize(Display display, int width, int height, int density)方法中找到,

  <span style="font-size:18px;"> if (!mHasSystemNavBar)
  mHasNavigationBar = mContext.getResources().getBoolean(
  com.android.internal.R.bool.config_showNavigationBar);
  // Allow a system property to override this. Used by the emulator.
  // See also hasNavigationBar().
  String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
  if (! "".equals(navBarOverride))
  if (navBarOverride.equals("1")) mHasNavigationBar = false;
  else if (navBarOverride.equals("0")) mHasNavigationBar = true;
  
   else
  mHasNavigationBar = false;
  </span>
  从上面代码可以看到mHasNavigationBar的值的设定是由两处决定的:
  1.首先从系统的资源文件中取设定值config_showNavigationBar, 这个值的设定的文件路径是frameworks/base/core/res/res/values/config.xml
  <!-- Whether a software navigation bar should be shown. NOTE: in the future this may be
  autodetected from the Configuration. -->
  <bool name="config_showNavigationBar">false</bool>
  2.然后系统要获取“qemu.hw.mainkeys”的值,这个值可能会覆盖上面获取到的mHasNavigationBar的值。如果 “qemu.hw.mainkeys”获取的值不为空的话,不管值是true还是false,都要依据后面的情况来设定。
  所以上面的两处设定共同决定了NavigationBar的显示与隐藏。

以上是关于如何在Android中自定义动画的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android 中自定义进度条

如何在 Blend 中自定义动画?

如何在 Android 应用中自定义顶部状态栏?

显示导航控制器时在动画中自定义幻灯片根本无法快速工作

加入域时如何将自定义组自动加入到本地管理员组

vue中自定义指令组件化生命周期节流和防抖获取DOMmint-ui简介过渡和动画