Android 系统中,那贰个能急剧升高级工程师作成效的 API 汇总(持续立异中…)

by admin on 2019年9月8日

图片 1

转自:https://juejin.im/post/58c407ee44d90400698757d8

Android开发中小问题汇总(持续更新),此排序没有任何优先级或者重要程度。

此笔记只为记录平时开发中碰到的经常用到确不太注意的一些问题,每次用过就忘记,还要重新搜索解决方案,所以在此积累下平时开发中碰到的一些常用而又容易忘记的简单小bug。

  1. Android 如何让EditText不自动获取焦点。
    场景:有时候我们需要界面初始化时候并不希望EditText自动获取焦点,因为自动获取焦点会弹出软键盘,此种场景并不是我们所需要的,所以我们要设置让EditText不自动获取焦点
    解决方案:在EditText的父级控件中找一个,设置成如些:

android:focusable="true"     
android:focusableInTouchMode="true"

这样,就把EditText默认的行为拦截了。
或者采用下面这种方式:

EditText对象的clearFocus();
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 
imm.hideSoftInputFromWindow(editMsgView.getWindowToken(), 0); //关闭软键盘
  1. Android 禁止初始时ScrollView自动滚动到底部。
    场景:用ScrollView,加载数据时如果数据超过一屏的高度,有时会出现ScrollView自动滚动到底部,可能不是我们所需要的,我们可能需要的是从顶部开始显示。
    解决方案:在ScrollView子标签LinearLayout里面加上:

android:focusable="true"  
android:focusableInTouchMode="true"

如果出现某个控件抢占焦点造成的,可以禁止此控件的焦点。

  1. Android 软键盘弹出时把原来布局顶上去的解决方法。
    场景:键盘弹出时,会将布局底部的导航条顶上去。
    解决方案:在mainfest.xml中,在和导航栏相关的activity中添加如下代码:

 <activity
  android:name=".MainActivity"
android:windowSoftInputMode="adjustResize|stateHidden"
 />

扩展:

【A】stateUnspecified:软键盘的状态并没有指定,系统将选择一个合适的状态或依赖于主题的设置
【B】stateUnchanged:当这个activity出现时,软键盘将一直保持在上一个activity里的状态,无论是隐藏还是显示
【C】stateHidden:用户选择activity时,软键盘总是被隐藏
【D】stateAlwaysHidden:当该Activity主窗口获取焦点时,软键盘也总是被隐藏的
【E】stateVisible:软键盘通常是可见的
【F】stateAlwaysVisible:用户选择activity时,软键盘总是显示的状态
【G】adjustUnspecified:默认设置,通常由系统自行决定是隐藏还是显示
【H】adjustResize:该Activity总是调整屏幕的大小以便留出软键盘的空间
【I】adjustPan:当前窗口的内容将自动移动以便当前焦点从不被键盘覆盖和用户能总是看到输入内容的部分
  1. 在Android 6.0下继续使用HttpClient。
    HttpClient在Android
    6.0已经被Google废除了,如果想继续使用HttpClient的话,只需在主Module的gradle中配置:

android {  
    useLibrary ‘org.apache.http.legacy‘  
}  
  1. android 6.0以上运行时权限问题
  • 报错如下:

java.lang.RuntimeException: 
Unable to start activity ComponentInfo{com.liujc.supereader/com.liujc.supereader.ui.activity.ReadActivity}: java.lang.SecurityException: com.liujc.supereader was not granted  this permission: android.permission.WRITE_SETTINGS.
  • 解决方案:Android
    中有两个特殊权限,使用requestPermission方法是不成功的,需要特殊设置

    • SYSTEM_ALERT_WINDOW
    • WRITE_SETTINGS
      关于这两个权限,需要我们自己手动开启系统设置的Activity界面。后来打开系统设置页面,才有这个权限的设置,设置下就ok了。
      友好方式就是检测到该权限获取失败,提示用户跳转到设置界面设置:

      • 设置弹框权限

    //权限申请相关方法
private static final int REQUEST_CODE = 1;
private void requestAlertWindowPermission() {    
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);  
        intent.setData(Uri.parse("package:" + getPackageName())); 
        startActivityForResult(intent, REQUEST_CODE);             
}
//回调
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);    
    if (requestCode == REQUEST_CODE) {        
         if (Settings.canDrawOverlays(this)) {            
              Toast.makeText(this,"弹窗权限开启!",Toast.LENGTH_SHORT).show();            
              PrefUtils.setBoolean(MainActivity.this, "isAllowAlert", true);        
          }else {           
              PrefUtils.setBoolean(MainActivity.this, "isAllowAlert", false);        
             }    
       }
  }
  • 设置权限

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        // 判断是否有WRITE_SETTINGS权限
        if(!Settings.System.canWrite(this)) {
            // 申请WRITE_SETTINGS权限
            Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS,
                    Uri.parse("package:" + getPackageName()));
            startActivityForResult(intent, REQUEST_CODE);
        } else {
            dosomething();
        }
    } else {
        dosomething();
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intentdata) {
        if (requestCode == REQUEST_CODE) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                // 判断是否有WRITE_SETTINGS权限
                if (Settings.System.canWrite(this)) {
                    dosomething();
                }
            }
        }
        super.onActivityResult(requestCode, resultCode, data);
    }
  1. 在使用toolbar时报以下错误:
    This Activity already has an action bar supplied by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR
    主要问题出在我们配置的style中:
    错误写法:

<style name="AppTheme.NoActionBar">
        <item name="android:windowActionBar">false</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>

