Android 常见错误

INSTALL_FAILED_NO_MATCHING_ABIS 的解决办法

出现时机

INSTALL_FAILED_NO_MATCHING_ABIS 的解决办法,在 Android 模拟器上安装 apk 的时候出现

解决办法

是由于使用了 native libraries ,该 native libraries 不支持当前的cpu的体系结构。

INSTALL_FAILED_NO_MATCHING_ABIS is when you are trying to install an app that has native libraries and it doesn't have a native library for your cpu architecture. For example if you compiled an app for armv7 and are trying to install it on an emulator that uses the Intel architecture instead it will not work.

现在安卓模拟器的CPU/ABI一般有三种类型,INTEL X86,ARM,MIPS,

如果选择用INTEL X86出现 INSTALL_FAILED_NO_MATCHING_ABIS 的错误,那就改用ARM的

参考:http://stackoverflow.com/questions/24572052/install-failed-no-matching-abis-when-install-apk

Apache HTTP Client

Android 6.0 中移除 了 Apache HTTP client

程序包org.apache.http.client.methods不存在

android {
    useLibrary 'org.apache.http.legacy'
}

2016-09-29 Android , AndroidDev

android 6 runtime permission

在 target API 23 之前,应用申请权限为一次性给予,开发者需要在 Manifest 中使用 users-permission 来申请权限,而用户则是在安装应用时一次性赋予应用所有申请的权限。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.app.myapp" >
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    ...
</manifest>

而在 Android API Level 23 也就是 Android 6.0 以后权限的问题被进一步细化,开发者可以在运行时申请权限(Runtime permissions),而此时众多的Android 权限也被细分为 Normal 普通权限 和 Dangerous 危险权限,普通权限和 6.0 以前一样在 Manifest 中申请,并且在安装应用时一次性赋予,而危险权限的申请则需要额外的注意。否则可能会引发异常

java.lang.SecurityException: Permission Denial

所有的权限列表在官方文档 可以查到,每一个权限都标明了 Protection level: normal or dangerous.

运行时获取权限

申请照相权限例子:

/**
 * Requests the Camera permission.
 * If the permission has been denied previously, a SnackBar will prompt the user to grant the
 * permission, otherwise it is requested directly.
 */
private void requestCameraPermission() {
    Log.i(TAG, "CAMERA permission has NOT been granted. Requesting permission.");

    // BEGIN_INCLUDE(camera_permission_request)
    if (ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.CAMERA)) {
        // Provide an additional rationale to the user if the permission was not granted
        // and the user would benefit from additional context for the use of the permission.
        // For example if the user has previously denied the permission.
        Log.i(TAG,
                "Displaying camera permission rationale to provide additional context.");
            Snackbar.make(mLayout, R.string.permission_camera_rationale,
                    Snackbar.LENGTH_INDEFINITE)
                    .setAction(R.string.ok, new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            ActivityCompat.requestPermissions(MainActivity.this,
                                    new String[]{Manifest.permission.CAMERA},
                                    REQUEST_CAMERA);
                        }
                    })
                    .show();
    } else {

        // Camera permission has not been granted yet. Request it directly.
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
                REQUEST_CAMERA);
    }
    // END_INCLUDE(camera_permission_request)
}

重要方法

检查权限

ContextCompat 中

public static int checkSelfPermission (Context context, String permission)

检查是否拥有权限,如果有返回 PackageManager PERMISSION_GRANTED, 如果没有则返回 PERMISSION_DENIED

在 ActivityCompat 中

public static boolean shouldShowRequestPermissionRationale (Activity activity, String permission)

在 UI 中弹出对话框申请权限,仅仅只有当当前功能需要权限的时候才需要申请。需要参数,目标 activity ,和需要申请的权限,返回是否需要弹出对话框。该方法监测是否需要申请权限。

申请权限

public static void requestPermissions (Activity activity, String[] permissions, int requestCode)

申请权限的权限需要在 manifest 中定义,权限需要是危险权限 #PROTECTION_DANGEROUS dangerous

普通权限 PROTECTION_NORMAL 会在安装时一次性授予, 同样 PROTECTION_SIGNATURE 权限也会在安装时授予。

定义 signature 权限时,不仅需要添加权限说明,还需要相同的签名。

如果app不拥有申请的权限,在用户接受或者拒绝之后,会收到一个回调,说明是否授予了权限,需要实现接口。

public abstract void onRequestPermissionsResult (int requestCode, String[] permissions, int[] grantResults)

因为申请权限不能保证被授予,所以无论在有没有权限的情况下都要保证app能够运行。

requestPermissions 方法会开始一个 activity 来让用户选择是否授予权限,因此程序自身 Activity 可能会 paused 或者 resumed。进一步,授予某些权限可能会导致重启应用,这种情况下系统会重新生成 activity stack ,之后再调用 onRequestPermissionsResult 。

回调方法 onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 的第一个参数时 requestPermissions 方法传入的,第二个参数是申请的权限,Never null,第三个参数是是否授予权限的结果,也就是 PERMISSION_GRANTED 或者 PERMISSION_DENIED,Never null。

权限介绍

Android 的权限分成四个类别

  • normal 普通级别
  • dangerous 危险级别
  • signature 签名级别
  • signatureOrSystem 系统/签名级别

前两个权限直接定义时候即可,6.0以后 dangerous 可以在运行时申请。后两个权限为高级权限,拥有 platform 级别认证才可以申请,应用在没有权限情况下做受限操作,应用会被系统杀掉。

