<span style="font-size:12px;"> <?xml version="1.0" encoding="utf-8"?>
<level-list
xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:drawable="@drawable/drawable_resource"
android:maxLevel="integer"
android:minLevel="integer" />
</level-list>
</span>
XML定义的Drawable的一种,以<level-list>作为根元素,其间可包含任意多个<item>节点,每一个<item>节点包含一个drawable对象和maxLevel与minLevel值,如:
<span style="font-size:12px;"><?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:minLevel="0" android:maxLevel="10" android:drawable="@drawable/b1" />
<item android:minLevel="11" android:maxLevel="20" android:drawable="@drawable/b2" />
<item android:minLevel="21" android:maxLevel="30" android:drawable="@drawable/b3" />
<item android:minLevel="31" android:maxLevel="40" android:drawable="@drawable/b4" />
</level-list></span>
当我们向LevelListDrawable对象提供一个Level值后,LevelListDrawable对象就会从前往后查看每一个<item>,当某个<item>节点的Level范围满足提供的那个Level值后,就会返回该<item>结点里指定的drawable。并且不会继续往后找。所以定义这个LevelListDrawable时要注意各个<item>的顺序。比如:
<span style="font-size:12px;"><?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:maxLevel="40" android:drawable="@drawable/b4" />
<item android:maxLevel="10" android:drawable="@drawable/b1" />
<item android:maxLevel="20" android:drawable="@drawable/b2" />
<item android:maxLevel="30" android:drawable="@drawable/b3" />
</level-list></span>
那么无论提供什么样的Level值,都不会返回后面三个<item>里的drawable(这里如果提供的Level值超过40,将返回一个空对象)。
可以通过Drawable对象的setLevel(int)方法来提供Level值。
比如当我们将一个LevelListDrawable作为一个View的background后,可以通过View的getBackground()方法获取这个Drawable对象,然后调用这个Drawable对象的setLevel()方法,提供不同的Level值,就可以改变View的背景。这个可以用来制作诸如进度条、音量调节等效果。
ImageView组件还提供了setImageLevel()方法来快捷设置android:src指定的LevelListDrawable的Level值(android:backgroudn指定的背景还是要通过View的形式来更改)。
下面是例子:
1、在XML文件中定义
level_list.xml:
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:maxLevel="1" android:drawable="@drawable/image1" />
<item android:maxLevel="2" android:drawable="@drawable/image2" />
<item android:maxLevel="3" android:drawable="@drawable/image3" />
<item android:maxLevel="4" android:drawable="@drawable/image4" />
<item android:maxLevel="5" android:drawable="@drawable/image5" />
</level-list>
在布局文件main.xml中:
<ImageView
android:id="@+id/imgView"
android:src="@drawable/level_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
2、在代码中使用:
因为我所使用的图片过大,会导致内存泄露,所以做了一些处理。
package com.example.drawabletest;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.LevelListDrawable;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends Activity {
private int[] ids = new int[] { R.drawable.image1, R.drawable.image2, R.drawable.image3,
R.drawable.image4, R.drawable.image5 };
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ImageView imageView = (ImageView) findViewById(R.id.imgView);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.image1, opts);
opts.inSampleSize = computeSampleSize(opts, -1, 500 * 500);
opts.inJustDecodeBounds = false;
opts.inInputShareable=true;
opts.inPurgeable=true;
LevelListDrawable levelListDrawable = new LevelListDrawable();//定义一个LevelDrawable
try {
for (int i = 0; i < ids.length; i++) {//for循环,加载5个drawable资源
Bitmap bmp = BitmapFactory.decodeResource(getResources(),ids[i], opts);
BitmapDrawable bitmapDrawable = new BitmapDrawable(bmp);
levelListDrawable.addLevel(i, i+1, bitmapDrawable);//添加到LevelListDrawable
}
imageView.setImageDrawable(levelListDrawable);//设置
} catch (OutOfMemoryError err) {
err.printStackTrace();
}
imageView.setImageLevel(1);//默认的level为0,将到设置为1
Button btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
int i = imageView.getDrawable().getLevel();
if (i >=5)
i = 0;
// imageView.setImageLevel(++i);//改变level
imageView.getDrawable().setLevel(++i); //能达到同样的效果
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
//这是android源码中智能计算opts.inSampleSize的方法
public static int computeSampleSize(BitmapFactory.Options options, int minSideLength,
int maxNumOfPixels) {
int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
int roundedSize;
if (initialSize <= 8) {
roundedSize = 1;
while (roundedSize < initialSize) {
roundedSize <<= 1;
}
} else {
roundedSize = (initialSize + 7) / 8 * 8;
}
return roundedSize;
}
private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength,
int maxNumOfPixels) {
double w = options.outWidth;
double h = options.outHeight;
int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h
/ maxNumOfPixels));
int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(
Math.floor(w / minSideLength), Math.floor(h / minSideLength));
if (upperBound < lowerBound) {
// return the larger one when there is no overlapping zone.
return lowerBound;
}
if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
return 1;
} else if (minSideLength == -1) {
return lowerBound;
} else {
return upperBound;
}
}
}
使用LevelDrawable注意几点:
1、默认的level为0,如果没有和0匹配的level,那么就不显示。
2、level匹配以maxLevel优先。即如果有个item,min:1,max:2。 另一份item,min:2,max:3。
如果此时设置level=2,那么会匹配第一个item。
解释上面防止图片内存溢出的代码:
首先我们把这个图片转成Bitmap,然后再利用Bitmap的getWidth()和getHeight()方法就可以取到图片的宽高了。
新问题又来了,在通过BitmapFactory.decodeFile(Stringpath)方法将突破转成Bitmap时,遇到大一些的图片,我们经常会遇到OOM(Out OfMemory)的问题。怎么避免它呢?
这就用到了我们上面提到的BitmapFactory.Options这个类。
BitmapFactory.Options这个类,有一个字段叫做
If set to true, the decoderwill return null (no bitmap), but theout…
也就是说,如果我们把它设为true,那么BitmapFactory.decodeFile(Stringpath, Optionsopt)并不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回来给你,这样就不会占用太多的内存,也就不会那么频繁的发生OOM了。
示例代码如下:
- BitmapFactory.Options options = newBitmapFactory.Options();
- options.inJustDecodeBounds = true;
- Bitmap bmp = BitmapFactory.decodeFile(path, options);
这段代码之后,options.outWidth 和 options.outHeight就是我们想要的宽和高了。
有了宽,高的信息,我们怎样在图片不变形的情况下获取到图片指定大小的缩略图呢?
比如我们需要在图片不变形的前提下得到宽度为200的缩略图。
那么我们需要先计算一下缩放之后,图片的高度是多少
- int height = options.outHeight * 200 / options.outWidth;
- options.outWidth = 200;
- options.outHeight = height;
- options.inJustDecodeBounds = false;
- Bitmap bmp = BitmapFactory.decodeFile(path, options);
- image.setImageBitmap(bmp);
这样虽然我们可以得到我们期望大小的ImageView
但是在执行BitmapFactory.decodeFile(path,options);时,并没有节约内存。要想节约内存,还需要用到BitmapFactory.Options这个类里的
我们可以根据图片实际的宽高和我们期望的宽高来计算得到这个值。
- inSampleSize = options.outWidth / 200;
options.inSampleSize = 10; //width,hight设为原来的十分一,图片大小为原来的1/100
另外,为了节约内存我们还可以使用下面的几个字段:
- options.inPreferredConfig =Bitmap.Config.ARGB_4444;
//默认是Bitmap.Config.ARGB_8888 - options.inPurgeable = true;
- options.inInputShareable = true;