其中AppTheme使用的主题是AppCompat的主题,由于AppCompat主题下的windowActionBar和windowNoTitle的命名方式前都没有android字样,所以报错。
正确写法:

<style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>
  1. Android Studio导入项目报:错误: 非法字符: ‘\ufeff’。
    出现场景:
    复制的Eclipse项目的源文件粘贴到Android Studio上,在Android
    Studio上面编译运行报错:“错误: 非法字符: ‘\ufeff’”。
    原因:
    Eclipse可以自动把UTF-8+BOM文件转为普通的UTF-8文件,但AndroidStudio需要重新转一下

    解决办法:
    将编码格式UTF-8+BOM文件转为普通的UTF-8文件。

    • 简单方法,在AS右下角,将编码改为GBK,再转为UTF-8,可以解决。

      图片 2

      这里写图片描述

    • 用EditPlus
      将文件用EditPlus打开,然后选择Document(文件),再选择Convert
      Encoding(编码转换)成UTF-8即可。将修改过编码格式的类,拷到项目中,覆盖原先的类。

  2. Android
    Studio出现Error:No service of type Factory available in ProjectScopeServices.

    出现场景:
    当你从第三方download一个项目时,可能用Android
    Studio去打开想运行下看看demo的运行效果,结果很不爽,碰到个error:No service of type Factory available in ProjectScopeServices.
    解决办法:
    定位到根目录(也就是Project)下的build.gradle文件中的buildscript
    —> dependencies中添加
    classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'编译下应该就OK了。
    具体如下:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'
        classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' 
    }
}
  1. 使用Genymotion调试出现错误INSTALL_FAILED_CPU_ABI_INCOMPATIBLE
    出现场景:
    在Android模拟器上安装apk调试程序时,出现以下异常:

Installation failed with message INSTALL_FAILED_NO_MATCHING_ABIS. It is possible that this issue is resolved by uninstalling an existing version of the apk if it is present, and then re-installing.
WARNING: Uninstalling will remove the application data!
Do you want to uninstall the existing application?

调查原因:
由于程序中使用了native libraries(也就是.so文件) 。该native libraries
不支持当前的cpu的体系结构。
现在安卓模拟器的CPU/ABI一般有三种类型,INTEL X86,ARM,MIPS。
解决方案:
根据当前模拟器的cpu体系结构在程序目录中提供不同目录下的so文件,如果程序中只提供了ARM,出现INSTALL_FAILED_NO_MATCHING_ABIS的错误,那么久改用ARM,否则反之。再或者你全部添加,如下图:

图片 3

image.png

  1. Android 7.0调用系统相机拍照崩溃问题
    场景分析:
    之前也许我们认为Android
    6.0以后加了动态获取权限问题,我们加了对应权限问题然后调用下面代码就可以调用系统相机拍照。