在 Manifest 中使用 来定义权限, 使用 来申请权限。申请的权限需要被系统或者其他应用定义,否则视为无效申请。

users Permission

申请普通权限可以使用 简单语法如下:

<uses-permission android:name="string"
        android:maxSdkVersion="integer" />

前一个参数简单,后以参数 maxSDKVersion 表示,如果应用从某一个版本开始不需要特定的权限,可以设置该属性。表示高于该 API Level 之后就不授予该权限。

如下定义

<uses-permission-sdk-23 android:name="string"
        android:maxSdkVersion="integer" />

表示只有当app运行在 API Level 23 或以上时才申请权限,以下不申请权限。

自定义权限

<permission android:description="string resource"
            android:icon="drawable resource"
            android:label="string resource"
            android:name="string"
            android:permissionGroup="string"
            android:protectionLevel=["normal" | "dangerous" |
                                     "signature" | "signatureOrSystem"] />
  • android:description:对权限的描述,比lable更加的详细,介绍该权限的相关使用情况,比如当用户被询问是否给其他应用该权限时。注意描述应该使用的是string资源,而不是直接使用string串。 android:icon:用来标识该权限的一个图标。
  • android:label:权限的一个给用户展示的简短描述。方便的来说,这个可以直接使用一个string字串来表示,但是如果要发布的话,还是应该使用string资源来表示。
  • android:name:权限的唯一名字,由于独立性,一般都是使用包名加权限名,该属性是必须的,其他的可选,未写的系统会指定默认值。
  • android:permissionGroup: 权限所属权限组的名称,并且需要在这个或其他应用中使用标签提前声明该名称,如果没有声明,该权限就不属于该组。
  • android:protectionLevel:权限的等级

reference


2016-09-27 Android , AndroidDev

Android Snackbar 使用

Snackbar 提供操作的轻量级反馈。显示在手机底部或者大屏幕的左下,Snackbar显示在所有界面的最上层,并且只显示一次。

Snackbar 可以包含一个操作,使用 setAction(CharSequence, android.view.View.onClickListener) 设置。 Snackbar 可以通过 setCallback(Callback) 来设置显示和消失的回调 Snackbar.Callback

显示时间长短的常量

  • int LENGTH_INDEFINITE 没有操作不消失
  • int LENGTH_LONG 显示长时间
  • int LENGTH_SHORT 显示短时间

Android Support Library (22.2.1) 起才支持 LENGTH_INDEFINITE。如果使用该属性, Snackbar 会一直显示,直到调用 dismiss() 或者下一个 Snackbar 出现。

make 方法的第一个参数表示 Snackbar 会寻找该 View 来hold Snackbar 的View。第二个参数为需要显示的字符串。第三个参数为显示时间,使用以上三个常量。

Snackbar.make(mLayout, R.string.permission_camera_rationale,
        Snackbar.LENGTH_INDEFINITE)
        .setAction(R.string.ok, new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ActivityCompat.requestPermissions(MainActivity.this,
                        new String[]{Manifest.permission.CAMERA},
                        REQUEST_CAMERA);
            }
        })
        .setCallback(new Snackbar.Callback() {
            @Override
            public void onDismissed(Snackbar snackbar, int event) {
                super.onDismissed(snackbar, event);
                Toast.makeText(getApplicationContext(), "onDismissed", Toast.LENGTH_LONG).show();
            }

            @Override
            public void onShown(Snackbar snackbar) {
                super.onShown(snackbar);
                Toast.makeText(getApplicationContext(), "onShown", Toast.LENGTH_LONG).show();
            }
        })
        .show();

综上,Snackbar 作为带响应的通知来说能带来不错的体验。相较于 Toast 来说,Snackbar 能够提供一种操作,对于修改内容来说,提供短时间内的撤销操作应该是不错的。其他能够想到的一些操作,比如撤销邮件的发送,撤销消息的发送,等等。


2016-09-26 Android , AndroidDev

Android Notification

Android Support v4 包中的 NotificationCompat.Builder ,在 Android 3.0 API Level 11 中才有 Notification.Builder。

创建通知

您可以在 NotificationCompat.Builder 对象中为通知指定 UI 信息和操作。要创建通知,请调用 NotificationCompat.Builder.build(),它将返回包含您的具体规范的 Notification 对象。要发出通知,请通过调用 NotificationManager.notify() 将 Notification 对象传递给系统。

必需的通知内容

Notification 对象必须包含以下内容:

  • 小图标,由 setSmallIcon() 设置
  • 标题,由 setContentTitle() 设置
  • 详细文本,由 setContentText() 设置

setContentIntent() 设置用户点击通知之后的动作。

NotificationCompat.Builder 在构造时自动设置时间为 System.currentTimeMillis() ,设置 Audio stream 为 STREAM_DEFAULT

NotificationCompat.Builder 中可选的其他参数有

  • setAutoCancel() 设置通知是否自动消失
  • setLargeIcon(Bitmap icon) 设置大图标
  • setTicker() 设置通知第一次到达时在status bar 上显示的文字,在Android L版本之后不再显示
  • setLights() 设置通知呼吸灯的颜色以及频率
  • setDeleteIntent() 设置用户直接在通知列表删除通知时的动作
  • setAction() 设置通知中的动作

重要类

NotificationCompat.Builder 不必多说,其他重要的类有 NotificationCompat.Action 通知动作需要包含一个图标,一个标签,一个 PendingIntent 。通知动作在 Android 4.1 之前不会显示。

如果要在通知栏显示复杂View,需要设定 RemoteView,使用 setContent(RemoteViews views) 方法

