学习内容
- Google Android Training
http://developer.android.com/training/index.html - Github托管
https://github.com/kesenhoo/android-training-course-in-chinese - 中文阅读地址
http://hukai.me/android-training-course-in-chinese/index.html
以下是仅对我个人有意义的笔记
4. Android图像与动画
4.1. 高效显示Bitmap(如何在加载并处理bitmaps的同时保持用户界面响应,防止超出内存限制。)
4.1.1. 高效加载大图(实现方式:加载一个缩小版本的图片)
实现缩放展示图片
缩放的方法
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions;inJustDecodeBounds 值设为true,不返回实际的bitmap,也不给其分配内存空间这样就避免内存溢出了。但允许查询图片的信息(大小) final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; //获取图片的信息 BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize;计算inSampleSize,不过这种也是预先设置了要变成什么尺寸的图片,然后计算出inSampleSize应该设为什么值 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set;设置为false之后,就返回bitmap真实值了 options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); }
实现
mImageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
4.1.2. 非UI线程处理Bitmap(非本地图片资源)
跟上一节相比,这里展示了用异步来实现bitmap缩放展示。
异步方法
class BitmapWorkerTask extends AsyncTask { private final WeakReference imageViewReference; private int data = 0; public BitmapWorkerTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can be garbage collected imageViewReference = new WeakReference(imageView); } // Decode image in background. @Override protected Bitmap doInBackground(Integer... params) { data = params[0]; return decodeSampledBitmapFromResource(getResources(), data, 100, 100)); } // Once complete, see if ImageView is still around and set bitmap. @Override protected void onPostExecute(Bitmap bitmap) { if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); if (imageView != null) { imageView.setImageBitmap(bitmap); } } } }
实现
public void loadBitmap(int resId, ImageView imageView) { BitmapWorkerTask task = new BitmapWorkerTask(imageView); task.execute(resId); }
4.1.3. 缓存Bitmap
这个就是为了处理并发的问题(listview……),所以对于AsyncTask进行扩展
方法
static class AsyncDrawable extends BitmapDrawable { private final WeakReference bitmapWorkerTaskReference; public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { super(res, bitmap); bitmapWorkerTaskReference = new WeakReference(bitmapWorkerTask); } public BitmapWorkerTask getBitmapWorkerTask() { return bitmapWorkerTaskReference.get(); } }
使用
public void loadBitmap(int resId, ImageView imageView) { if (cancelPotentialWork(resId, imageView)) { //新建 异步下载所需要展示的图片,并设置展示在imageview上 final BitmapWorkerTask task = new BitmapWorkerTask(imageView); //为这个异步设定一个占位图 final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), mPlaceHolderBitmap, task); //为这个imageview设置异步展现图片 imageView.setImageDrawable(asyncDrawable); task.execute(resId); } }
取消 例如,两个异步都对同一个imageview进行了操作,取消其中一个操作
实现
public static boolean cancelPotentialWork(int data, ImageView imageView) { final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (bitmapWorkerTask != null) { final int bitmapData = bitmapWorkerTask.data; if (bitmapData == 0 || bitmapData != data) { // Cancel previous task;取消前一个操作 bitmapWorkerTask.cancel(true); } else { // The same work is already in progress return false; } } // No task associated with the ImageView, or an existing task was cancelled return true; }
方法
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { if (imageView != null) { final Drawable drawable = imageView.getDrawable(); if (drawable instanceof AsyncDrawable) { final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; return asyncDrawable.getBitmapWorkerTask(); } } return null; }
BitmapWorkerTask的onPostExecute() 方法里面做更新操作:
class BitmapWorkerTask extends AsyncTask { ... @Override protected void onPostExecute(Bitmap bitmap) { if (isCancelled()) { bitmap = null; } if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (this == bitmapWorkerTask && imageView != null) { imageView.setImageBitmap(bitmap); } } } }
4.1.4. 管理Bitmap的内存(原理)
管理Android 3.0及其以上版本的内存(以下的那些版本,就暂时不看了)
- 3.0以后,BitmapFactory.Options.inBitmap,decode方法会在加载Bitmap数据的时候去重用已在内存中已存在的Bitmap。然而,在Android 4.4 (API level 19)之前,只有同等大小的bitmap才可以被重用。
- 那么,如何保存已存在的bitmap以供之后使用呢?(系统提供的方法,了解一下原理即可,当一个应用运行在Android 3.0或者更高的平台上并且Bitmap从LruCache中移除时,Bitmap的一个软引用会被存放在Hashset中,这样便于之后可能被inBitmap重用)
如何使用缓存的bitmap
decodeSampledBitmapFromFile
//这句就是重点了,就是在这里去查询cache是不是有存储bitmap addInBitmapOptions(options, cache);
addInBitmapOptions
// Try to find a bitmap to use for inBitmap.重点代码,主要就是这句话 Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
getBitmapFromReusableSet
//最主要是这句代码,判断是否可以返回存储的bitmap使用 canUseForInBitmap(item, options)
canUseForInBitmap(重点,大意就是存储的图片能达到使用要求就表示可用)
static boolean canUseForInBitmap( Bitmap candidate, BitmapFactory.Options targetOptions) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // From Android 4.4 (KitKat) onward we can re-use if the byte size of // the new bitmap is smaller than the reusable bitmap candidate // allocation byte count. int width = targetOptions.outWidth / targetOptions.inSampleSize; int height = targetOptions.outHeight / targetOptions.inSampleSize; int byteCount = width * height * getBytesPerPixel(candidate.getConfig()); return byteCount <= candidate.getAllocationByteCount(); } // On earlier versions, the dimensions must match exactly and the inSampleSize must be 1 return candidate.getWidth() == targetOptions.outWidth && candidate.getHeight() == targetOptions.outHeight && targetOptions.inSampleSize == 1; } /** * A helper function to return the byte usage per pixel of a bitmap based on its configuration.分辨率转换 */ static int getBytesPerPixel(Config config) { if (config == Config.ARGB_8888) { return 4; } else if (config == Config.RGB_565) { return 2; } else if (config == Config.ARGB_4444) { return 2; } else if (config == Config.ALPHA_8) { return 1; } return 1; }
4.1.5. 在UI上显示Bitmap(使用)
ViewPager+FragmentStatePagerAdapter(基本就是结合上述bitmap的所有知识点显示图片)
GridView
4.2. 使用OpenGL ES显示图像(一组可以显示高级动画和图形的工具集)(太复杂了,暂时不看)
- OpenGL ES 1.0和2.0的接口不通用。 2.
4.2.1. 建立OpenGL ES的环境
4.2.2. 定义Shapes
4.2.3. 绘制Shapes
4.2.4. 运用投影与相机视图
4.2.5. 添加移动
4.2.6. 响应触摸事件
4.3. 添加动画(如何给你的用户界面添加过渡动画。)
4.3.1. View间渐变
- 关键在于Alpha值(0→1)+view(gone→visible),淡入;
- onAnimationEnd()中设置,visible→gone,淡出。
4.3.2. 使用ViewPager实现屏幕滑动
- viewpage本身带有左右移动,所以,实现屏幕滑动动画的时候,根据position来设置alpha的值和ScaleX、ScaleY就可以了。
4.3.3. 展示Card翻转动画
- 自定义animation里面的属性需要了解清楚,这样才好知道为什么会出现这样的效果。
4.3.4. 缩放View
- zoom 动画,计算画面大小+自定义动作
4.3.5. 布局变更动画
为创建的布局加上这个属性
<LinearLayout android:id="@+id/container" android:animateLayoutChanges="true" .../>
在布局中设置add/del/update
private void addItem() { View newView; ... mContainerView.addView(newView, 0);}