File tempFile = new File(photoFile, pickPicName);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 // 从文件中创建uri
Uri uri = Uri.fromFile(tempFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(intent, CAMERA_REQUSTCODE);

上面代码是常见的打开系统相机拍照的代码,拍照成功后,照片会存储在tempFile文件中。这段代码在Android
7.0系统之前使用没有问题,可以正常调用系统相机拍照问题,但如果运行在7.0系统上就会崩溃,抛出android.os.FileUriExposedException异常。
调查原因:
Android 7.0系统强制启用了被称作
StrictMode的策略,禁止不安全路径被外部访问(禁止向您的应用外公开
file://URI),若要在应用间共享文件,您应发送一项
content://URI,并授予 URI
临时访问权限。进行此授权的最简单方式是使用
FileProvider类。如需了解有关权限和共享文件的详细信息,请参阅共享文件。
解决方案:
下面就针对7.0系统采用共享文件的形式:content://
URI去调用系统相机拍照,完整代码如下:

File tempFile = new File(photoFile, pickPicName);
        //获取系統版本
        int currentapiVersion = android.os.Build.VERSION.SDK_INT;
        // 激活相机
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (currentapiVersion < Build.VERSION_CODES.N) {
            // 从文件中创建uri
            Uri uri = Uri.fromFile(tempFile);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        } else {
            //兼容android7.0 使用共享文件的形式
            ContentValues contentValues = new ContentValues(1);
            contentValues.put(MediaStore.Images.Media.DATA, tempFile.getAbsolutePath());
            Uri uri = AssistantApplication.getContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        }
        startActivityForResult(intent, CAMERA_REQUSTCODE);
  1. List集合中contains方法总是返回false
    出现场景:
    在利用ArrayList类的caontains方法时,新建一个User类(一个Javabean),然后写了一个存放User类的ArrayList
    但在调用list.contains(user)时总是返回false。
    调查原因:
    查看ArrayList的源码,其contains方法源码如下:

/**
     * Searches this {@code ArrayList} for the specified object.
     *
     * @param object
     *            the object to search for.
     * @return {@code true} if {@code object} is an element of this
     *         {@code ArrayList}, {@code false} otherwise
     */
    @Override public boolean contains(Object object) {
        Object[] a = array;
        int s = size;
        if (object != null) {
            for (int i = 0; i < s; i++) {
                if (object.equals(a[i])) {
                    return true;
                }
            }
        } else {
            for (int i = 0; i < s; i++) {
                if (a[i] == null) {
                    return true;
                }
            }
        }
        return false;
    }

发现在contains方法会调用
object.equals(a[i])方法,其中a[i]是个Object类的实例。也就是说我在调用list.contains(user)时实际上比较的是user.equals(a[i])。也就是这里一直返回false。下面我们来通过拓展知识"=="和equals方法究竟有什么区别?来看一下具体什么原因导致的。
拓展知识:“==”和equals方法究竟有什么区别?

  • ==操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用==操作符
    如果一个变量指向的数据是对象类型的,那么,这时候涉及了两块内存,对象本身占用一块内
    存(堆内存),变量也占用一块内存,例如Objet obj = new
    Object();变量obj是一个内存,new
    Object()是另一个内存(堆内存),此时,变量obj所对应的内存中存储的数值就是对象占用的那块内存(堆内存)的首地址。对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,即要看这两个变量所对应的内存中的数值是否相等,这时候就需要用==操作符进行比较。
  • equals方法是用于比较两个独立对象的内容是否相同,就好比去比较两个人的长相是否相同,它比较的两个对象是独立的。例如,对于下面的代码:
    String a=new String(“foo”);
    String b=new String(“foo”);
    两条new语句创建了两个对象,然后用a/b这两个变量分别指向了其中一个对象,这是两个不同的对象,它们的首地址是不同的,即a和b中存储的数值(对应对象的首地址)是不相同的,所以,表达式a==b将返回false,而这两个对象中的内容是相同的,所以,表达式a.equals(b)将返回true。

如果一个类没有自己定义equals方法(就比如之前创建的User类),那么它将继承Object类的equals方法,Object类的equals方法的实现源代码如下:

public boolean equals(Object o) {
        return this == o;
    }

这说明,如果一个类没有自己定义equals方法,它默认的equals方法(从Object类继承的)就是使用==操作符,也是在比较两个变量指向的对象是否是同一对象,这时候使用equals和使用==会得到同样的结果,如果比较的是两个独立的对象则总返回false。
现在再回过头来看看user.equals(a[i])为什么一直返回false,因为这里的a[i]和user是两个独立的对象,并且User类也是继承Object类的equals方法,所以一直返回false。
解决方案:
如果希望能够比较该User类创建的两个实例对象的内容是否相同,那么必须在User类中覆盖equals方法(不再是继承Object类的equals方法)即可。代码如下:

public boolean equals(Object obj) { 
        if (obj instanceof User) {   
            User u = (User) obj;   
            return this.username.equals(u.username)   
                    && this.password.equals(password);   
        }   
        return super.equals(obj); 
}
  1. 在android 4.0.3上对sdcard不能创建目录或文件。
    出现场景:
    之前都在4.4以后运行的程序,发现可以在本地创建对应的数据库文件或者其他日志文件,然而运行到了Android
    4.0.3上却崩溃了,提示我文件目录找不到,也真是纳闷了,没办法碰到问题得解决啊。
    调查原因:
    刚开始以为自己权限没有申请,打开manifest发现权限已申请,如下:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

那肯定不是权限的问题了,所以那就是获取路径上面出现问题了。
所以这里就要看看/storage/sdcard/sdcard/mnt/sdcard
三者的区别了。

  • /sdcard是/mnt/sdcard的符号链,指向/mnt/sdcard,
  • /storage/sdcard 是sdcard的分区……
    /sdcard/:这是一个软连接,指向以下:

    • /mnt/sdcard (Android < 4.1)
    • /storage/sdcard0 (Android 4.1+)
      参考以下内容:

      • /storage/emulated/0/: to my knowledge, this refers to
        the “emulated MMC” (“owner part”). Usually this is the
        internal one. The “0” stands for the user here, “0” is the
        first user aka device-owner. If you create additional users,
        this number will increment for each.
      • /storage/emulated/legacy/ as before, but pointing to the
        part of the currently working user (for the owner, this
        would be a symlink to /storage/emulated/0/). So this path
        should bring every user to his “part”.
      • /sdcard/: According to a comment by
        Shywim,
        this is a symlink to…

        • /mnt/sdcard (Android < 4.1)
        • /storage/sdcard0 (Android 4.1+)
      • /storage/sdcard0/: As there’s no legacy pendant here
        (see comments below), the “0” in this case rather identifies
        the device (card) itself. One could, eventually, connect a
        card reader with another SDCard via OTG, which then would
        become /storage/sdcard1 (no proof for that, just a guess —
        but I’d say a good one)

解决方案:
将获取本地SD卡目录的代码改成以下:

public static String getSDPath() {
       boolean sdCardExist = Environment.getExternalStorageState().equals(
               Environment.MEDIA_MOUNTED);
       if (sdCardExist) {
           return Environment.getExternalStorageDirectory().toString();
       } else {
           if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1){
               File sdPath = new File("/mnt/sdcard2");
               if(sdPath != null && sdPath.exists()) {
                   return sdPath.getAbsolutePath();
               }
           }
           return "";
       }
   }

这里之所以指定目录为/mnt/sdcard2,是因为Android
4.0.3系统的手机在无本地内存卡是对应的本地目录为/mnt/sdcard2,而当插入内存卡时,对应的内存卡目录为/mnt/sdcard,而手机的本地目录依然是/mnt/sdcard2,所以这里硬编码写死了,如果有的手机还是没办法使用,请自行修改。

  1. databinding在android studio2.3版本后不再默认支持使用
    出现场景:
    由于项目中用到了databinding,在android studio
    2.2.3上没有任何问题,然而在android
    studio2.3版本后却不再默认支持使用,编译时会提示com.xxx.xxx.databinding不存在。
    解决方案:
    需要在项目的app目录下的build.gradle中的dependencies里面添加apt 'com.android.databinding:compiler:2.3.0',此时编译即可通过,可正常使用。
    注意:
    在部分手机上(如红米和小米)报错如下:Android app installation: Unknown failure (Failure - not installed for 0)
    解决方案:

For Redmi and Mi devices turn off MIUI Optimization and reboot your phone.
Settings > Additional Settings > Developer Options > MIUI Optimization

即:将红米和小米设备关闭MIUI优化功能然后重启手机,
设置->更多设置->开发者选项->启用MIUI优化(设置成关闭)

  1. Android Studio
    2.3版本出现警告:Warning:Using incompatible plugins for the annotation processing: android-apt. This may result in an unexpected behavior.

    Android Studio Warning: Using incompatible plugins for the
    annotation
    processing

Q1:Error:(93, 12) 错误: 需要常量表达式

问题描述:这个问题是在添加一个module到项目中时遇到的,主要原因是因为原来module中的R文件是不会以final形式存在的,但是在module中的一些代码里对R中的数据使用了switch,而switch必须使用final常量:

图片 4

解决方案:将switch改为if/else就可以了。

这里就是所有的Android
API,包含所有的API类:

“条条大路通罗马。”工作中,实现某个需求的方式往往不是唯一的,这些不同实现方式不仅表现在代码质量上,还影响着我们的工作效率。就像,在
Android 系统中,总有那么一些鲜为人知的 API
能够减少我们很多零碎的工作量。于是,就想凭着一些经验,整理一些常用的,找个地方归纳总结,也供日后翻阅。

“条条大路通罗马。”工作中,实现某个需求的方式往往不是唯一的,这些不同实现方式不仅表现在代码质量上,还影响着我们的工作效率。就像,在
Android 系统中,总有那么一些鲜为人知的 API
能够减少我们很多零碎的工作量。于是,就想凭着一些经验,整理一些常用的,找个地方归纳总结,也供日后翻阅。

软件使用链接:

  • 1.EditPlus注册码在线生成
  • 2.IDEA激活码在线生成
  • 3.抓包工具Charles的使用
    下载链接
    http://charles.iiilab.com/
    如何截取分析 Https 协议相关的内容:
  1. 点击 Charles 的顶部菜单,选择 “Help” –> “SSL Proxying” –>
    “Install Charles Root Certificate”.
![](https://upload-images.jianshu.io/upload_images/3678546-bbf8e2ebaeaa8278.png)

1.png
  1. 用手机在浏览器中打开http://charlesproxy.com/getssl
    下载及安装证书。此时可以拦截http请求了,不过如果拦截https请求时,会出现乱码,那就接着往下看。
  2. 即使是安装完证书之后,Charles 默认也并不截取 Https
    网络通讯的信息,如果你想对截取某个网站上的所有 Https
    网络请求,可以在该请求上右击,选择Enable SSL
    Proxying,此时即可拦截手机的https请求数据。

Q2:Error:Execution failed for task ‘:app:transformClassesWithDexForDebug’. > com.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536

解决方案:在主build.gradle中添加以下语句:

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.2"
    defaultConfig {
        applicationId "com.xxx.xxx"
        minSdkVersion 16
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
        multiDexEnabled true
    }

根据资源名称获取资源
id。正常情况下,我们会在代码中直接根据资源ID获取资源,比如:

getResources().getIdentifier(String name, String defType, String defPackage)

根据资源名称获取资源
id。正常情况下,我们会在代码中直接根据资源ID获取资源,比如:

getResources().getDrawable(R.drawable.ic_launcher);

但有时候,资源并不是固定的,需要根据使用情况从多个同类资源中动态选择,比如根据服务器传递给客户端的接口数据动态设置,怎么办呢?设置一个硬编码的映射关系数组?太繁琐!不妨用上这个方法。

一个完整的资源名为package:type/entry,对应该方法的三个参数:资源名称、资源类型、应用包名。举几个例子:

imageView.setImageResource(getResources().getIdentifier(“ic_launcher”,
“drawable”, getPackageName()));
radioGroup.check(getResources().getIdentifier(“rb_indicator_” +
position, “id”, getPackageName()));

实际使用过程中,第一个参数
name,也就是资源名称,根据需要动态设置,这就需要多个资源在命名时保持一定的规范格式。另外,需要注意的是,直接通过
id
获取资源比通过名字的方式效率更高,所以,如果没有这般特殊需求的话,不提倡使用这个方法获取资源。

Q3:图片 5

解决方案:

 

getResources().getDrawable(R.drawable.ic_launcher);

TextUtils.isEmpty(CharSequence str)

使用频率超高的字符串判空方法,返回一个 boolean
值,内部实现的判断条件为:str == null || str.length() ==
0。备受开发人员喜爱的一个 if 字符串判断,系统已经帮我们封装过。

Q4:在主module和其他module中都有继承Application的子类,并且注册了,出现冲突

解决方案:

让主module的Application继承子module的Application(虽然依然不是最好的方案,但暂时也只能这样了)

 

但有时候,资源并不是固定的,需要根据使用情况从多个同类资源中动态选择,比如根据服务器传递给客户端的接口数据动态设置,怎么办呢?设置一个硬编码的映射关系数组?太繁琐!不妨用上这个方法。

Html.fromHtml()

解析 Html 格式的富文本内容,并返回一个带样式的字符串,供 TextView
等控件显示。可以解决一些含超链接、图文混排等格式的富文本内容的显示问题。

Q5:

一个完整的资源名为package:type/entry,对应该方法的三个参数:资源名称、资源类型、应用包名。举几个例子:

DateUtils.formatDateTime()

利用 Android SDK 提供的这个日期工具类可以将 long
类型的毫秒级时间数据格式化成特定显示格式的字符串。通常我们使用 Java SDK
中的 SimpleDateFormat 格式化日期数据,比如 new
SimpleDateFormat(“yyyy-MM-dd HH:mm”).format(),DateUtils
的作用就是替我们封装了这个过程。格式化结果与当前设备的本地语言环境有关。这里列举几个常用
format 格式(中文环境下):

  • FORMAT_SHOW_DATE:3月3日
  • FORMAT_SHOW_TIME:10:37
  • FORMAT_SHOW_WEEKDAY:星期五
  • FORMAT_SHOW_YEAR:2017年3月3日
  • FORMAT_NUMERIC_DATE:3/3
  • FORMAT_NO_MONTH_DAY:三月

图片 6

解决方案:

在对应module的gradle文件中的dependencies中加上这句:

    

testCompile 'org.json:json:20140107'

 

imageView.setImageResource(getResources().getIdentifier("ic_launcher", "drawable", getPackageName;radioGroup.check(getResources().getIdentifier("rb_indicator_" + position, "id", getPackageName;

Formatter.formatFileSize(Context context, long sizeBytes)

格式化文件大小,将字节数据格式化为 B、KB、M 等单位的相应数据。context
参数用于判断返回结果的字符串顺序,right-to-left 还是 left-to-right
形式的。这个工具类免去我们自己转化计算的过程,非常方便,特别适用于应用内文件下载的类似场景。

实际使用过程中,第一个参数
name,也就是资源名称,根据需要动态设置,这就需要多个资源在命名时保持一定的规范格式。另外,需要注意的是,直接通过
id
获取资源比通过名字的方式效率更高,所以,如果没有这般特殊需求的话,不提倡使用这个方法获取资源。

TypedValue.applyDimension(int unit, float value, DisplayMetrics metrics)

将指定单位的尺寸数据按照当前设备屏幕信息转化为相应的像素值。其中,TypedValue
为第一个参数提供了常用的单位值,比如:

  • COMPLEX_UNIT_PX
  • COMPLEX_UNIT_DIP
  • COMPLEX_UNIT_PT
  • COMPLEX_UNIT_SP

源码如下:

public static float applyDimension(int unit, float value, DisplayMetrics metrics){
    switch (unit) {
    case COMPLEX_UNIT_PX:
        return value;
    case COMPLEX_UNIT_DIP:
        return value * metrics.density;
    case COMPLEX_UNIT_SP:
        return value * metrics.scaledDensity;
    case COMPLEX_UNIT_PT:
        return value * metrics.xdpi * (1.0f/72);
    case COMPLEX_UNIT_IN:
        return value * metrics.xdpi;
    case COMPLEX_UNIT_MM:
        return value * metrics.xdpi * (1.0f/25.4f);
    }
    return 0;
}

其实在实际使用过程中,像素值都是 int 整数类型,而该方法返回的是 float
类型,如果直接强制转换的话,会自动舍去小数部分。所以,如果不用该方法的话,通常我们会这么转换:

public static int convertDipToPx(Context context, int dip) {
    float scale = context.getResources().getDisplayMetrics().density;
    // 0.5f 用于向上取整
    return (int) (dip * scale + 0.5f * (dip >= 0 ? 1 : -1));
}

使用频率超高的字符串判空方法,返回一个 boolean
值,内部实现的判断条件为:str == null || str.length() == 0。备受开发人员喜爱的一个
if 字符串判断,系统已经帮我们封装过。

Space

官方注释如下:

Space is a lightweight View subclass that may be used to create gaps
between components in general purpose layouts.

Space 是一个用于创建视图之间空隙的轻量级 View。在 onDraw()
方法中不执行任何绘制,所以 android:background
属性对他来说不起作用。通常我们使用 View
创建视图间的空隙,在不考虑背景色的情况下,Space
其实效率更高。注意,由于是 API 14
引入的控件,如果需要向前兼容的话,需要使用到 support v4 包。

解析 Html 格式的富文本内容,并返回一个带样式的字符串,供 TextView
等控件显示。可以解决一些含超链接、图文混排等格式的富文本内容的显示问题。

view.performClick()

自动调用 View
点击事件。通常按钮等控件只有在用户点击时才能触发其点击事件,该方法可以由某些特殊条件触发模拟用户点击行为。类似的还有
performLongClick() 方法。

利用 Android SDK 提供的这个日期工具类可以将 long
类型的毫秒级时间数据格式化成特定显示格式的字符串。通常我们使用 Java SDK
中的 SimpleDateFormat 格式化日期数据,比如
new SimpleDateFormat("yyyy-MM-dd HH:mm").format()DateUtils
的作用就是替我们封装了这个过程。格式化结果与当前设备的本地语言环境有关。这里列举几个常用
format 格式:

Log.getStackTraceString(Throwable tr)

Log 类提供的一个公共静态方法,与常见的 Log.i() 等方法打印日志到 logcat
控制台不同的是,该方法从 Throwable
对象中获取错误信息,并以字符串的形式返回。当你需要做错误信息的数据持久化,比如保存至本地存储卡中或者上传至服务器时,利用这个方法就非常方便。

  • FORMAT_SHOW_DATE:3月3日
  • FORMAT_SHOW_TIME:10:37
  • FORMAT_SHOW_WEEKDAY:星期五
  • FORMAT_SHOW_YEAR:2017年3月3日
  • FORMAT_NUMERIC_DATE:3/3
  • FORMAT_NO_MONTH_DAY:三月

Linkify.addLinks()

我们知道对于 TextView 文本控件中的内容,通过 android:autoLink
属性可以为其添加诸如 web、phone
等固定模版的超链接点击事件。但毕竟系统模版有限,而利用
Linkify.addLinks() 方法可以添加一些应用内自定义模版,比如新浪微博中的
“@XXX” 格式的超链接跳转等,都可以通过自定义正则表达式来匹配处理。

格式化文件大小,将字节数据格式化为 B、KB、M 等单位的相应数据。context
参数用于判断返回结果的字符串顺序,right-to-left 还是 left-to-right
形式的。这个工具类免去我们自己转化计算的过程,非常方便,特别适用于应用内文件下载的类似场景。

getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE)

设置安全窗口,禁用系统截屏。防止 App
中的一些界面被截屏,并显示在其他设备中造成信息泄漏。(常见手机设备系统截屏操作方式为:同时按下电源键和音量键。)

比如支付宝 App
的“向商家付款”的包含付款二维码的界面。(补充说明一点,微信付款界面不是这么做的,采用的是在
onResume()
生命周期方法中实时刷新付款二维码,与支付宝在安全方法采取的手段不同。)

将指定单位的尺寸数据按照当前设备屏幕信息转化为相应的像素值。其中,TypedValue
为第一个参数提供了常用的单位值,比如:

拦截 Back 键,使 App 进入后台而不是关闭

  @Override
  public void onBackPressed() {
    Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
    launcherIntent.addCategory(Intent.CATEGORY_HOME);
    startActivity(launcherIntent);
  }

使用 Back 键返回桌面,但不关闭当前应用,而是使之进入后台,就像按下 Home
键一样。

这个技巧厉害了。通常为了防止出现用户误按 Back 键退出 App
的情况,我们会在应用首页的 Activity 中监听返回键操作,使用 Toast
弱提示甚至 Dialog
强提示的方式给到用户一个再次确认的操作,但无法阻止用户通过返回键逐步关闭应用。

然而,如果用这个方法拦截 App 最后一个
Activity(常见为首页界面),既没有阻碍用户操作(回到桌面),又没有关闭掉我们的应用(后台运行中),间接提高
App 的存活时间,真乃暗度陈仓。并且据我实验,微信、支付宝、微博等 App
都是这么做的,大家不妨一试。

  • COMPLEX_UNIT_PX
  • COMPLEX_UNIT_DIP
  • COMPLEX_UNIT_PT
  • COMPLEX_UNIT_SP

ThumbnailUtils

缩略图工具类,可以根据本地视频文件源、Bitmap
对象生成缩略图,常用的公共静态方法为:

  • createVideoThumbnail(String filePath, int kind)
  • extractThumbnail(Bitmap source, int width, int height)

源码如下:

bitmap.extractAlpha()

从源 bitmap 中根据 alpha 获取一个新的 bitmap 对象。比较绕口,通常 App
中的 Icon
多数是纯色透明像素背景组成,利用这个方法可以对该图的非透明区域着色,有多种使用场景,常见如
Button 的 pressed 状态,View 的阴影状态等。举个例子:

private static Bitmap getDropShadow(ImageView iv, Bitmap src, float radius, int color) {

    final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    paint.setColor(color);

    final int width = src.getWidth(), height = src.getHeight();
    final Bitmap dest = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    final Canvas canvas = new Canvas(dest);
    final Bitmap alpha = src.extractAlpha();
    canvas.drawBitmap(alpha, 0, 0, paint);

    final BlurMaskFilter filter = new BlurMaskFilter(radius, BlurMaskFilter.Blur.OUTER);
    paint.setMaskFilter(filter);
    canvas.drawBitmap(alpha, 0, 0, paint);
    iv.setImageBitmap(dest);

    return dest;
}
public static float applyDimension(int unit, float value, DisplayMetrics metrics){ switch  { case COMPLEX_UNIT_PX: return value; case COMPLEX_UNIT_DIP: return value * metrics.density; case COMPLEX_UNIT_SP: return value * metrics.scaledDensity; case COMPLEX_UNIT_PT: return value * metrics.xdpi * ; case COMPLEX_UNIT_IN: return value * metrics.xdpi; case COMPLEX_UNIT_MM: return value * metrics.xdpi * (1.0f/25.4f); } return 0;}

ArgbEvaluator

系统提供的一个 TypeEvaluator
,我们只需要提供两个起始颜色值和一个分值,系统会通过特定的算法计算得出一个新的颜色中间值。利用这个类,我们至少可以做两件事情。

第一,用于属性动画中。由于其实现了 TypeEvaluator
接口,可以用来做自定义属性动画的求值器,改变 View 的显示状态。比如:

int colorStart = ContextCompat.getColor(this, R.color.black);
int colorEnd = ContextCompat.getColor(this, R.color.green);
ValueAnimator valueAnimator = ValueAnimator
    .ofObject(new ArgbEvaluator(), colorStart, colorEnd)
    .setDuration(3000);
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        textView.setTextColor((Integer) animation.getAnimatedValue());
    }
});
valueAnimator.start();

第二,利用该类提供的颜色求值算法,配合 ViewPager
提供的滑动偏离值使用。这种场景常见于使用 ViewPager
实现的引导页,其背景色随着滑动距离动态改变;使用 ViewPager 实现的 Tab
样式菜单页面,Tab
中文本内容随着滑动距离动态改变字体颜色(可以参考安卓版微信)。这两种使用都使得
ViewPager 页面切换过渡得很自然,体验极佳。如:

viewPager.addOnPageChangeListener(new OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        new ArgbEvaluator().evaluate(positionOffset, startColor, endColor);
    }

    @Override
    public void onPageSelected(int position) {

    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
});

另外,关于颜色差值的计算,Google Sample 里有另一种算法,可参考
SlidingTabStrip.java
文件源码,核心方法内容如下:

/**
* Blend {@code color1} and {@code color2} using the given ratio.
*
* @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
*              0.0 will return {@code color2}.
*/
private static int blendColors(int color1, int color2, float ratio) {
    final float inverseRation = 1f - ratio;
    float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
    float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
    float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
    return Color.rgb((int) r, (int) g, (int) b);
}

其实在实际使用过程中,像素值都是 int 整数类型,而该方法返回的是 float
类型,如果直接强制转换的话,会自动舍去小数部分。所以,如果不用该方法的话,通常我们会这么转换:

android:weightSum

用于 LinearLayout 中,用于设置 Children weight 的总比重。在 LinearLayout
的 children 中,我们经常会使用 android:layout_weight
按比例分配容器布局的空间,但有时候不一定会分完。以往,有些朋友可能会使用一个空
放在最后来达到末尾占位效果。如果你知道这个属性的话,就能少写一些代码。

public static int convertDipToPx(Context context, int dip) { float scale = context.getResources().getDisplayMetrics().density; // 0.5f 用于向上取整 return  (dip * scale + 0.5f * (dip >= 0 ? 1 : -1));}

android:descendantFocusability

用于 ViewGroup 中,解决作为 Parent 的 ViewGroup 与 Children View
之间的焦点占用问题。最最常见的使用场景就是 list item
中含有一些点击效果的控件,比如 Button、CheckBox
等,相信大家都遇到过。取值有三种,含义就不用再多说了:

  • afterDescendants
  • beforeDescendants
  • blocksDescendants

官方注释如下:

android:duplicateParentState

是否将 View 自身的 drawable state 交给直接 parent ViewGroup 控制,值为
boolean 类型。比如有一个 item 布局,item 中有一个 button,如果点击 item
layout 时,需要 button 呈现对应的点击效果,就可以在 button
中用到这个属性。不过,从设计的角度来讲,这种场景还是比较少见的。知道有这个属性就好,不推荐这种交互设计。

Space is a lightweight View subclass that may be used to create gaps
between components in general purpose layouts.

android:fillViewport

ScrollView
的一个属性,用于设置内容部分是否填满屏幕,主要针对内容不足以填满屏幕的情况。这里推荐一个使用技巧,参考我之前写的文章:Android
日常开发中,两个非常实用的布局技巧。

Space 是一个用于创建视图之间空隙的轻量级 View。在 onDraw()
方法中不执行任何绘制,所以 android:background
属性对他来说不起作用。通常我们使用 View
创建视图间的空隙,在不考虑背景色的情况下,Space
其实效率更高。注意,由于是 API 14
引入的控件,如果需要向前兼容的话,需要使用到 support v4 包。

android:adjustViewBounds

使用 ImageView 时,你可能会用 android:scaleType
属性设置图片缩放方式。殊不知,android:adjustViewBounds
属性也能起到类似的效果。但要注意的是,后者需要至少指定 ImageView
宽高中的一个属性,或者 maxHeight
之类的,然后另一个属性随之适配。这个属性用在列表中较为合适,比如 App
中的活动列表页面,图片宽度设置为 match_parent,然后高度设为
wrap_content
使其自适应,这样便能保证从服务获取的高分辨率图片在不同的屏幕中不被拉伸变形。(备注:最好在项目资源文件中放置一个与网络图片相同尺寸的默认图,起到
placeholder 作用,避免图片显示前高度为 0 的较差体验。)

自动调用 View
点击事件。通常按钮等控件只有在用户点击时才能触发其点击事件,该方法可以由某些特殊条件触发模拟用户点击行为。类似的还有
performLongClick() 方法。

持续更新中…

本文先记这么多吧,日积月累,后续持续更新中,敬请关注…(当然,你有什么高效而又少见的
API,欢迎一起分享交流哦~)

Log 类提供的一个公共静态方法,与常见的 Log.i() 等方法打印日志到
logcat 控制台不同的是,该方法从 Throwable
对象中获取错误信息,并以字符串的形式返回。当你需要做错误信息的数据持久化,比如保存至本地存储卡中或者上传至服务器时,利用这个方法就非常方便。

我们知道对于 TextView 文本控件中的内容,通过 android:autoLink
属性可以为其添加诸如 web、phone
等固定模版的超链接点击事件。但毕竟系统模版有限,而利用
Linkify.addLinks() 方法可以添加一些应用内自定义模版,比如新浪微博中的
“@XXX” 格式的超链接跳转等,都可以通过自定义正则表达式来匹配处理。

设置安全窗口,禁用系统截屏。防止 App
中的一些界面被截屏,并显示在其他设备中造成信息泄漏。(常见手机设备系统截屏操作方式为:同时按下电源键和音量键。)

比如支付宝 App
的“向商家付款”的包含付款二维码的界面。(补充说明一点,微信付款界面不是这么做的,采用的是在
onResume()
生命周期方法中实时刷新付款二维码,与支付宝在安全方法采取的手段不同。)

 @Override public void onBackPressed() { Intent launcherIntent = new Intent(Intent.ACTION_MAIN); launcherIntent.addCategory(Intent.CATEGORY_HOME); startActivity(launcherIntent); }

使用 Back 键返回桌面,但不关闭当前应用,而是使之进入后台,就像按下 Home
键一样。

这个技巧厉害了。通常为了防止出现用户误按 Back 键退出 App
的情况,我们会在应用首页的 Activity 中监听返回键操作,使用 Toast
弱提示甚至 Dialog
强提示的方式给到用户一个再次确认的操作,但无法阻止用户通过返回键逐步关闭应用。

然而,如果用这个方法拦截 App 最后一个
Activity,既没有阻碍用户操作,又没有关闭掉我们的应用,间接提高 App
的存活时间,真乃暗度陈仓。并且据我实验,微信、支付宝、微博等 App
都是这么做的,大家不妨一试。

缩略图工具类,可以根据本地视频文件源、Bitmap
对象生成缩略图,常用的公共静态方法为:

  • createVideoThumbnail(String filePath, int kind)
  • extractThumbnail(Bitmap source, int width, int height)

从源 bitmap 中根据 alpha 获取一个新的 bitmap 对象。比较绕口,通常 App
中的 Icon
多数是纯色透明像素背景组成,利用这个方法可以对该图的非透明区域着色,有多种使用场景,常见如
Button 的 pressed 状态,View 的阴影状态等。举个例子:

private static Bitmap getDropShadow(ImageView iv, Bitmap src, float radius, int color) { final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor; final int width = src.getWidth(), height = src.getHeight(); final Bitmap dest = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); final Canvas canvas = new Canvas; final Bitmap alpha = src.extractAlpha(); canvas.drawBitmap(alpha, 0, 0, paint); final BlurMaskFilter filter = new BlurMaskFilter(radius, BlurMaskFilter.Blur.OUTER); paint.setMaskFilter; canvas.drawBitmap(alpha, 0, 0, paint); iv.setImageBitmap; return dest;}

系统提供的一个 TypeEvaluator
,我们只需要提供两个起始颜色值和一个分值,系统会通过特定的算法计算得出一个新的颜色中间值。利用这个类,我们至少可以做两件事情。

第一,用于属性动画中。由于其实现了 TypeEvaluator
接口,可以用来做自定义属性动画的求值器,改变 View 的显示状态。比如:

int colorStart = ContextCompat.getColor(this, R.color.black);int colorEnd = ContextCompat.getColor(this, R.color.green);ValueAnimator valueAnimator = ValueAnimator .ofObject(new ArgbEvaluator(), colorStart, colorEnd) .setDuration;valueAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { textView.setTextColor animation.getAnimatedValue;valueAnimator.start();

第二,利用该类提供的颜色求值算法,配合 ViewPager
提供的滑动偏离值使用。这种场景常见于使用 ViewPager
实现的引导页,其背景色随着滑动距离动态改变;使用 ViewPager 实现的 Tab
样式菜单页面,Tab
中文本内容随着滑动距离动态改变字体颜色(可以参考安卓版微信)。这两种使用都使得
ViewPager 页面切换过渡得很自然,体验极佳。如:

viewPager.addOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { new ArgbEvaluator().evaluate(positionOffset, startColor, endColor); } @Override public void onPageSelected(int position) { } @Override public void onPageScrollStateChanged(int state) { }});