NotificationCompat.Style 用来展现更丰富的通知内容的样式,直接子类有

  • NotificationCompat.BigPictureStyle
  • NotificationCompat.BigTextStyle
  • NotificationCompat.InboxStyle
  • NotificationCompat.MediaStyle

BigPictureStyle 可以显示大图,如果通知附带一张大图片,可以使用该样式。通常 Android 截屏之后显示的通知就是。

Notification notif = new Notification.Builder(mContext)
     .setContentTitle("New photo from " + sender.toString())
     .setContentText(subject)
     .setSmallIcon(R.drawable.new_post)
     .setLargeIcon(aBitmap)
     .setStyle(new Notification.BigPictureStyle()
         .bigPicture(aBigBitmap))
     .build();

InboxStyle 可以产生多行文本的通知,至多可以显示5个字符串。

Notification noti = new Notification.Builder()
     .setContentTitle("5 New mails from " + sender.toString())
     .setContentText(subject)
     .setSmallIcon(R.drawable.new_mail)
     .setLargeIcon(aBitmap)
     .setStyle(new Notification.InboxStyle()
         .addLine(str1)
         .addLine(str2)
         .setContentTitle("")
         .setSummaryText("+3 more"))
     .build();

还有可以显示很多文字的 BigTextStyle,和比较复杂的 MediaStyle。

Android 4.4 以下的通知

android notification

android notification expand

Android 5.0 及以上的通知

android notification 5

reference


2016-09-25 Android , AndroidDev

Android monkey test

Android UI monkey 测试

伪随机用户事件,发送到模拟器或者设备,用来对应用程序进行压力测试。

功能:

  • 设定事件数
  • 操作限定到某一个特定 package
  • 事件类型和频率
  • 调试选项

报错:

  • 如果应用 crash 或者遇到 unhandled exception , monkey 会停止并上报错误
  • 如果应用产生 not responding error , monkey 也会停止并上报

基本使用

基本语法:

adb shell monkey [options] <event-count>

下面的例子是测试在特定包上,发送500随机事件

adb shell monkey -p your.package.name -v 500

一些有用的选项:

Option 描述
-v -vv -vvv 三档等级,越来越详细
--throttle 事件和事件之间延迟
--pct-touch 点击事件,单个点按下抬起,后接百分比
--pct-motion 滑动事件,某一点按下,随机移动距离,抬起
--pct-trackball 模拟轨迹球,包含随机的移动,可能伴随着点击
--pct-nav 外部输入,上下左右操作(没有使用过,但似乎游戏可用)
--pct-syskeys 调整系统事件,包括Home,back ,音量键等等
-p 允许的 package name
-c 指定允许monkey跑的 category,下面有例子
--ignore-crashes 通常monkey 遇到crash 会停止,此选项忽略crash 直到指定次数跑完
--ignore-timeouts 忽略 ANR
--ignore-security-exceptions 忽略Permission error 或者其他 unhandled exception

测试特定Activity

Manifest 文件中定义 category:

<activity android:name="MonkeyActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.MONKEY" />
    </intent-filter>
</activity>

使用命令:

adb shell monkey -p my.package -c android.intent.category.MONKEY -v 500

防止通知栏下拉

在 Android 5.0 Lollipop 及以上系统中可以使用 screen pinning.

  • 在 settings>security>screen pinning
  • 点击 程序切换按钮 multitasking
  • 点击图标上的绿色图钉, pin icon

此时再运行则不会跳出应用。

停止 MonkeyTest

使用以下命令停止 monkey Test :

adb shell ps | awk '/com\.android\.commands\.monkey/ { system("adb shell kill " $2) }'

monkeyrunner

features 功能:

  • 多设备控制,同时在多台设备测试
  • 功能测试
  • 回归测试
  • 可扩展自动化

monkeyrunner 使用 Jython

reference


2016-09-22 Andorid , AndroidDev , monkey

Gradle 重复导入错误

记录一下纠结了两天的问题。

问题

项目中遇到如下错误:

Error:Execution failed for task ':mobile:packageAllDebugClassesForMultiDex'.
> java.util.zip.ZipException: duplicate entry:android/support/annotation/AnyRes.class

这个问题是因为项目中引入了 support-annotations 包,但是导入的其他 jar 包中包含了这个package,重复导致了 duplicate entry 错误。 在 app 的 build.gradle 中加入:

android{
    ...
    configurations {
        all*.exclude group: 'com.android.support', module: 'support-annotations'
    }
}

总结

项目中尽量只导入一个库,在引用其他库时注意查看该库的依赖版本,在导入 support v4 包或者其他 v7 包时尽量考虑只在主项目中导入一次,在任何 library 中减少使用。 这个问题的由来,应该也是项目历史原因,在我接手时,项目 build.gradle 中 buildTypes 是这样的:

buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
    }
    debug {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
    }
}

这里竟然 debug 和 release 使用了同样的 minifyEnabled 配置,默认情况下 minifyEnabled 应该是 false,设置成 true 之后,debug 下无法调试,无法设置断点。查看 git 历史这个问题竟然从项目初始就这样,真无法想象他们是怎么调试的。然后将 minifyEnabled 设置成 false 之后就产生了 multiDex 错误,然后是上面的错误。 buildTypes 下 minifyEnabled 选项, 设置为 true 情况下, debug 无法设置断点及调试1, 但是这个选线在 release 下非常有用,可以减少包的大小,缩减无用代码(shrink unless codes)。

修改后的配置:

buildTypes {
   release {
       minifyEnabled true
       proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
   }
   debug {
       minifyEnabled false
       debuggable true
   }
}

multiDex 的错误倒是还好解决2,设置:

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.0"

         defaultConfig {
             minSdkVersion 14 //lower than 14 doesn't support multidex
             targetSdkVersion 22

             // Enabling multidex support.
             multiDexEnabled true
         }
}

dependencies {
    compile 'com.android.support:multidex:1.0.1'
}

然后在 Application 类中设置:

public class YouApplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(this);
    }

}

正是这个错误促使我看了 Gradle 中依赖的语法,总结如下:

Dependencies, Android Libraries and Multi-project setup

Gradle 项目可能依赖其他组件,包括外部的二进制文件,或者另一个 Gradle 项目。

本地依赖

下面代码可以添加本地 libs 目录下所有 jar 包:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}


android {
    ...
}

如果想要配置本地 jar 包,可以依照下面顺序:

  • 将jar 包放入 libs 目录
  • 右击 jar 包,选择 “Add as library”
  • 确认 compile files('libs/xxx.jar')build.gradle 文件中,或者 compile fileTree(dir: 'libs', include: '*.jar')
  • rebuild

远程依赖

Gradle 支持从远端 Maven 或者 Ivy repositories 自动拉取依赖。首先远端 repository 需要加入列表,其次需要定义具体依赖。

repositories {
     jcenter()
}


dependencies {
    compile 'com.google.guava:guava:18.0'
}


android {
    ...
}

Note:

  • jcenter() 是远端库的URL缩写, Gradle 支持本地和远程库。
  • Gradle 如果发现依赖需要使用其他依赖会自动pull其他依赖。

多项目配置

通常用子目录来配置 libraries ,例如

MyProject/
 + app/
 + libraries/
    + lib1/
    + lib2/

三个项目, Gradle 通过如下指定:

:app
:libraries:lib1
:libraries:lib2

每一个项目都有自己的 build.gradle 文件,另外在跟目录下有 settings.gradle 来描述工程:

MyProject/
 | settings.gradle
 + app/
    | build.gradle
 + libraries/
    + lib1/
       | build.gradle
    + lib2/
       | build.gradle

settings.gradle 中的描述:

include ':app', ':libraries:lib1', ':libraries:lib2'

然后在 :app 项目中定义,依赖 library :

dependencies {
    compile project(':libraries:lib1')
}

排除导入的包

可以使用如下的语法排除 v4 包:

compile ('com.android.support:recyclerview-v7:+') {
    exclude module: 'support-v4'
}

同样也可以在:

android{
    configurations {
        'com.android.support', module: 'support-v4'
    }
}

下排除。

更加系统的学习 Android Gradle build system 可以参考官方文档,或者中文译本3

关于 build.gradle 文件中的字段属性,可以参考 Android Plugin DSL Reference.

referencee

  1. http://stackoverflow.com/questions/31926189/android-debugging-with-minifyenabled-true 

  2. StackOverflow官网 

  3. Gradle for Android 


2016-09-18 Android , AndroidDev , Gradle , Google

常用 adb command 命令

一些常用的 adb 命令,包括Android录屏,及从电脑复制文件,从 Android 设备拉取文件等等。

adb 的全称是 Android Debug Bridge, 这个命令可以用来发送一系列指令给 Android 设备,包括但不限于基本的 Linux 指令。只要手机或者任何 Android 设备开启了Debug模式并且取得信任,adb 命令几乎可以用来做任何事情。因此网络上也存在使用 adb 来入侵同一局域网下的 Android 盒子的例子。

adb devices

来查看设备是否已经连接

$ adb devices
List of devices attached
2dd11c6e	device

adb push

将文件复制到 Android 设备上:

adb push filename.txt /sdcard/Download/

adb pull

将文件从 Android 设备上拉回本地

adb pull /sdcard/Download/filename.txt ~/filename.txt

adb reboot

重启设备,在刷机时经常使用

adb reboot-bootloader

重启进去 bootloader

或者使用 adb reboot recovery 直接进入 recovery 模式

adb shell

直接使用该命令可以进入手机的 Shell.

修改权限

进入 Android Shell 之后就可以使用任何 Linux 命令来直接操作 Android 设备, 比如:

adb shell chmod 666 /data/filename.txt

shell 中录制屏幕

在 shell 命令中可以使用 screenrecord 命令来录制屏幕。需要 Android 4.4 (API Level 19)及以上,该命令将屏幕保存成 MPEG-4 文件。不录制声音。

adb shell screenrecord /sdcard/Download/filename.mp4

使用 Ctrl + c 来停止录像,否则默认录制 3min 或者使用 --time-limit 参数来指定。

注意:

  • screenrecord 命令可以按照设备原始分辨率录屏,但是有些设备可能不支持原始分辨率,此时降低分辨率再尝试。
  • 不支持录屏中旋转屏幕

可选参数:

  • --help
  • --size <width*Height> 比如 1280*720.
  • --bit-rate <rate> 默认码率 4Mbps,6Mbps 可以设置 6000000.
  • --time-limit <TIME> 默认为180 (3min) 设置时间,单位秒
  • --rotate 旋转输出
  • --verbose 显示 log 信息,如果不设置,不显示任何信息

列出Android 设备上所有安装的应用

使用如下命令1:

adb shell 'pm list packages'

使用如下命令去除前面的 package:

adb shell pm list packages | awk -F ":" '{print $2}'
# 或者,-f 用来输出第二部分, -d 用来标示分割符号
adb shell pm list packages | cut -f 2 -d ":"

