Oak_Zmm的技术博客

Cool Android Apis 整理(二)

Foreword

本文主要整理 Cool Android Apis
这是这个系列的第二篇,第一篇 Cool Android Apis 整理(一)

多说几句:
原本的想法是目前整理的知识点多分几篇来梳理,篇幅短些,这样大家看起来不累。但是也有弊端,就是不利于知识的整理。所以我对这些TIPS做了简单的筛选分类整理,在保证可读性的前提下,尽量的不割裂知识点间的联系。接下来会用两篇来总结完,请大家持续关注。如果有好的tip可以推荐给我。

整理来源

所以说严格来讲这篇文章基本不是原创,但是我对每个Tip都加入或官方文档或使用方法或效果之类的补充。整个来说算是 “把书读厚” 的过程吧。

Content

这个方法可以输出相应格式化的时间或者日期。这个方法提供足够多的格式化的形式,并且有一点:

date formats in these examples are shown using the US date format convention but that may change depending on the local settings

也就是这个输出的格式化的时间格式会根据本地语言设置来处理,但是中文的可能处理的并不能尽如人意,并不能像英文那样有很丰富的显示格式。 这里给大家看一些简单格式。

FORMAT_ABBREV_ALL 8月10日
FORMAT_SHOW_TIME: 下午10:19
FORMAT_SHOW_DATE: 8月10日
FORMAT_SHOW_YEAR: 2015年8月10日
FORMAT_SHOW_WEEKDAY: 星期一
FORMAT_NUMERIC_DATE: 8/10

但是我在看文档的过程中,很多格式在Google的文档中都提示过时了。并且推荐我们使用 SimpleDateFormat
这个类大家应该用的比较多,而且使用更方便。我个人也比较推荐大家使用这个。当然了,过时并不是不能使用。有些情况下还是可以用的。

Official Example:

 String[] formats = new String[] {
"yyyy-MM-dd",
"yyyy-MM-dd HH:mm",
"yyyy-MM-dd HH:mmZ",
"yyyy-MM-dd HH:mm:ss.SSSZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSZ",
};
for (String format : formats) {
SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.US);
System.out.format("%30s %s\n", format, sdf.format(new Date(0)));
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.format("%30s %s\n", format, sdf.format(new Date(0)));
}
//---------------result----------------------------------------//
yyyy-MM-dd 1969-12-31
yyyy-MM-dd 1970-01-01
yyyy-MM-dd HH:mm 1969-12-31 16:00
yyyy-MM-dd HH:mm 1970-01-01 00:00
yyyy-MM-dd HH:mmZ 1969-12-31 16:00-0800
yyyy-MM-dd HH:mmZ 1970-01-01 00:00+0000
yyyy-MM-dd HH:mm:ss.SSSZ 1969-12-31 16:00:00.000-0800
yyyy-MM-dd HH:mm:ss.SSSZ 1970-01-01 00:00:00.000+0000
yyyy-MM-dd'T'HH:mm:ss.SSSZ 1969-12-31T16:00:00.000-0800
yyyy-MM-dd'T'HH:mm:ss.SSSZ 1970-01-01T00:00:00.000+0000

部分语法对照,完整版可以参考Google文档 ,注意大小写。

字母 含义 示例 说明
y Year 1996;96 哪一年
M Month in year July;Jul;07 一年中的哪一月
m Minute in hour 30 一个小时中的第几分钟
w Week in year 27 一年中的第几个星期
W Week in month 2 一个月中的第几个星期
D Day in year 189 一年中的第几天
d Day in month 10 一个月中的第几天
H Hour in day (0-23) 0 一天中的第几个小时(24小时制)
h Hour in am/pm (1-12) 12 一天中上午、下午的第几个小时(12小时制)
S Millisecond 978 毫秒数
s Second in minute 55 一分钟的第几秒

Formats a content size to be in the form of bytes, kilobytes, megabytes, etc

这个方法会格式化数据的大小,根据输入的字节大小,返回 B KB MB GB 等等(最大支持到 PB)。当然要注意的是输入的最大值是 Long.MAX_VALUE. 以前不知道有这个方法 都是自己写的。。。摔!

注意: 这个类在 android.text.format 包下。

首先这个方法我们可以用来对sp dp 和 px 之间的单位转换。应该是有不少同学用过的。

我们来看下源码然后做下简单的分析:

/**
* Converts an unpacked complex data value holding a dimension to its final floating point value. The two parameters <var>unit</var> and <var>value</var>
* are as in {@link #TYPE_DIMENSION}.
*
* @param unit The unit to convert from.
* @param value The value to apply the unit to.
* @param metrics Current display metrics to use in the conversion --
* supplies display density and scaling information.
*
* @return The complex floating point value multiplied by the appropriate
* metrics depending on its unit.
*/

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;
}