另外,关于颜色差值的计算,Google Sample 里有另一种算法,可参考
SlidingTabStrip.java 文件源码,核心方法内容如下:

 /*** Blend {@code color1} and {@code color2} using the given ratio.** @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,* 0.0 will return {@code color2}.*/private static int blendColors(int color1, int color2, float ratio) { final float inverseRation = 1f - ratio; float r = (Color.red * ratio) + (Color.red * inverseRation); float g = (Color.green * ratio) + (Color.green * inverseRation); float b = (Color.blue * ratio) + (Color.blue * inverseRation); return Color.rgb r,  g, ;}

用于 LinearLayout 中,用于设置 Children weight 的总比重。在
LinearLayout 的 children 中,我们经常会使用 android:layout_weight
按比例分配容器布局的空间,但有时候不一定会分完。以往,有些朋友可能会使用一个空
<View>
放在最后来达到末尾占位效果。如果你知道这个属性的话,就能少写一些代码。

用于 ViewGroup 中,解决作为 Parent 的 ViewGroup 与 Children View
之间的焦点占用问题。最最常见的使用场景就是 list item
中含有一些点击效果的控件,比如 Button、CheckBox
等,相信大家都遇到过。取值有三种,含义就不用再多说了:

  • afterDescendants
  • beforeDescendants
  • blocksDescendants