在使用 adb shell 进入 手机 Shell 之后可以使用, pm help 来获取更多关于 pm 命令的详情。关于 pm 的命令。

adb install

可以使用该命令安装应用程序

可以使用 -r 命令更新应用

adb install -r apkfilename.apk

同理可以使用 adb uninstall apkfilename.apk 来卸载应用。

使用 adb uninstall -k apkfilename.apk 可以卸载应用,但是保留数据。

reference

  1. https://gist.github.com/davidnunez/1404789 


2016-09-08 AndroidDev , Android , adb

Android Animation Interpolator

动画插值器,用来描述动画的变化率,这里讨论的 Interpolator 指的是 android.animation 包下的 TimeInterpolator。 以下所有的插值器都继承自 Interpolator , 而 Interpolator 接口直接继承自 TimeInterpolator , 自身并没有添加任何方法。

TimeInterpolator 中有

abstract float getInterpolation(float input) 方法,参数 input:input 参数是一个 float 类型,它取值范围是 0 到 1,表示当前动画的进度,取 0 时表示动画刚开始,取 1 时表示动画结束,取 0.5 时表示动画中间的位置。返回值:表示当前实际想要显示的进度。取值可以超过 1 也可以小于 0,超过 1 表示已经超过目标值,小于 0 表示小于开始位置。

input 的值只与时间相关,和我们设定没有任何关系。线性插值器函数实现:

public float getInterpolation(float input) {  
    return input;  
}  

AccelerateInterpolator

加速插值器,开始很慢,不断加速

public AccelerateInterpolator(float factor)

将factor设置为1.0f会产生一条y=x^2的抛物线。增加factor到1.0f之后为加大这种渐入效果(也就是说它开头更加慢,结尾更加快)

当fractor不为1时,轨迹曲线是 y=x^(2*fractor)(0<x<=1) 的曲线

DecelerateInterpolator

减速插值器,开始比较快然后减速的插值器,动画的快慢度。将factor值设置为1.0f时将产生一条从上向下的y=x^2抛物线。增加factor到1.0f以上将使渐入的效果增强(也就是说,开头更快,结尾更慢)

AccelerateDecelerateInterpolator

加速减速插值器,开始慢从中间后开始变快

LinearInterpolator

线性插值器

BounceInterpolator

弹跳插值器

AnticipateInterpolator

回荡秋千插值器

AnticipateOvershootInterpolator

CycleInterpolator

正弦周期变化插值器

OvershootInterpolator

reference


2016-09-06 Android , AndroidDev

Android ImageView ScaleType

ImageView 的 ScaleType 属性决定了图片在 View 上显示时的样子,是比例缩放,还是显示图片的整体或者局部等等。对于一张图片,有其自身的大小,而 ImageView 也有其自身的大小,这两者如何完美的合作其结果很重要的设置便是 ScaleType 属性。

设置该属性的方式有两种:

  1. 在布局 Layout 中 ImageView 中定义 android:scaleType="center"
  2. 在代码中调用 imageView.setScaleType(ImageView.ScaleType.CENTER);

8 种 ScaleType

所有的 Scale 方式有 8 种,文档 中可以查看到,下文也会详细介绍。

ImageView.ScaleType.CENTER

按图片原大小居中显示,不缩放原图片。当原始图片长/宽超过View的长/宽,则截取图片的居中部分显示,当图片长/宽小于 View 长宽时,可能造成填充不完全, ImageView 周围有空白的情况。

在 xml 中使用定义: android:scaleType="center"

ImageView.ScaleType.CENTER_CROP

按比例扩大图片的size居中显示,不改变图片宽高比,使得图片长(宽)等于或大于View的长(宽),图片填充满 ImageView,可能产生裁剪,此时能保证 View 被完整填充。也就意味着这种裁剪方式会使 ImageView 被完整填充,对于高大于宽的图片,高部分就会被裁剪。

在 xml 中使用定义: android:scaleType="centerCrop"

ImageView.ScaleType.CENTER_INSIDE

将图片的内容完整居中显示,通过按比例缩小或原来的size使得图片长/宽小于或等于View的长/宽,ImageView 可能产生空白部分。对于高度大于宽度图片,则高度填充整个 ImageView 高度,宽度则小于 ImageView 宽度,则宽度两边产生空白。

在 xml 中使用定义: android:scaleType="centerInside"

ImageView.ScaleType.FIT_CENTER

把图片按比例扩大/缩小到 ImageView 的高度或者宽度,使用 CENTER 方式居中显示,也就是使用该方式,使用图片长的一部分适应。

FIT_START, FIT_END 在图片缩放效果上与 FIT_CENTER 一样,只是显示位置不同,FIT_START 是置于顶部,FIT_CENTER 居中,FIT_END 置于底部。

在 xml 中使用定义: android:scaleType="fitCenter"android:scaleType="fitEnd"android:scaleType="fitStart"

ImageView.ScaleType.FIT_XY

不按比例缩放图片,把图片塞满整个 View ,如果 View 与 原图片长宽不对应,可能造成图片长宽比改变,以及图片变形。

在 xml 中使用定义: android:scaleType="fitXY"

ImageView.ScaleType.MATRIX

通过变化矩阵来设置 ImageView 大小。可以通过设置 matrix 然后手动利用 matrix 来调整 ImageView 可以实现 ImageView 的双指缩放和旋转。不过似乎用的很少。

具体细节可以继续再讨论。

如果要看效果图,这里 很不错,一目了然。

