ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 부드러운 리스트 뷰 스크롤
    안드로이드/학습&강좌 2011. 5. 6. 03:43

    원본 블로그 주소 : http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html
    코드 주소 : http://code.google.com/p/android-imagedownloader/

    이번에 소개할 강좌는 가장 흔히 사용하는 ListView에 대한 퍼포먼스에 대한 글이다.

    리스트뷰의 데이터의 갯수가 적을때는 속도의 영향을 받지 않겠으나 웹에서 URL을 통해서 이미지를 읽어 올 경우 데이터가 많

    아지면 상당히 버벅 거리는 느낌을 받을 수 있다. 이렇게 되면 사용자의 입장에서는 상당히 답답하고 짜증이 날 것이다.

    이는 이 프로그램에 대한 만족도를 떨어 뜨리는 원인이 될 것이며 사용 하기 조차 싫어 지게 될 것이 분명하다.

    퍼포먼스는 고급 프로그래머가 되기 위해선 끊임 없이 연구하고 노력해야 하는 부분이 아닌가 생각 한다.

    나도 이런 고민을 하다 열심히 구글링을 한결과 좋은 글을 발견해서 이 글을 추천 하고자 해서 올린다.

    분석을 다 하진 못했지만 쉽게 가져다가 구현 할 수 있도록 되있어서 정말 큰 도움이 되었다.

    이글을 원본 주소는 위에 있으며 소스 코드도 제공하므로 개인적으로 찾아서 해 보길 바란다. 꼭!

    다운받는 경로를 잘 못찾겠다면 이 글을 참고 하기 바란다.


    ImageAdapter.java







     * Copyright (C) 2010 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */

    package epong.tistory.com;

    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.ImageView;

    public class ImageAdapter extends BaseAdapter {

        private static final String[] URLS = {
            "http://lh5.ggpht.com/_mrb7w4gF8Ds/TCpetKSqM1I/AAAAAAAAD2c/Qef6Gsqf12Y/s144-c/_DSC4374%20copy.jpg",
            "http://lh5.ggpht.com/_Z6tbBnE-swM/TB0CryLkiLI/AAAAAAAAVSo/n6B78hsDUz4/s144-c/_DSC3454.jpg",
            "http://lh3.ggpht.com/_GEnSvSHk4iE/TDSfmyCfn0I/AAAAAAAAF8Y/cqmhEoxbwys/s144-c/_MG_3675.jpg",
            "http://lh6.ggpht.com/_Nsxc889y6hY/TBp7jfx-cgI/AAAAAAAAHAg/Rr7jX44r2Gc/s144-c/IMGP9775a.jpg",
            "http://lh3.ggpht.com/_lLj6go_T1CQ/TCD8PW09KBI/AAAAAAAAQdc/AqmOJ7eg5ig/s144-c/Juvenile%20Gannet%20despute.jpg",
            "http://lh6.ggpht.com/_ZN5zQnkI67I/TCFFZaJHDnI/AAAAAAAABVk/YoUbDQHJRdo/s144-c/P9250508.JPG",
            "http://lh4.ggpht.com/_XjNwVI0kmW8/TCOwNtzGheI/AAAAAAAAC84/SxFJhG7Scgo/s144-c/0014.jpg",
            "http://lh6.ggpht.com/_lnDTHoDrJ_Y/TBvKsJ9qHtI/AAAAAAAAG6g/Zll2zGvrm9c/s144-c/000007.JPG",
            "http://lh6.ggpht.com/_qvCl2efjxy0/TCIVI-TkuGI/AAAAAAAAOUY/vbk9MURsv48/s144-c/DSC_0844.JPG",
            "http://lh4.ggpht.com/_TPlturzdSE8/TBv4ugH60PI/AAAAAAAAMsI/p2pqG85Ghhs/s144-c/_MG_3963.jpg",
            "http://lh4.ggpht.com/_4f1e_yo-zMQ/TCe5h9yN-TI/AAAAAAAAXqs/8X2fIjtKjmw/s144-c/IMG_1786.JPG",
            "http://lh6.ggpht.com/_iFt5VZDjxkY/TB9rQyWnJ4I/AAAAAAAADpU/lP2iStizJz0/s144-c/DSCF1014.JPG",
            "http://lh5.ggpht.com/_hepKlJWopDg/TB-_WXikaYI/AAAAAAAAElI/715k4NvBM4w/s144-c/IMG_0075.JPG",
            "http://lh6.ggpht.com/_OfRSx6nn68g/TCzsQic_z3I/AAAAAAABOOI/5G4Kwzb2qhk/s144-c/EASTER%20ISLAND_Hanga%20Roa_31.5.08_46.JPG",
            "http://lh6.ggpht.com/_ZGv_0FWPbTE/TB-_GLhqYBI/AAAAAAABVxs/cVEvQzt0ke4/s144-c/IMG_1288_hf.jpg",
            "http://lh6.ggpht.com/_a29lGRJwo0E/TBqOK_tUKmI/AAAAAAAAVbw/UloKpjsKP3c/s144-c/31012332.jpg",
            "http://lh3.ggpht.com/_55Lla4_ARA4/TB6xbyxxJ9I/AAAAAAABTWo/GKe24SwECns/s144-c/Bluebird%20049.JPG",
            "http://lh3.ggpht.com/_iVnqmIBYi4Y/TCaOH6rRl1I/AAAAAAAA1qg/qeMerYQ6DYo/s144-c/Kwiat_100626_0016.jpg",
            "http://lh6.ggpht.com/_QFsB_q7HFlo/TCItd_2oBkI/AAAAAAAAFsk/4lgJWweJ5N8/s144-c/3705226938_d6d66d6068_o.jpg",
            "http://lh5.ggpht.com/_JTI0xxNrKFA/TBsKQ9uOGNI/AAAAAAAChQg/z8Exh32VVTA/s144-c/CRW_0015-composite.jpg",
            "http://lh6.ggpht.com/_loGyjar4MMI/S-InVNkTR_I/AAAAAAAADJY/Fb5ifFNGD70/s144-c/Moving%20Rock.jpg",
            "http://lh4.ggpht.com/_L7i4Tra_XRY/TBtxjScXLqI/AAAAAAAAE5o/ue15HuP8eWw/s144-c/opera%20house%20II.jpg",
            "http://lh3.ggpht.com/_rfAz5DWHZYs/S9cstBTv1iI/AAAAAAAAeYA/EyZPUeLMQ98/s144-c/DSC_6425.jpg",
            "http://lh6.ggpht.com/_iGI-XCxGLew/S-iYQWBEG-I/AAAAAAAACB8/JuFti4elptE/s144-c/norvig-polar-bear.jpg",
            "http://lh3.ggpht.com/_M3slUPpIgmk/SlbnavqG1cI/AAAAAAAACvo/z6-CnXGma7E/s144-c/mf_003.jpg",
            "http://lh4.ggpht.com/_loGyjar4MMI/S-InQvd_3hI/AAAAAAAADIw/dHvCFWfyHxQ/s144-c/Rainbokeh.jpg",
            "http://lh4.ggpht.com/_yy6KdedPYp4/SB5rpK3Zv0I/AAAAAAAAOM8/mokl_yo2c9E/s144-c/Point%20Reyes%20road%20.jpg",
            "http://lh5.ggpht.com/_6_dLVKawGJA/SMwq86HlAqI/AAAAAAAAG5U/q1gDNkmE5hI/s144-c/mobius-glow.jpg",
            "http://lh3.ggpht.com/_QFsB_q7HFlo/TCItc19Jw3I/AAAAAAAAFs4/nPfiz5VGENk/s144-c/4551649039_852be0a952_o.jpg",
            "http://lh6.ggpht.com/_TQY-Nm7P7Jc/TBpjA0ks2MI/AAAAAAAABcI/J6ViH98_poM/s144-c/IMG_6517.jpg",
            "http://lh3.ggpht.com/_rfAz5DWHZYs/S9cLAeKuueI/AAAAAAAAeYU/E19G8DOlJRo/s144-c/DSC_4397_8_9_tonemapped2.jpg",
            "http://lh4.ggpht.com/_TQY-Nm7P7Jc/TBpi6rKfFII/AAAAAAAABbg/79FOc0Dbq0c/s144-c/david_lee_sakura.jpg",
            "http://lh3.ggpht.com/_TQY-Nm7P7Jc/TBpi8EJ4eDI/AAAAAAAABb0/AZ8Cw1GCaIs/s144-c/Hokkaido%20Swans.jpg",
            "http://lh3.ggpht.com/_1aZMSFkxSJI/TCIjB6od89I/AAAAAAAACHM/CLWrkH0ziII/s144-c/079.jpg",
            "http://lh5.ggpht.com/_loGyjar4MMI/S-InWuHkR9I/AAAAAAAADJE/wD-XdmF7yUQ/s144-c/Colorado%20River%20Sunset.jpg",
            "http://lh3.ggpht.com/_0YSlK3HfZDQ/TCExCG1Zc3I/AAAAAAAAX1w/9oCH47V6uIQ/s144-c/3138923889_a7fa89cf94_o.jpg",
            "http://lh6.ggpht.com/_K29ox9DWiaM/TAXe4Fi0xTI/AAAAAAAAVIY/zZA2Qqt2HG0/s144-c/IMG_7100.JPG",
            "http://lh6.ggpht.com/_0YSlK3HfZDQ/TCEx16nJqpI/AAAAAAAAX1c/R5Vkzb8l7yo/s144-c/4235400281_34d87a1e0a_o.jpg",
            "http://lh4.ggpht.com/_8zSk3OGcpP4/TBsOVXXnkTI/AAAAAAAAAEo/0AwEmuqvboo/s144-c/yosemite_forrest.jpg",
            "http://lh4.ggpht.com/_6_dLVKawGJA/SLZToqXXVrI/AAAAAAAAG5k/7fPSz_ldN9w/s144-c/coastal-1.jpg",
            "http://lh4.ggpht.com/_WW8gsdKXVXI/TBpVr9i6BxI/AAAAAAABhNg/KC8aAJ0wVyk/s144-c/IMG_6233_1_2-2.jpg",
            "http://lh3.ggpht.com/_loGyjar4MMI/S-InS0tJJSI/AAAAAAAADHU/E8GQJ_qII58/s144-c/Windmills.jpg",
            "http://lh4.ggpht.com/_loGyjar4MMI/S-InbXaME3I/AAAAAAAADHo/4gNYkbxemFM/s144-c/Frantic.jpg",
            "http://lh5.ggpht.com/_loGyjar4MMI/S-InKAviXzI/AAAAAAAADHA/NkyP5Gge8eQ/s144-c/Rice%20Fields.jpg",
            "http://lh3.ggpht.com/_loGyjar4MMI/S-InZA8YsZI/AAAAAAAADH8/csssVxalPcc/s144-c/Seahorse.jpg",
            "http://lh3.ggpht.com/_syQa1hJRWGY/TBwkCHcq6aI/AAAAAAABBEg/R5KU1WWq59E/s144-c/Antelope.JPG",
            "http://lh5.ggpht.com/_MoEPoevCLZc/S9fHzNgdKDI/AAAAAAAADwE/UAno6j5StAs/s144-c/c84_7083.jpg",
            "http://lh4.ggpht.com/_DJGvVWd7IEc/TBpRsGjdAyI/AAAAAAAAFNw/rdvyRDgUD8A/s144-c/Free.jpg",
            "http://lh6.ggpht.com/_iO97DXC99NY/TBwq3_kmp9I/AAAAAAABcz0/apq1ffo_MZo/s144-c/IMG_0682_cp.jpg",
            "http://lh4.ggpht.com/_7V85eCJY_fg/TBpXudG4_PI/AAAAAAAAPEE/8cHJ7G84TkM/s144-c/20100530_120257_0273-Edit-2.jpg"
        };
       
        private final ImageDownloader imageDownloader = new ImageDownloader();
       
        public int getCount() {
            return URLS.length;
        }

        public String getItem(int position) {
            return URLS[position];
        }

        public long getItemId(int position) {
            return URLS[position].hashCode();
        }

        public View getView(int position, View view, ViewGroup parent) {
            if (view == null) {
                view = new ImageView(parent.getContext());
                view.setPadding(6, 6, 6, 6);
            }

            imageDownloader.download(URLS[position], (ImageView) view);
           
            return view;
        }

        public ImageDownloader getImageDownloader() {
            return imageDownloader;
        }
    }


     



    ImageListActivity .java

    /*
     * Copyright (C) 2010 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */

    package epong.tistory.com;

    import android.app.ListActivity;
    import android.os.Bundle;
    import android.widget.RadioGroup;

    public class ImageListActivity extends ListActivity  implements RadioGroup.OnCheckedChangeListener {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
           
            setContentView(R.layout.main);

            RadioGroup radioGroup = (RadioGroup) findViewById(R.id.radioGroup);
            radioGroup.setOnCheckedChangeListener(this);
           
            setListAdapter(new ImageAdapter());
        }

        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            ImageDownloader.Mode mode = ImageDownloader.Mode.NO_ASYNC_TASK;
           
            if (checkedId == R.id.correctButton) {
                mode = ImageDownloader.Mode.CORRECT;
            }else if (checkedId == R.id.randomButton) {
                    mode = ImageDownloader.Mode.NO_DOWNLOADED_DRAWABLE;
            }
           
            ((ImageAdapter) getListAdapter()).getImageDownloader().setMode(mode);
        }
    }





    ImageDownloader.java


    /*
     * Copyright (C) 2010 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */

    package epong.tistory.com;

    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.HttpStatus;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.DefaultHttpClient;

    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Color;
    import android.graphics.drawable.ColorDrawable;
    import android.graphics.drawable.Drawable;
    import android.net.http.AndroidHttpClient;
    import android.os.AsyncTask;
    import android.os.Handler;
    import android.util.Log;
    import android.widget.ImageView;

    import java.io.FilterInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.ref.SoftReference;
    import java.lang.ref.WeakReference;
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.concurrent.ConcurrentHashMap;

    /**
     * This helper class download images from the Internet and binds those with the provided ImageView.
     *
     * <p>It requires the INTERNET permission, which should be added to your application's manifest
     * file.</p>
     *
     * A local cache of downloaded images is maintained internally to improve performance.
     */
    public class ImageDownloader {
        private static final String LOG_TAG = "ImageDownloader";

        public enum Mode { NO_ASYNC_TASK, NO_DOWNLOADED_DRAWABLE, CORRECT }
        private Mode mode = Mode.NO_ASYNC_TASK;
       
        /**
         * Download the specified image from the Internet and binds it to the provided ImageView. The
         * binding is immediate if the image is found in the cache and will be done asynchronously
         * otherwise. A null bitmap will be associated to the ImageView if an error occurs.
         *
         * @param url The URL of the image to download.
         * @param imageView The ImageView to bind the downloaded image to.
         */
        public void download(String url, ImageView imageView) {
            resetPurgeTimer();
            Bitmap bitmap = getBitmapFromCache(url);

            if (bitmap == null) {
                forceDownload(url, imageView);
            } else {
                cancelPotentialDownload(url, imageView);
                imageView.setImageBitmap(bitmap);
            }
        }

        /*
         * Same as download but the image is always downloaded and the cache is not used.
         * Kept private at the moment as its interest is not clear.
           private void forceDownload(String url, ImageView view) {
              forceDownload(url, view, null);
           }
         */

        /**
         * Same as download but the image is always downloaded and the cache is not used.
         * Kept private at the moment as its interest is not clear.
         */
        private void forceDownload(String url, ImageView imageView) {
            // State sanity: url is guaranteed to never be null in DownloadedDrawable and cache keys.
            if (url == null) {
                imageView.setImageDrawable(null);
                return;
            }

            if (cancelPotentialDownload(url, imageView)) {
                switch (mode) {
                    case NO_ASYNC_TASK:
                        Bitmap bitmap = downloadBitmap(url);
                        addBitmapToCache(url, bitmap);
                        imageView.setImageBitmap(bitmap);
                        break;

                    case NO_DOWNLOADED_DRAWABLE:
                        imageView.setMinimumHeight(156);
                        BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
                        task.execute(url);
                        break;

                    case CORRECT:
                        task = new BitmapDownloaderTask(imageView);
                        DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
                        imageView.setImageDrawable(downloadedDrawable);
                        imageView.setMinimumHeight(156);
                        task.execute(url);
                        break;
                }
            }
        }

        /**
         * Returns true if the current download has been canceled or if there was no download in
         * progress on this image view.
         * Returns false if the download in progress deals with the same url. The download is not
         * stopped in that case.
         */
        private static boolean cancelPotentialDownload(String url, ImageView imageView) {
            BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);

            if (bitmapDownloaderTask != null) {
                String bitmapUrl = bitmapDownloaderTask.url;
                if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
                    bitmapDownloaderTask.cancel(true);
                } else {
                    // The same URL is already being downloaded.
                    return false;
                }
            }
            return true;
        }

        /**
         * @param imageView Any imageView
         * @return Retrieve the currently active download task (if any) associated with this imageView.
         * null if there is no such task.
         */
        private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
            if (imageView != null) {
                Drawable drawable = imageView.getDrawable();
                if (drawable instanceof DownloadedDrawable) {
                    DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;
                    return downloadedDrawable.getBitmapDownloaderTask();
                }
            }
            return null;
        }

        Bitmap downloadBitmap(String url) {
            final int IO_BUFFER_SIZE = 4 * 1024;

            // AndroidHttpClient is not allowed to be used from the main thread
            final HttpClient client = (mode == Mode.NO_ASYNC_TASK) ? new DefaultHttpClient() :
                AndroidHttpClient.newInstance("Android");
            final HttpGet getRequest = new HttpGet(url);

            try {
                HttpResponse response = client.execute(getRequest);
                final int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode != HttpStatus.SC_OK) {
                    Log.w("ImageDownloader", "Error " + statusCode +
                            " while retrieving bitmap from " + url);
                    return null;
                }

                final HttpEntity entity = response.getEntity();
                if (entity != null) {
                    InputStream inputStream = null;
                    try {
                        inputStream = entity.getContent();
                        return BitmapFactory.decodeStream(new FlushedInputStream(inputStream));
                    } finally {
                        if (inputStream != null) {
                            inputStream.close();
                        }
                        entity.consumeContent();
                    }
                }
            } catch (IOException e) {
                getRequest.abort();
                Log.w(LOG_TAG, "I/O error while retrieving bitmap from " + url, e);
            } catch (IllegalStateException e) {
                getRequest.abort();
                Log.w(LOG_TAG, "Incorrect URL: " + url);
            } catch (Exception e) {
                getRequest.abort();
                Log.w(LOG_TAG, "Error while retrieving bitmap from " + url, e);
            } finally {
                if ((client instanceof AndroidHttpClient)) {
                    ((AndroidHttpClient) client).close();
                }
            }
            return null;
        }
       
        /**
         * A patched InputSteam that tries harder to fully read the input stream.
         */
        static class FlushedInputStream extends FilterInputStream {
            public FlushedInputStream(InputStream inputStream) {
                super(inputStream);
            }

            @Override
            public long skip(long n) throws IOException {
                long totalBytesSkipped = 0L;
                while (totalBytesSkipped < n) {
                    long bytesSkipped = in.skip(n-totalBytesSkipped);
                    if (bytesSkipped == 0L) break;
                    totalBytesSkipped += bytesSkipped;
                }
                return totalBytesSkipped;
            }
        }

        /**
         * The actual AsyncTask that will asynchronously download the image.
         */
        class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
            private String url;
            private final WeakReference<ImageView> imageViewReference;

            public BitmapDownloaderTask(ImageView imageView) {
                imageViewReference = new WeakReference<ImageView>(imageView);
            }

            /**
             * Actual download method.
             */
            @Override
            protected Bitmap doInBackground(String... params) {
                url = params[0];
                return downloadBitmap(url);
            }

            /**
             * Once the image is downloaded, associates it to the imageView
             */
            @Override
            protected void onPostExecute(Bitmap bitmap) {
                if (isCancelled()) {
                    bitmap = null;
                }

                addBitmapToCache(url, bitmap);

                if (imageViewReference != null) {
                    ImageView imageView = imageViewReference.get();
                    BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
                    // Change bitmap only if this process is still associated with it
                    // Or if we don't use any bitmap to task association (NO_DOWNLOADED_DRAWABLE mode)
                    if ((this == bitmapDownloaderTask) || (mode != Mode.CORRECT)) {
                        imageView.setImageBitmap(bitmap);
                    }
                }
            }
        }


        /**
         * A fake Drawable that will be attached to the imageView while the download is in progress.
         *
         * <p>Contains a reference to the actual download task, so that a download task can be stopped
         * if a new binding is required, and makes sure that only the last started download process can
         * bind its result, independently of the download finish order.</p>
         */
        static class DownloadedDrawable extends ColorDrawable {
            private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

            public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
                super(Color.BLACK);
                bitmapDownloaderTaskReference =
                    new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
            }

            public BitmapDownloaderTask getBitmapDownloaderTask() {
                return bitmapDownloaderTaskReference.get();
            }
        }

        public void setMode(Mode mode) {
            this.mode = mode;
            clearCache();
        }

       
        /*
         * Cache-related fields and methods.
         *
         * We use a hard and a soft cache. A soft reference cache is too aggressively cleared by the
         * Garbage Collector.
         */
       
        private static final int HARD_CACHE_CAPACITY = 50;
        private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds

        // Hard cache, with a fixed maximum capacity and a life duration
        private final HashMap<String, Bitmap> sHardBitmapCache =
            new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
                if (size() > HARD_CACHE_CAPACITY) {
                    // Entries push-out of hard reference cache are transferred to soft reference cache
                    sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
                    return true;
                } else
                    return false;
            }
        };

        // Soft cache for bitmaps kicked out of hard cache
        private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache =
            new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);

        private final Handler purgeHandler = new Handler();

        private final Runnable purger = new Runnable() {
            public void run() {
                clearCache();
            }
        };

        /**
         * Adds this bitmap to the cache.
         * @param bitmap The newly downloaded bitmap.
         */
        private void addBitmapToCache(String url, Bitmap bitmap) {
            if (bitmap != null) {
                synchronized (sHardBitmapCache) {
                    sHardBitmapCache.put(url, bitmap);
                }
            }
        }

        /**
         * @param url The URL of the image that will be retrieved from the cache.
         * @return The cached bitmap or null if it was not found.
         */
        private Bitmap getBitmapFromCache(String url) {
            // First try the hard reference cache
            synchronized (sHardBitmapCache) {
                final Bitmap bitmap = sHardBitmapCache.get(url);
                if (bitmap != null) {
                    // Bitmap found in hard cache
                    // Move element to first position, so that it is removed last
                    sHardBitmapCache.remove(url);
                    sHardBitmapCache.put(url, bitmap);
                    return bitmap;
                }
            }

            // Then try the soft reference cache
            SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(url);
            if (bitmapReference != null) {
                final Bitmap bitmap = bitmapReference.get();
                if (bitmap != null) {
                    // Bitmap found in soft cache
                    return bitmap;
                } else {
                    // Soft reference has been Garbage Collected
                    sSoftBitmapCache.remove(url);
                }
            }

            return null;
        }
     
        /**
         * Clears the image cache used internally to improve performance. Note that for memory
         * efficiency reasons, the cache will automatically be cleared after a certain inactivity delay.
         */
        public void clearCache() {
            sHardBitmapCache.clear();
            sSoftBitmapCache.clear();
        }

        /**
         * Allow a new delay before the automatic cache clear is done.
         */
        private void resetPurgeTimer() {
            purgeHandler.removeCallbacks(purger);
            purgeHandler.postDelayed(purger, DELAY_BEFORE_PURGE);
        }
    }




    main.xml && string.xml

    <?xml version="1.0" encoding="utf-8" ?>
    <!--  Copyright (C) 2010 The Android Open Source Project Licensed under the
      Apache License, Version 2.0 (the "License"); you may not use this file
      except in compliance with the License. You may obtain a copy of the
      License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
      by applicable law or agreed to in writing, software distributed under
      the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      CONDITIONS OF ANY KIND, either express or implied. See the License for
      the specific language governing permissions and limitations under the
      License.
      -->
    <merge
     xmlns:android="http://schemas.android.com/apk/res/android">
     <LinearLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:orientation="vertical"
      android:layout_width="match_parent"
      android:layout_height="match_parent">
      <RadioGroup
       android:id="@+id/radioGroup"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="horizontal"
       android:gravity="center"
       android:checkedButton="@+id/noTasksButton">
       <RadioButton
        android:id="@+id/noTasksButton"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:text="@string/noTasks" />
       <RadioButton
        android:id="@+id/randomButton"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:text="@string/random" />
       <RadioButton
        android:id="@+id/correctButton"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:text="@string/correct" />
      </RadioGroup>
      <ListView
       android:id="@android:id/list"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />
      <TextView
       android:id="@android:id/empty"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:gravity="center"
       android:text="@string/empty"
       android:visibility="gone" />
     </LinearLayout>
    </merge>

    <!-- string.xml -->
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
     <string name="hello">Hello World, SmoothListView!</string>
     <string name="app_name">부드러운 리스트뷰 스크롤</string>
     <string name="empty">Loading images...</string>
     <string name="noTasks">No tasks</string>
     <string name="random">Random</string>
     <string name="correct">Correct</string>
    </resources>


    실행 화면



    코드를 퍼가실땐 댓글을 달아 주시길 바란다.

    ~~


    댓글

COPYRIGHT 2010 EpoNg. ALL RIGHTS RESERVED.