Converts an unpacked complex data value holding a dimension to its final floating point value.

先看下说明,这句话是说 将一个对应的数值转换为屏幕上实际的点的值,屏幕上实际的点的位置,就是像素值。

再来看 输入的参数:
Unit
The unit to convert from. 也就是要转换的数值的单位 (这个地方不要搞错了 )
value
he value to apply the unit to. 也就是要转换的数值。
metrics:
Current display metrics to use in the conversion — supplies display density and scaling information.

DisplayMetrics 这个类提供一些和屏幕分辨率以及 显示的大小 或者字体大小之类的信息。这里不做深究(其实是因为看的并不很懂。。。捂脸)。但是还是推荐大家能去认真研究下这个类,因为这个类对于理解自定义View还是挺重要的。

我们可以看到下面关于 转换的方法。可以得到的是:
Sp -> px; dp -> px ;以及 pt、in、mm、等转换为px。

关于这些单位以及 density 和scaledDensity 详细的解析说明 可以参考这里 Android屏幕密度(Density)和分辨率概念详解 ;

scaledDensitydensity:
根据DisplayMetrics源码的说明,scaledDensitydensity, 一个是叫做显示密度,一个叫做缩放参数(和字体有关) 。而且对同一个设备来讲 density = scaledDensity = dpi/160f.
对于不同dpi的设备来讲:

0.75 on ldpi (120 dpi)
1.0 on mdpi (160 dpi; baseline)
1.5 on hdpi (240 dpi)
2.0 on xhdpi (320 dpi)
3.0 on xxhdpi (480 dpi)
4.0 on xxxhdpi (640 dpi)

这个类 可以用来存储存储一”组”数据。但不是key和value的关系。

Example:

Pair<String, String> pair = Pair.create("android开发", "自定view");
Log.d(TAG, pair.first); // android开发
Log.d(TAG, pair.second); // 自定view

至于怎么使用,暂时,我没有想到什么合适场景使用,我个人并没有在项目使用过这个类。但是pair中数据的类型可以随便玩,可以是基本数据类型,可以是集合,可以是Bean,可以是Context等等。。。也就是说它可能合适在很多地方使用。而且在某些情况下会有很好的效果。如果有那位用过或者对这个类有更多的理解,可以留言告知。

目前有很多地方从性能优化方说使用SparseArray来替换hashMap,来节省内存,提高性能。
Google 文档中是这么说的。

SparseArrays map integers to Objects. Unlike a normal array of Objects, there can be gaps in the indices. It is intended to be more memory efficient than using a HashMap to map Integers to Objects, both because it avoids auto-boxing keys and its data structure doesn’t rely on an extra entry object for each mapping.

Note that this container keeps its mappings in an array data structure, using a binary search to find keys. The implementation is not intended to be appropriate for data structures that may contain large numbers of items. It is generally slower than a traditional HashMap, since lookups require a binary search and adds and removes require inserting and deleting entries in the array. For containers holding up to hundreds of items, the performance difference is not significant, less than 50%.

按照官方文档的解释,因为SparseArray不需要对key和value进行auto-boxing(将原始类型封装为对象类型,比如把int类型封装成Integer类型),结构比HashMap简单(SparseArray内部主要使用两个一维数组来保存数据,一个用来存key,一个用来存value)不需要额外的额外的数据结构(主要是针对HashMap中的HashMapEntry而言的)。

但是,在大量数据的情况下,SparseArray 的查询速度要慢很多。可能是 SparseArray 查询方法的问题。但是在数据量不特别的大的情况下还是推荐使用SparseArray

在整理(一)中我们提到可以使用Html的方式给文本添加超链接。而这个类可以更方便的为文本添加超链接。
Example:

linkView0.setText("http://oakzmm.com");
Linkify.addLinks(linkView0, Linkify.WEB_URLS);
linkView0.setMovementMethod(LinkMovementMethod.getInstance());

超链接支持 WEBEMAILPHONEMAP 四种形式。

另外 Linkify 可以自定义约束和正则匹配 ,这是LInkify要比其他几种方式更加强大的地方。
详细可以参考这里:TextView 借助Linkify,使用自定义模式设置链接

说到这里就简单的说下实现超链接的几种方式 (可以匹配 WEBEMAILPHONEMAP):

0.Linkify.addLinks()

1.直接在xml中TextView下直接设置:

android:text="web:http://oakzmm.com \n tel:123457534 \n mail:macouen@gmail.com "