在 xml 中使用定义: android:scaleType="matrix"

其他问题

ImageView src 与 background 属性区别

每一种 View 都可以包含背景图片,而 ImageView 的 src 可以设置 ScaleType ,adjustViewBounds 等属性。

保证 ImageView 长宽比

center, center_crop, center_inside, fit_center, fit_start, fit_end 等方式都会改变图片的比例,但是会有一定程度的裁剪。

保持图片比例填满宽度

使用如下 Layout

<ImageView
    android:id="@id/img"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:scaleType="fitCenter" />

使用 fitCenter 来使图片填充整个 ImageView 宽度,并且进行等比例放缩。

答案来自 StackOverflow

reference


2016-09-05 Android , AndroidDev

Android Animation

Android 的动画实现有不同的方式,在 3.0 以前 Android 的动画很简单只能在 View 层做,在后期版本中不断的加入动画实现,至今已经有这非常完善的动画系统了。Android 系统提供了 Property animation 和 View animation 两大动画系统。除去这两大动画系统之外,还有一类 Drawable Animation, 允许加载 drawable 并且一帧一帧播放:

  • View Animation 视图动画
  • Property Animation 属性动画
  • Drawable Animation 帧动画

另外官方文档也有另外一种分类方法 将 Tween Animation 补间动画 和 Frame Animation 逐帧动画归纳为 View Animation。 见文档

View Animation

旧的动画系统,只能用来对 View 进行动画。View Animation 很简单,不过只能支持简单的缩放、平移、旋转、透明度基本的动画,且有一定的局限性。

Android中提供的 4种 View 动画:

  • AlphaAnimation 透明度动画效果,可以实现渐隐渐现
  • ScaleAnimation 缩放动画效果
  • TranslateAnimation 位移动画效果
  • RotateAnimation 旋转动画效果

以及 AnimationSet 动画集合,可以混合使用多种动画。 View Animation 都在 view.animation 包名下。

Android 系统在 3.0 之后推出了 Property Animation (属性动画)框架,属性动画可以用于任何对象,相比属性动画,View Animation (视图动画)的一个非常大的缺陷就是不具备交互性,当某个元素发生视图动画后,其响应事件的位置还依然是在动画前的地方,所以视图动画只能做普通的动画效果,避免交互的发生。但它的优点也非常明显,即效率比较高且使用方便。

视图动画使用非常简单,不仅可以通过XML文件来描述一个动画过程,同样可以使用代码来控制整个动画过程。

通用方法

animation.setDuration(long durationMillis);           // 设置动画时长
animation.setRepeatCount(int repeatCount);        // 设置动画重复次数
animation.setFillAfter(boolean);           // 动画执行完后是否停留在执行完的状态
animation.setFillBefore(boolean);                 // 动画执行前是否应用状态
animation.setStartOffset(long startOffset);          // 动画执行前的等待时间
animation.setInterpolator(Interpolator i);         // 设置动画插值
// 设置重复模式, 这个模式只有在 repeat count 大于0 或者 INFINITE 时才生效,有 RESTART 和 REVERSE 两种,RESTART 为重头播放,REVERSE  为逆向播放动画。
animation.setRepeatMode(int repeatMode);
animation.setStartTime(long startTimeMillis);      // 设置动画开始时间
animation.start();            // 开始动画
animation.startNow();           // 立即开始动画

启动动画的两种方式:

view.startAnimation(animation);

//或者设置动画之后延后开始动画

view.setAnimation(animation);
animation.start();

使用 xml 定义动画

当使用 xml 配置动画时,需要放到 res 目录下 res/anim/ 。 xml 定义的 root 元素可以是 :

  • <alpha>
  • <scale>
  • <translate>
  • <rotate>
  • interpolator 元素
  • <set> 或者 set 中包含其他 set

举例:

<set android:shareInterpolator="false">
    <scale
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:fromXScale="1.0"
        android:toXScale="1.4"
        android:fromYScale="1.0"
        android:toYScale="0.6"
        android:pivotX="50%"
        android:pivotY="50%"
        android:fillAfter="false"
        android:duration="700" />
    <set android:interpolator="@android:anim/decelerate_interpolator">
        <scale
           android:fromXScale="1.4"
           android:toXScale="0.0"
           android:fromYScale="0.6"
           android:toYScale="0.0"
           android:pivotX="50%"
           android:pivotY="50%"
           android:startOffset="700"
           android:duration="400"
           android:fillBefore="false" />
        <rotate
           android:fromDegrees="0"
           android:toDegrees="-45"
           android:toYScale="0.0"
           android:pivotX="50%"
           android:pivotY="50%"
           android:startOffset="700"
           android:duration="400" />
    </set>
</set>

可以使用如下代码,从 XML 中加载动画:

ImageView spaceshipImage = (ImageView) findViewById(R.id.spaceshipImage);
Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump);
spaceshipImage.startAnimation(hyperspaceJumpAnimation);

接下来就分别看一下不同 View Animation 的使用。

AlphaAnimation

AlphaAnimation 动画,可实现淡入淡出。

AlphaAnimation(float fromAlpha, float toAlpha)

参数说明:float 设置透明度渐变动画, 1.0 完全不透明, 0.0 是完全透明

然后利用 View 的 startAnimation 方法就可以使用。

RotateAnimation

旋转动画,可以在 X-Y 平面设置旋转的动画,可以指定旋转中心, (0,0) 表示 top left 坐点,如果不指定,默认为左上角为旋转中心。

有四个构造函数:

RotateAnimation(Context context, AttributeSet attrs)

