(翻译) Android SDK文档之Drawable Mutations

原文来自Android SDK文档中的 resources/articles/drawable-mutations.html

       

       Android的drawables非常便于构建应用。Drawable是一个可插拨的绘制容器(drawing container), 通常与一个View关联。 比如, BitmapDrawable用于显示图片, ShapeDrawable用于绘制图形和渐变, 等等。 还可以组合使用,创建更复杂的渲染效果。

 
       Drawables允许不必继承Widget就可以方便地定制Widget的渲染效果。事实上, 它们是如此方便易用,多数自带的Android应用和Widget是通过使用Drawables来构建的。 Android核心框架使用了大约700个Drawables。 由于Drawables的系统中大量使用, Android优化过其加载过程。  比如, 每次当你创建一个Button, 一个新的Drawable将从框架资源文件中(android.R.drawable.btn_default)加载进来。 这意味着所有应用中所有的button使用不同的Drawable实例作为它们的背景。 但是所有这些Drawables共享相同的状态, 即Constant State。 这个状态中的内容因使用的Drawable类型而变化,但通常都包含所有可用的资源属性。 以button为例, Constant State包含一个Bitmap Image。 这样,所有应用中的所有Button都共享相同的Bitmap, 可以节省大量内存。
 
     下图展示当把同一个Image资源作为两个不同的View的背景时哪些实体将被创建。 正如你所见, 两个Drawable被创建,但他们共享同一个Constant State, 即同一个Bitmap
(翻译) Android SDK文档之Drawable Mutations
      虽然共享Constant State这个特性有利于减少内存的使用, 但当你需要修改Drawable的属性时可能产生问题。 考虑一个应用, 它有一个Book列表。 每个Book书名旁边有一个星形, 当用户标记当前Book为喜欢时这个星形完全不透明, 而未标记为喜欢时这个星形为半透明。 为实现这个显示效果, list adapter中的getView()方法可以这样写:
Book book = ...;
TextView listItem = ...;

listItem.setText(book.getTitle());

Drawable star = context.getResources().getDrawable(R.drawable.star);
if (book.isFavorite()) {
  star.setAlpha(255); // opaque
} else {
  star.setAlpha(70); // translucent
}
很不幸, 以上代码将产生奇怪的结果:所有的星形都成了半透明。如下图所示:
(翻译) Android SDK文档之Drawable Mutations
       这个结果也说明了Constant State的特点。 尽管我们为每个Item生成了一个新的Drawable实例, Constant State仍然是相同的。 对BitmapDrawable而言, 透明度也是Constant State的一部分。因此, 改变一个Drawable实例的透明度, 同时也会影响其他Drawable实例的透明度。 更糟糕的是, 这个问题在Android 1.0和Android 1.1上不容易修复。
 
      Android 1.5及以上版本提供了一个简单的办法来解决这个问题:新添加的mutate()方法。当你在某个Drawable上调用mutate()方法时, 将复制该Drawable的Constant State, 并允许修改该复制结果的属性且不会对其他Drawable生成影响。 注意, 就算复制了Drawable, Drawable仍然共享Bitmap。 下图显示了在Drawable上调用mutate()之后的结果:
 (翻译) Android SDK文档之Drawable Mutations
现在, 使用mutate()重新修改一下之前的代码:
Drawable star = context.getResources().getDrawable(R.drawable.star);
if (book.isFavorite()) {
  star.mutate().setAlpha(255); // opaque
} else {
  star. mutate().setAlpha(70); // translucent
}
mutate()方法返回Drawable自己, 所以链式调用非常方便。 这个方法并没有创建一个新的Drawable实例 。 修改后的代码结果是正确的。 
(翻译) Android SDK文档之Drawable Mutations
 
(另外附上自己根据这篇文档写的一个例子, 验证下mutate()的用法)
代码:
public class ConstantStateActivity extends ListActivity {

	private ListAdapter mAdapter;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		mAdapter = new MyListAdapter(this, R.layout.my_adatper, R.id.textView1, Arrays.asList("and-aa", "iphone-b"));
		getListView().setAdapter(mAdapter);
	}
	
	static class MyListAdapter extends ArrayAdapter<String> {
		private int mRes;
		private List<String> mData;
		
		public MyListAdapter(Context context, int resource,
				int textViewResourceId, List<String> objects) {
			super(context, resource, textViewResourceId, objects);
			this.mRes = resource;
			this.mData = objects;
		}
		
		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			
			View view = convertView;
			if (null == view) {
				LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(LAYOUT_INFLATER_SERVICE);
				view = inflater.inflate(mRes, parent, false);
				
				ImageView icon = (ImageView) view.findViewById(R.id.imageView1);
				MyLog.i(icon + "");
				
				if (mData.get(position).startsWith("and")) {
					icon.getDrawable().setAlpha(0);
//					icon.getDrawable().mutate().setAlpha(0);
				}
			}
			return view;
		}
		
	}
}
 布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent" android:layout_height="fill_parent"
	android:gravity="center_vertical">
	<ImageView android:layout_height="wrap_content" android:src="@drawable/icon"
		android:layout_width="wrap_content" android:id="@+id/imageView1"></ImageView>
	<TextView android:text="TextView" android:id="@+id/textView1"
		android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>

</LinearLayout>
 不调用mutate()时截图如下(ImageView都变成透明的了, 所以看不见)
(翻译) Android SDK文档之Drawable Mutations
 
调用mutate()方法时截图如下(一个ImageView透明, 另一个不透明, 正确结果!)

(翻译) Android SDK文档之Drawable Mutations
 
 

相关推荐