2.Html方式:

//-----1--Html

linkView1.setText(
Html.fromHtml(
"<b>fromHtml:</b> \t Click " +
"<a href=\"http:/oakzmm.com\">here</a> " +
"to visit my website "
)
);
linkView1.setMovementMethod(LinkMovementMethod.getInstance());

3.使用在TextView中使用< a > 标签,和上面的这种方式类似。

<string name="aText">
String_a标签: Click <a href="http://oakzmm.com">here</a> to visit my website</string>

4.使用强大的Spans来实现(下面会说到这个)。

SpannableString ss = new SpannableString("fromSpan:\t Click here to dial the phone.");
ss.setSpan(new StyleSpan(Typeface.BOLD), 0, 9,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ss.setSpan(new URLSpan("tel:4155551212"), 17, 21,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
linkView2.setText(ss);
linkView2.setMovementMethod(LinkMovementMethod.getInstance());

Span非常之强大,但是并没有得到充分的使用。
这里,不再做详细的说明和介绍,因为有人全面的总结过,这里给大家贴出文章连接。

我自己简单的写了部分效果的代码 , 包括上面的超链接的。

Example:

//-----0--autolink---
linkView0.setText("http://oakzmm.com");
Linkify.addLinks(linkView0, Linkify.WEB_URLS);
linkView0.setMovementMethod(LinkMovementMethod.getInstance());
//-----1--Html

linkView1.setText(
Html.fromHtml(
"<b>fromHtml:</b> \t Click " +
"<a href=\"http://macouen.github.io\">here</a> " +
"to visit my website "
)
);
linkView1.setMovementMethod(LinkMovementMethod.getInstance());
//-----2--span
/**
* Spanned.SPAN_EXCLUSIVE_EXCLUSIVE(前后都不包括) (a,b);
* Spanned.SPAN_INCLUSIVE_EXCLUSIVE(前面包括,后面不包括) [a,b);
* Spanned.SPAN_EXCLUSIVE_INCLUSIVE(前面不包括,后面包括) (a,b];
* Spanned.SPAN_INCLUSIVE_INCLUSIVE(前后都包括) [a,b];
*/

SpannableString ss = new SpannableString("fromSpan:\t Click here to dial the phone.");
ss.setSpan(new StyleSpan(Typeface.BOLD), 0, 9, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ss.setSpan(new URLSpan("tel:4155551212"), 17, 21, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
linkView2.setText(ss);
linkView2.setMovementMethod(LinkMovementMethod.getInstance());
//----3---标签<a>
linkView3.setMovementMethod(LinkMovementMethod.getInstance());
/**
* ------------------------------------------
* 1.字体样式(斜体,加粗)
* 2.字体大小(放大和拉伸),颜色(前景色和背景色)
* 3.下划线、删除线
* 4.上下标
* 5.超链接
* 6.TextView显示图片
* ------------------------------------------
*/

spanView = (TextView) findViewById(R.id.spanText1);
span = new SpannableString(
"字体:textVIEW\n" +
"样式:正常斜体加粗粗斜体\n" +
"大小:单位设置一半两倍横向拉伸\n" +
"颜色:前景色背景色\n" +
"下划线删除线\n" +
"上标x1下标x2\n" +
"超链接:电话 邮件 网站 短信 彩信 地图\n" +
"图片:abc"
);
//字体 android默认有 monospace normal sans serif
span.setSpan(new TypefaceSpan("monospace"), 3, 7, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new TypefaceSpan("serif"), 7, 11, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
//样式
span.setSpan(new StyleSpan(Typeface.NORMAL), 15, 17, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //正常
span.setSpan(new StyleSpan(Typeface.ITALIC), 17, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //斜体
span.setSpan(new StyleSpan(Typeface.BOLD), 19, 21, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //加粗
span.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 21, 24, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //粗斜体
//大小
span.setSpan(new AbsoluteSizeSpan(20), 28, 30, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new AbsoluteSizeSpan(20, true), 30, 32, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new RelativeSizeSpan(0.5f), 32, 34, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new RelativeSizeSpan(2.0f), 34, 36, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new ScaleXSpan(1.5f), 36, 40, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
//颜色
span.setSpan(new ForegroundColorSpan(Color.RED), 44, 47, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new BackgroundColorSpan(Color.CYAN), 47, 50, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
//线
span.setSpan(new UnderlineSpan(), 51, 54, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new StrikethroughSpan(), 54, 57, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
//上下标
span.setSpan(new SuperscriptSpan(), 61, 62, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new SubscriptSpan(), 65, 66, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
//连接
span.setSpan(new URLSpan("tel:4155551212"), 71, 73, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //电话
span.setSpan(new URLSpan("mailto:macouen@gmail.com"), 74, 76, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //邮件
span.setSpan(new URLSpan("http://macouen.github.io"), 77, 79, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //网络
span.setSpan(new URLSpan("sms:4155551212"), 80, 82, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //短信 使用sms:或者smsto:
span.setSpan(new URLSpan("mms:4155551212"), 83, 85, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //彩信 使用mms:或者mmsto:
span.setSpan(new URLSpan("geo:38.899533,-77.036476"), 86, 88, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //地图
//图片
span.setSpan(new ImageSpan(this, R.mipmap.ic_launcher), 92, 95, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spanView.setText(span);
spanView.setMovementMethod(LinkMovementMethod.getInstance());

效果图:Span

这个类主要是用来处理缩略图相关的,有过这方面需求的,应该是用过这个类的。而且这个类只有3个静态方法,很简单。至于缩略图是怎么生成的 以及源码的实现,有兴趣的可以去看下。

  • extractThumbnail (source, width, height)

    /** 
    *
    * 创建一个指定大小的缩略图
    * @param source 源文件(Bitmap类型)
    * @param width 压缩成的宽度
    * @param height 压缩成的高度
    */

    ThumbnailUtils.extractThumbnail(source, width, height);
  • extractThumbnail(source, width, height, options)

    /** 
    * 创建一个指定大小居中的缩略图
    *
    * @param source 源文件(Bitmap类型)
    * @param width 输出缩略图的宽度
    * @param height 输出缩略图的高度
    * @param options 如果options定义为OPTIONS_RECYCLE_INPUT,则回收@param source这个资源文件
    * (除非缩略图等于@param source)
    *
    */

    ThumbnailUtils.extractThumbnail(source, width, height, options);
  • createVideoThumbnail(filePath, kind)

    /** 
    * 创建一张视频的缩略图
    * 如果视频已损坏或者格式不支持可能返回null
    *
    * @param filePath 视频文件路径 如:/sdcard/android.3gp
    * @param kind kind可以为MINI_KIND或MICRO_KIND
    *
    */

    ThumbnailUtils.createVideoThumbnail(filePath, kind);
  • Bitmap.extractAlpha())

Returns a new bitmap that captures the alpha values of the original. This may be drawn with Canvas.drawBitmap(), where the color(s) will be taken from the paint that is passed to the draw call.

这句话正面翻译过来:
返回一个新的位图,该位图从源图中捕获了alpha值。这个方法可能跟Canvas.drawBitmap()一起被画,颜色值从传递过来的画笔中获取。

有的时候我们需要动态的修改一个元素的背景图片又不希望使用多张图片的时候,通过这个方法,结合Canvas和Paint可以动态的修改一个纯色Bitmap的颜色。也可以在某些情况下对图片处理后给轮廓添加光圈效果等。

单独这么说,可能很抽象,上个简单Demo大家看下就应该明白了。

.....
// 核心代码:

Canvas mCanvas = new Canvas(mAlphaBitmap);
Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
// 从原位图中提取只包含alpha的位图
Bitmap alphaBitmap = mBitmap.extractAlpha();
// 在画布上(mAlphaBitmap)绘制alpha位图
mCanvas.drawBitmap(alphaBitmap, 0, 0, mPaint);
.....

效果图:Bitmap

Summary

我们从第一篇开始大概是提到了这么几个类:

  • TextUtils
  • Html
  • PhoneNumberUtils
  • DateUtils
  • Formatter
  • Linkify
  • ThumbnailUtils
  • Bitmap
  • Spans

基本上这几个类都是提到了一个或者几个“比较巧妙”的方法。并不是说就那么几个方法是 VeryCool ,而其他的不是。在这些类中还是有好多方法需要花费更多的时间去了解和掌握。
尤其是像 Bitmap这样的,其中不乏我们经常使用而且很重要的方法,而且Bitmap 相关的,如,CanvasPaintGradientDrawable 等。尤其是CanvasclipRectclipPathclipRegion 这些十分重要的Api。

预告:第三篇,我准备整理一些很Cool的控件,以及控件的一些很棒的属性;Activity的方法。敬请期待。


待续。



声明

  1. 由于互联网数据的分享性,如果我发表的文章,来源于您的原创文章,且我没有注明,请微博私信或者邮件macouen@gmail.com说明。
  2. 欢迎转载,但请注明文章原始出处。

作者:Oak_Zmm
出处:http://oakzmm.com/


如果您觉得此博客对您有用,不妨请我喝杯咖啡。