// 默认旋转中心为 (0,0)
RotateAnimation(float fromDegrees, float toDegrees)

RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY)

RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)

参数说明:

  • float fromDegrees 旋转的开始角度。默认为角度, 0为原始角度, 90为顺时针个旋转90度。
  • float toDegrees 旋转的结束角度。
  • float pivotX X方向旋转中心,默认为0,左
  • float pivotY Y方向旋转中心,默认为0, 上边缘
  • int pivotXType X 轴的伸缩模式,可以取值为 Animation.ABSOLUTERELATIVE_TO_SELFRELATIVE_TO_PARENT。决定了 pivotXValue 的方式。
  • float pivotXValue X轴坐标的伸缩值,当方式为 ABSOLUTE 时,可以为固定值,也可以为百分数,1.0 为 100%。
  • int pivotYType:Y轴的伸缩模式,可以取值为 ABSOLUTERELATIVE_TO_SELFRELATIVE_TO_PARENT
  • float pivotYValue:Y轴坐标的伸缩值。同 X 轴

ScaleAnimation

放缩动画,可指定缩放中心。

ScaleAnimation(Context context, AttributeSet attrs)

ScaleAnimation(float fromX, float toX, float fromY, float toY)

ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY)

ScaleAnimation(float fromX, float toX, float fromY, float toY, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)

参数说明:

  • float fromX 动画起始时 X坐标上的伸缩尺寸
  • float toX 动画结束时 X坐标上的伸缩尺寸
  • float fromY 动画起始时Y坐标上的伸缩尺寸
  • float toY 动画结束时Y坐标上的伸缩尺寸
  • float pivotX 缩放中心X坐标
  • float pivotY 缩放中心Y坐标
  • int pivotXType 动画在X轴 pivotValue 如何伸缩,有 Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF,Animation.RELATIVE_TO_PARENT..
  • float pivotXValue 动画相对于物件的X坐标的开始位置
  • int pivotYType 动画在Y轴相对于物件位置类型
  • float pivotYValue 动画相对于物件的Y坐标的开始位置

TranslateAnimation

位移动画,可以控制位置的动画

TranslateAnimation(Context context, AttributeSet attrs)

TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)

TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue, int fromYType, float fromYValue, int toYType, float toYValue)

参数说明:

  • float fromXDelta 动画开始的点离当前View X坐标上的差值
  • float toXDelta 动画结束的点离当前View X坐标上的差值
  • float fromYDelta 动画开始的点离当前View Y坐标上的差值
  • float toYDelta 动画开始的点离当前View Y坐标上的差值
  • int fromType 决定 fromXValue 如何被调用,Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or Animation.RELATIVE_TO_PARENT
  • int toXType 参数同上
  • int fromYType 参数同上
  • int toYType 参数同上

Property Animation

Android 3.0, API 11 及以上,可以对任意类型进行动画。 属性动画在系统的 android.animation 包名下。总的来说,属性动画就是,动画的执行类来设置动画操作的对象的属性、持续时间,开始和结束的属性值,时间差值等,然后系统会根据设置的参数动态的变化对象的属性。

可定义如下参数:

  • Duration 动画的持续时间,默认300ms
  • Time interpolation 动画插值,LinearInterpolator 等等,定义动画变化率,可单独展开讲
  • Repeat count 重复动作,无限播放,或者到达时间之后停止,或者反转播放动画
  • Animator set 动画集合
  • Frame refresh delay 动画刷新时间,默认为 10ms, 最终依赖系统状态,不一定为 10ms。

常用方法

addListener(Animator.AnimatorListener listener);      // 添加监听器,可以手动设置监听,手动设置属性
isRunning();     // 当前动画是否在执行
isStarted(); 	 // 动画是否开始
isPaused();      // 暂停状态

setDuration(long duration);    // 设置动画时长,单位为毫秒
setInterpolator(TimeInterpolator value);  // 设置动画插值
setStartDelay(long startDelay);     // start() 方法被调用之后延时
setTarget(Object target);          // 设置动画主体
start();   // 开始动画
cancel();   // 取消动画

static ValueAnimator ofArgb(int... values);   // 颜色动画
static ValueAnimator	ofFloat(float... values)
static ValueAnimator	ofInt(int... values)
static ValueAnimator	ofObject(TypeEvaluator evaluator, Object... values)

setRepeatCount(int value);      // 可选值 INFINITE 无限循环动画
setRepeatMode(int value);     //  RESTART,REVERSE

Object	getAnimatedValue();    // 运动时,当前运动点的值

// 监听动画变化时的实时值 
public static interface AnimatorUpdateListener {  
    void onAnimationUpdate(ValueAnimator animation);  
}
// 添加方法为:public void addUpdateListener(AnimatorUpdateListener listener)  

// 监听动画变化时四个状态
public static interface AnimatorListener {  
    void onAnimationStart(Animator animation);		// 动画开始
    void onAnimationEnd(Animator animation);  		// 动画结束
    void onAnimationCancel(Animator animation);  	// 动画 cancel
    void onAnimationRepeat(Animator animation);  	// 动画重复
}
//添加方法为:public void addListener(AnimatorListener listener)  

removeAllListeners();
removeListener(Animator.AnimatorListener listener);
removePauseListener(Animator.AnimatorPauseListener listener);

默认的 ValueAnimator 使用非线性的插值器 interpolation,加速进入减速退出,可以通过 setInterpolator(TimeInterpolator) 来改变默认的行为。

xml 定义动画