是否将 View 自身的 drawable state 交给直接 parent ViewGroup 控制,值为
boolean 类型。比如有一个 item 布局,item 中有一个 button,如果点击 item
layout 时,需要 button 呈现对应的点击效果,就可以在 button
中用到这个属性。不过,从设计的角度来讲,这种场景还是比较少见的。知道有这个属性就好,不推荐这种交互设计。

ScrollView
的一个属性,用于设置内容部分是否填满屏幕,主要针对内容不足以填满屏幕的情况。这里推荐一个使用技巧,参考我之前写的文章:Android
日常开发中,两个非常实用的布局技巧。

使用 ImageView 时,你可能会用 android:scaleType
属性设置图片缩放方式。殊不知,android:adjustViewBounds
属性也能起到类似的效果。但要注意的是,后者需要至少指定 ImageView
宽高中的一个属性,或者 maxHeight
之类的,然后另一个属性随之适配。这个属性用在列表中较为合适,比如 App
中的活动列表页面,图片宽度设置为 match_parent,然后高度设为
wrap_content
使其自适应,这样便能保证从服务获取的高分辨率图片在不同的屏幕中不被拉伸变形。(备注:最好在项目资源文件中放置一个与网络图片相同尺寸的默认图,起到
placeholder 作用,避免图片显示前高度为 0 的较差体验。)

本文先记这么多吧,日积月累,后续持续更新中,敬请关注…

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图