@TryLoveCatch
2022-05-05T16:28:20.000000Z
字数 5855
阅读 2108
Android知识体系
在android的官方文档Supported Media Formats里面,介绍了多媒体的支持情况,而Android支持的图片格式如下图:
JPEG(Joint Photographic Experts Group(联合图像专家小组))是目前最常见的图片格式,它诞生于 1992 年,是一个很古老的格式。它只支持有损压缩,其压缩算法可以精确控制压缩比,以图像质量换得存储空间。由于它太过常见,以至于许多移动设备的 CPU 都支持针对它的硬编码与硬解码。
PNG,便携式网络图形(Portable Network Graphics,PNG)是一种无损压缩的位图图形格式,支持索引、灰度、RGB三种颜色方案以及Alpha通道等特性。
矢量图在很久很久以前就已经应用起来了,是一种基于xml的图像,因为图片不提供具体的像素,只提供的是绘图的指令,所以好处是占用内存非常小,性能高,可以任意缩放而不会失真,但是缺点也很明显,没有位图表达的色彩丰富。
然而现在app风格越来越扁平,拟物化已经成了过去,矢量图成了越来越多人的选择。
从Lollipop(Android5.0)开始,Android引入了对矢量图的支持,但并不支持svg这种矢量图片格式,而是以VectorDrawable的方式来实现矢量图的效果。
如何兼容之前的?
1、不需要添加任何依赖包, Gradle在编译时会根据Vectordrawable生成对应的位图资源。
2、Support Library 23.2+提供了兼容包,VectorDrawable可以被兼容到Android2.1, AnimateVectorDrawable可以被兼容到Android3.0。
方法1、打包的时候,会生成位图资源,低版本引用位图,5.0之上的继续引用VectorDrawable。我们可以在Gradle里面配置,具体生成的那种分辨率的位图资源:
defaultConfig {
vectorDrawables.generatedDensities = ['hdpi','xxhdpi']
}
方法2、不会生成位图,但是代码需要做一些调整。
1、修改appcompat-v7的版本
compile 'com.android.support:appcompat-v7:23.2.0'
2、关闭生成位图的配置:
gradle插件的版本为2.0以下:
android {
defaultConfig {
// 不让gradle自动生成不同屏幕分辨率的png图
generatedDensities = []
}
aaptOptions {
additionalParameters "--no-version-vectors"
}
}
gradle插件2.0+:
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
3、在引用Vectordrawable资源时使用app:srcCompat取代android:src
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_mood_black_24dp" />
4、在非src属性的地方使用矢量图时,就得用代码来使用,需要将矢量图用drawable容器(如StateListDrawable, InsetDrawable, LayerDrawable, LevelListDrawable, 和RotateDrawable)包裹起来使用。否则会在低版本的情况下报错。
如何编写VectorDrawable?
手动编写,是不可能,一般都应该使用矢量绘图软件生成SVG图片, 再通过一些工具将SVG转化为VectorDrawable。
VectorDrawable也是Drawable的一个直接子类,像其它Drawable那样通常情况下是在xml中定义, 它对应的xml标签是, 基本结构如下:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
</vector>
上面这个xml,对应的矢量图如下:
下面对上面的一些参数作一下解释:
1. vector是VectorDrawable对应的根标签
2. android:width与android:height对应矢量图的实际大小(矢量图是可以无限大, 但通常情况下一个图片都应该有一个原始大小, 假如你将此VectorDrawable作为一个ImageView的src, ImageView的大小都设置为wrap_content, 则ImageView对应的实际大小就是这里设置的大小)
3. android:viewportWidth与android:viewportHeight是指当前Drawable对应的虚拟Canvas的大小, 之所以说是虚拟的是因为实际上并不存在这样一个Canvas, 又之所以需要这个值是因为在标签中的路径数据要基于具体的坐标系来绘制.
4. 标签对应路径信息, 这里的path与我们自定义绘制图形时用的Path原理一样, 就是记录一些绘图操作, 具体对应其中的pathData.PathData中对应的路径描述符号不需要我们去记, 通常情况下由绘图软件生成svg图片再从svg文件中提取.
关于矢量绘图软件
1、Android Studio 内置的Vector Asset Studio
2、svg2android
还有一张方法,就是直接找svg图片,
阿里巴巴UX矢量库,
- 在这个网站,找到你需要的图标,并下载svg
- 在android中打开vector assert
- 点击Local SVG 选择路径,并命名drawable文件名字
- 就在drawable目录生成了xm
- ok, 一张可以任意缩放的图片有了
WebP 是 Google 在2010年发布的图片格式。它支持有损和无损压缩、支持完整的透明通道、也支持多帧动画,并且没有版权问题,是一种非常理想的图片格式。Android 4.0+默认支持WebP,Android 4.2.1(API 17)开始支持无损WebP和带alpha通道的WebP。
windows 下webP转换方法:
1. 工具下载: https://developers.google.com/speed/webp/
2. 需要 WebpCodecSetup.exe
,这是一个webp的图片查看插件,有了它才能查看webp格式的图片,libwebp-0.5.1-windows-x86.zip
,转换核心工具
3. 解压libwebp-0.5.1-windows-x86.zip
,只要使用cwebp.exe
4. cwebp D:\from\test.jpg -o D:\to\test.webp
即可实现转换
5. 可以写批处理程序,批量处理多个图片
Mac下webp转换方法:
1. brew install webp
2. cwebp [-preset <...>] [options] in_file [-o out_file]
, options参数列表中包含质量参数q,q为0~100之间的数字,比较典型的质量值大约为80。
3. 可以使用dwebp将WebP图片转换回PNG图片(默认),dwebp in_file [options] [-o out_file]
Mac下更多使用查看使用文档
其他工具:
1. http://zhitu.isux.us/
2. http://isparta.github.io/
3. https://cloudconvert.com/webp-to-anything
前面我们也说了,webp是4.0之后才支持,4.0之前如果要使用,下面的库就可以
webp-android-backport
webp-android,具体操作可以见 这里
但是这些库只是解决了从服务器获取的webp图片的解析,如果是资源文件,怎么办?
这里就用到了LayoutInflaterFactory
,具体可以参考这里和这里,基本代码如下:
public class MainActivity extends AppCompatActivity {
private static final int[] LL = new int[]
{ //
android.R.attr.src,//
};
@Override
protected void onCreate(Bundle savedInstanceState) {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN){
LayoutInflaterCompat.setFactory(LayoutInflater.from(this), new LayoutInflaterFactory() {
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
AppCompatDelegate delegate = getDelegate();
View view = delegate.createView(parent, name, context, attrs);
if (view instanceof ImageView) {
ImageView imageView = (ImageView) view;
TypedArray a = context.obtainStyledAttributes(attrs, LL);
int webpSourceResourceID = a.getResourceId(0, 0);
if (webpSourceResourceID == 0) {
return view;
}
InputStream rawImageStream = getResources().openRawResource(webpSourceResourceID);
byte[] data = streamToBytes(rawImageStream);
final Bitmap webpBitmap = WebPDecoder.getInstance().decodeWebP(data);
imageView.setImageBitmap(webpBitmap);
a.recycle();
}
return view;
}
});
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
大致逻辑为:对于4.2以下的版本,我们设置一个LayoutInflaterFactory,当创建ImageView的时候,因为AppCompatActivity,ImageView的创建是由上述代码中的delegate指向的对象完成的,我们通过传入attrs,取出用户声明的src属性,经过一系列操作转化为bitmap,最好设置到创建好的ImageView上。
这里还有一个问题,那就是,png图片是无需转换的,所以我们需要区分png和webp,那就根据后缀名来区分,代码如下:
TypedValue value = new TypedValue();
getResources().getValue(webpSourceResourceID, value, true);
String resname = value.string.toString().substring(13,
value.string.toString().length());
if (resname.endsWith(".webp")) {
// do
}