可以类似 View Animation 定义 xml 来创建动画, 动画资源文件需要保存在 res/animator/ 目录下。Animator 动画支持:

  • ValueAnimator <animator>
  • ObjectAnimator <objectAnimator
  • AnimatorSet <set>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueFrom="1"
    android:valueTo="0"
    android:valueType="floatType"
    android:repeatCount="1"
    android:repeatMode="reverse"/>

<!-- 同样可以定义动画集合,顺序执行动画 -->
<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>

定义动画资源之后可以使用如下代码使用动画:

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.animator.property_animator);
set.setTarget(myObject);
set.start();

更多XML定义的语法可以参考官方 文档

ObjectAnimator 动画的执行类,后面详细介绍 ValueAnimator 动画的执行类 AnimatorSet 用于控制一组动画的执行:线性,一起,每个动画的先后执行等。 AnimatorInflater 用户加载属性动画的xml文件 TypeEvaluator 类型估值,主要用于设置动画操作属性的值。 TimeInterpolator 时间插值,有很多子类,可以具体展开再讲,AccelerateDecelerateInterpolator, AccelerateInterpolator, AnticipateInterpolator, AnticipateOvershootInterpolator, BaseInterpolator, BounceInterpolator, CycleInterpolator, DecelerateInterpolator, FastOutLinearInInterpolator, FastOutSlowInInterpolator, Interpolator, LinearInterpolator, LinearOutSlowInInterpolator, OvershootInterpolator, PathInterpolator

ValueAnimator

ValueAnimator animator = ValueAnimator.ofInt(0,1000);  
animator.setDuration(1000);  

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        int curValue = (int)animation.getAnimatedValue();  
        Log.d(TAG,"curValue: "+curValue);  
    }  
});  
animator.start();

同样也可以通过 ofFloat, ofObject 等等静态方法构造不同类型的 ValueAnimator。然后通过监听器动态地更新属性。

ObjectAnimator

ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();

ObjectAnimator 使用范围更广,但是同样也有很多约束条件。

Drawable Animation

可以在 res/drawable/ 目录下定义 XML 文件:

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">
    <item android:drawable="@drawable/rocket_thrust1" android:duration="200" />
    <item android:drawable="@drawable/rocket_thrust2" android:duration="200" />
    <item android:drawable="@drawable/rocket_thrust3" android:duration="200" />
</animation-list>

用如下代码,将 Drawable 附加到 ImageView 中:

AnimationDrawable rocketAnimation;

public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
  rocketImage.setBackgroundResource(R.drawable.rocket_thrust);
  rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
}

public boolean onTouchEvent(MotionEvent event) {
  if (event.getAction() == MotionEvent.ACTION_DOWN) {
    rocketAnimation.start();
    return true;
  }
  return super.onTouchEvent(event);
}

start() 方法不能在 onCreate() 方法中调用, AnimationDrawable 此时还没有附加到window 上,如果需要动画自动播放,可以在 onWindowFocusChanged() 中调用。

更多的 XML 语法可以参考官方文档

区别

  • View Animation 只能作用于 View, 而 Property Animation 因为修改属性,可以作用于任何对象。
  • 命名方式 View Animation 都以 Animation 结束,在 view.animation 包下,而 Property Animator 属性动画都是 Animator 结尾,并在 android.animation 包下。
  • View Animation 的动画实现是通过 Parent View 实现,实际在被 draw 时改变其位置,因此真实的属性并没有改变。而 Property Animator 属性动画正是相反,改变的属性值。

reference


2016-09-04 Android , AndroidDev , Animation

Google+

最近文章

  • Maven 介绍 Maven 是一个项目管理工具,主要用于项目构建,依赖管理,项目信息管理。自动化构建过程,从清理、编译、测试和生成报告、再到打包和部署。
  • IntelliJ IDEA 中使用 Resin 调试 平时开发环境使用的是 jetty,而 Java Web 有一个更好更快的服务器 Resin,这篇文章就来说一下什么是 Resin,以及在 Debug 中如何使用。
  • 使用Shell命令来对 Unix 时间戳和日期进行转换 date 命令 在程序中经常要使用到 Unix timestamp 和日期的转换,通常情况下都是Google一个时间戳转换的网页在进行转换,其实 Linux 命令中就有能够快速实现转换的命令。主要都是集中在 date 这个命令。date 命令主要用于显示或设定系统时间和日期。
  • Mastering the Vim 我已经用了很长一段时间 Vim 了,但是 Vim 最可贵之处便在于你永远达不到 Vim 的天花板,在使用的过程中我永远会发现操作 Vim 的其他便捷方法。最近看了一个关于 Vim 的讲座 ,革新我对 Vim 命令的认识。可以说掌握这样的一个世界观可以对 Vim 的操作上到另外一个层次。下面就总结一下这个视频中的精髓内容。
  • Spring MVC 应用处理 CORS 什么是跨域或者说什么是CORS(Cross-origin resource sharing),中文叫”跨域资源共享”。在了解 CORS 之前首先要知道“同源策略”,出于安全考虑,浏览器会限制Ajax中发起的跨站请求。比如,使用 XMLHttpRequest 对象发起 HTTP 请求就必须遵守同源策略(same-origin policy),”同源策略“是浏览器安全的基石。具体而言,Web 应用程序能且只能使用 XMLHttpRequest 对象向其加载的源域名发起 HTTP 请求,而不能向任何其它域名发起请求。阮一峰写的一篇关于 CORS 的文章 介绍得非常详细,这里主要记录一下重点以及 Spring MVC 中如何处理 CORS。