首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > 移动开发 >

(DOC)Displaying Bitmaps Efficiently 三

2012-07-08 
(DOC)Displaying Bitmaps Efficiently 3Handle Configuration Changes运行时配置改变了,如屏幕的方向改变

(DOC)Displaying Bitmaps Efficiently 3

Handle Configuration Changes运行时配置改变了,如屏幕的方向改变了,导致Android会销毁,重启。这就需要避免处理所有的图片了,南昌需要一个更缓和,更高效的办法。前面已经讨论过内存缓存了,这个缓存可以通过Fragment的setRetainInstance(true)得到,Activity重建以后,Fragment会重新加载,reattached附着到Activity中下面是一个使用Fragment与LruCache在配置改变时的例子。private LruCache mMemoryCache;@Overrideprotected void onCreate(Bundle savedInstanceState) {    ...    RetainFragment mRetainFragment =            RetainFragment.findOrCreateRetainFragment(getFragmentManager());    mMemoryCache = RetainFragment.mRetainedCache;    if (mMemoryCache == null) {        mMemoryCache = new LruCache(cacheSize) {            ... // Initialize cache here as usual        }        mRetainFragment.mRetainedCache = mMemoryCache;    }    ...}class RetainFragment extends Fragment {    private static final String TAG = "RetainFragment";    public LruCache mRetainedCache;    public RetainFragment() {}    public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {        RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);        if (fragment == null) {            fragment = new RetainFragment();        }        return fragment;    }    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setRetainInstance(true);    }}要测试的话可以旋转屏幕改变Fragment的获取方式Displaying Bitmaps in Your UI在你自己的ui中显示位图 ,这章节将前面的几章节综合讨论,在配置改变或并发时如何加载图片到gridview这样的组件中。在ViewPager中的实现这个非正式的view模式,是个不错的办法,可以使用ViewPager组件后端是PagerAdapter提供数据,一个更有效的是FragmentStatePagerAdapter,因为它会自动上和保存状态。如果只是一小部分的图片,且你相信他们不会超过内存限制,使用上述的adapter是个不错的选择。public class ImageDetailActivity extends FragmentActivity {    public static final String EXTRA_IMAGE = "extra_image";    private ImagePagerAdapter mAdapter;    private ViewPager mPager;    // A static dataset to back the ViewPager adapter    public final static Integer[] imageResIds = new Integer[] {            R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,            R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,            R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.image_detail_pager); // Contains just a ViewPager        mAdapter = new ImagePagerAdapter(getSupportFragmentManager(), imageResIds.length);        mPager = (ViewPager) findViewById(R.id.pager);        mPager.setAdapter(mAdapter);    }    public static class ImagePagerAdapter extends FragmentStatePagerAdapter {        private final int mSize;        public ImagePagerAdapter(FragmentManager fm, int size) {            super(fm);            mSize = size;        }        @Override        public int getCount() {            return mSize;        }        @Override        public Fragment getItem(int position) {            return ImageDetailFragment.newInstance(position);        }    }}具体的Fragmentpublic class ImageDetailFragment extends Fragment {    private static final String IMAGE_DATA_EXTRA = "resId";    private int mImageNum;    private ImageView mImageView;    static ImageDetailFragment newInstance(int imageNum) {        final ImageDetailFragment f = new ImageDetailFragment();        final Bundle args = new Bundle();        args.putInt(IMAGE_DATA_EXTRA, imageNum);        f.setArguments(args);        return f;    }    // Empty constructor, required as per Fragment docs    public ImageDetailFragment() {}    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mImageNum = getArguments() != null ? getArguments().getInt(IMAGE_DATA_EXTRA) : -1;    }    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,            Bundle savedInstanceState) {        // image_detail_fragment.xml contains just an ImageView        final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);        mImageView = (ImageView) v.findViewById(R.id.imageView);        return v;    }    @Override    public void onActivityCreated(Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);        final int resId = ImageDetailActivity.imageResIds[mImageNum];        mImageView.setImageResource(resId); // Load image into ImageView    }}这里图片由ui线程来读取的,会导致程序的fc,这时可以你自己修改成从地方读取,用到前面的AsyncTask或缓存方法。public class ImageDetailActivity extends FragmentActivity {    ...    public void loadBitmap(int resId, ImageView imageView) {        mImageView.setImageResource(R.drawable.image_placeholder);        BitmapWorkerTask task = new BitmapWorkerTask(mImageView);        task.execute(resId);    }    ... // include BitmapWorkerTask class}public class ImageDetailFragment extends Fragment {    ...    @Override    public void onActivityCreated(Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);        if (ImageDetailActivity.class.isInstance(getActivity())) {            final int resId = ImageDetailActivity.imageResIds[mImageNum];            // Call out to ImageDetailActivity to load the bitmap in a background thread            ((ImageDetailActivity) getActivity()).loadBitmap(resId, mImageView);        }    }}其它一些方法如确定大小,从网络获取可以在BitmapWorkerTask中处理了,可以加入缓存。public class ImageDetailActivity extends FragmentActivity {    ...    private LruCache mMemoryCache;    @Override    public void onCreate(Bundle savedInstanceState) {        ...        // initialize LruCache as per Use a Memory Cache section    }    public void loadBitmap(int resId, ImageView imageView) {        final String imageKey = String.valueOf(resId);        final Bitmap bitmap = mMemoryCache.get(imageKey);        if (bitmap != null) {            mImageView.setImageBitmap(bitmap);        } else {            mImageView.setImageResource(R.drawable.image_placeholder);            BitmapWorkerTask task = new BitmapWorkerTask(mImageView);            task.execute(resId);        }    }    ... // include updated BitmapWorkerTask from Use a Memory Cache section}将上面合在一起就可以了。3.0以后就有Fragment,这是个不错的东西,我在微博程序中就大量使用,它可以有Activity类似的生命周期,可以保存一些状态便于重建等。Load Bitmaps into a GridView ,ListView是一样的处理。http://developer.android.com/design/building-blocks/grid-lists.html 这里有说到一些相关的知识,可以参考下。GridView会回收一些子元素,所以它能有足够的内存一直加载新的图片。现在来看看:public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {    private ImageAdapter mAdapter;    // A static dataset to back the GridView adapter    public final static Integer[] imageResIds = new Integer[] {            R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,            R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,            R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};    // Empty constructor as per Fragment docs    public ImageGridFragment() {}    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mAdapter = new ImageAdapter(getActivity());    }    @Override    public View onCreateView(            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        final View v = inflater.inflate(R.layout.image_grid_fragment, container, false);        final GridView mGridView = (GridView) v.findViewById(R.id.gridView);        mGridView.setAdapter(mAdapter);        mGridView.setOnItemClickListener(this);        return v;    }    @Override    public void onItemClick(AdapterView parent, View v, int position, long id) {        final Intent i = new Intent(getActivity(), ImageDetailActivity.class);        i.putExtra(ImageDetailActivity.EXTRA_IMAGE, position);        startActivity(i);    }    private class ImageAdapter extends BaseAdapter {        private final Context mContext;        public ImageAdapter(Context context) {            super();            mContext = context;        }        @Override        public int getCount() {            return imageResIds.length;        }        @Override        public Object getItem(int position) {            return imageResIds[position];        }        @Override        public long getItemId(int position) {            return position;        }        @Override        public View getView(int position, View convertView, ViewGroup container) {            ImageView imageView;            if (convertView == null) { // if it's not recycled, initialize some attributes                imageView = new ImageView(mContext);                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);                imageView.setLayoutParams(new GridView.LayoutParams(                        LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));            } else {                imageView = (ImageView) convertView;            }            imageView.setImageResource(imageResIds[position]); // Load image into ImageView            return imageView;        }    }}同样是使用了Fragment,上面也是在ui线程对图片解码,所以需要修改为缓存的方式:public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {    ...    private class ImageAdapter extends BaseAdapter {        ...        @Override        public View getView(int position, View convertView, ViewGroup container) {            ...            loadBitmap(imageResIds[position], imageView)            return imageView;        }    }    public void loadBitmap(int resId, ImageView imageView) {        if (cancelPotentialWork(resId, imageView)) {            final BitmapWorkerTask task = new BitmapWorkerTask(imageView);            final AsyncDrawable asyncDrawable =                    new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);            imageView.setImageDrawable(asyncDrawable);            task.execute(resId);        }    }    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 static boolean cancelPotentialWork(int data, ImageView imageView) {        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);        if (bitmapWorkerTask != null) {            final int bitmapData = bitmapWorkerTask.data;            if (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;    }    ... // include updated BitmapWorkerTask classListView是一样的,就不说了。源码可以在示例程序中找到。至此,也写过几篇关于网络图片载入到ListView中的文章了,这里翻译了文档中的一些文章,结合了缓存使用,文档中的办法固然也算是一种解决方案,但也不是完美的,不同的情况还是要不同对待,分析问题后再选择相应的办法。文档只是提供了一些解决的办法,如何组合,如何应用不能一概而论的。因为它是一种通用型的。如产生很多的线程就是一个问题,这些文档中显然没有提到,还有可以把缓存设置为静态,全局变量,文档中没有提到可能是因为静态变量不提倡用吧(有一篇关于静态变量引用的文章),但是对于一个程序来说,多处地方用到相同的缓存图片也有可能,为了方便使用把缓存设置成全局变量也是可以的。

热点排行