天天看點

Android照片牆的實作

實作一個簡單的照片牆。

實作原理:

1:去重複 相同的url不要下載下傳兩次,可以用set集合對封裝的任務,但是我們應用使用了記憶體緩存即使不做判斷也不會加載兩次,因為LruCache對象已經幫我們實作了

2:下載下傳圖檔的任務使用了AsyncTask

3: 當我們快速滑動螢幕的時候怎麼做到當手指松開的時候才加載可見螢幕範圍對應的圖檔,避免了快速滑動導緻卡的問題

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <GridView
        android:id="@+id/photo_wall"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:columnWidth="90dip"
        android:gravity="center"
        android:numColumns="auto_fit"
        android:stretchMode="columnWidth"
        android:verticalSpacing="10dip" />

</RelativeLayout>
           

Constant.java

public class Constant {

    public static String[] imgs = new String[]{
            "http://img5.douban.com/view/photo/thumb/public/p1042461956.jpg",
            "http://img5.douban.com/lpic/s28035816.jpg",
            "http://img3.douban.com/lpic/s28049685.jpg",
            "http://img5.douban.com/lpic/s28004598.jpg",
            "http://img5.douban.com/lpic/s28050639.jpg",
            "http://img3.douban.com/lpic/s28056884.jpg",
            "http://img5.douban.com/lpic/s28036379.jpg",
            "http://img5.douban.com/lpic/s28045199.jpg",
            "http://img3.douban.com/lpic/s28034923.jpg",
            "http://img5.douban.com/lpic/s28067039.jpg",
            "http://img3.douban.com/spic/s28000872.jpg",
            "http://img3.douban.com/view/movie_poster_cover/mpst/public/p2237747953.jpg",
            "http://img5.douban.com/view/movie_poster_cover/mpst/public/p2240110789.jpg",
            "http://img5.douban.com/view/movie_poster_cover/mpst/public/p2235609577.jpg",
            "http://img5.douban.com/view/movie_poster_cover/mpst/public/p2233113686.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2225188257.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2201518484.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2229383316.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2222477444.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2230484792.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2204911658.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2210568759.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2201468648.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2226169113.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2221319641.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2231932406.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2208286457.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2203481530.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2232079769.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2224484447.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2233767512.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2202114105.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2209761319.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2230222544.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2220776342.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2233840624.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2194199028.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2230222544.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2220776342.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2233840624.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2194199028.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2233260210.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2210832820.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2203693875.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2231791943.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2203838902.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2220218254.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2215268072.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2219669553.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2208934451.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2225669293.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2225986472.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2199678535.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2230471461.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2204888387.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2224389662.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2207808539.jpg",
            "http://img5.douban.com/view/movie_poster_cover/lpst/public/p2225291257.jpg",
            "http://img3.douban.com/view/movie_poster_cover/lpst/public/p2230505723.jpg"
    };
}
           

ImageLoaderAdapter.java

package com.jackie.photowall;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;

/**
 * Created by Jackie on 5/14/15.
 */
public class ImageLoaderAdapter extends BaseAdapter implements AbsListView.OnScrollListener {

    private Activity mActivity;
    private GridView mGridView;
    private LayoutInflater mInflater;

    /**
     * 圖檔緩存技術的核心類,用于緩存所有下載下傳好的圖檔,在程式記憶體達到設定值時會将最少最近使用的圖檔移除掉。
     */
    private LruCache<String, Bitmap> mMemoryCache;
    /**
     * 記錄所有正在下載下傳或等待下載下傳的任務。
     */
    private Set<ImageLoaderTask> mImageLoaderTasks;
    /**
     * 第一張可見圖檔的下标
     */
    private int mFirstVisibleItem;
    /**
     * 可見區域一屏有多少張圖檔
     */
    private int mVisibleItemCount;
    /**
     * 代表第一次進來不滑動的時候自動加載圖檔
     */
    private boolean mIsFirstLoader = true;


    public ImageLoaderAdapter(GridView mGridView, Activity mActivity) {
        this.mActivity = mActivity;
        this.mGridView = mGridView;
        mInflater = LayoutInflater.from(mActivity);
        mImageLoaderTasks = new HashSet<ImageLoaderTask>();
        /**
         * 擷取應用給app配置設定的記憶體空間
         */
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        /**
         * 給圖檔配置設定合理的内容空間
         */
        int cacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount();
            }
        };
        mGridView.setOnScrollListener(this);
    }

    @Override
    public int getCount() {
        return Constant.imgs != null && Constant.imgs.length > 0 ? Constant.imgs.length : 0;
    }


    @Override
    public Object getItem(int position) {
        return Constant.imgs[position];
    }


    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup arg2) {
        final String url = (String) getItem(position);
        ViewHolder holder = null;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.img_item, null);
            holder = new ViewHolder();
            holder.imageview = (ImageView) convertView.findViewById(R.id.imageview);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        /**
         * 給ImageView設定一個Tag,保證異步加載圖檔時不會亂序
         */
        holder.imageview.setTag(url);
        setImageView(url, holder.imageview);
        return convertView;
    }

    /**
     * 首先用url到緩存中找 如果沒有就顯示預設圖檔  如果緩存中有 就顯示
     *
     * @param url 要顯示圖檔的url
     * @param imageview
     */
    private void setImageView(String url, ImageView imageview) {
        Bitmap bitmap = mMemoryCache.get(url);
        if (bitmap != null && imageview != null) {
            imageview.setImageBitmap(bitmap);
        } else {
//            imageview.setImageResource(R.drawable.empty_photo);
        }
    }

    class ViewHolder {
        ImageView imageview;
    }

    /**
     * view  就是GridView
     * firstVisibleItem 可見 第一個條目
     * visibleItemCount 可見的總條目
     * totalItemCount 要加載的總條目
     */
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mFirstVisibleItem = firstVisibleItem;
        mVisibleItemCount = visibleItemCount;
        if (mIsFirstLoader && visibleItemCount > 0) {
            loadImages(firstVisibleItem, visibleItemCount);
            mIsFirstLoader = false;
        }
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // 僅當GridView靜止時才去下載下傳圖檔,GridView滑動時取消所有正在下載下傳的任務
        if (scrollState == SCROLL_STATE_IDLE) {
            loadImages(mFirstVisibleItem, mVisibleItemCount);
        } else {
            cancelAllTasks();
        }
    }

    private void cancelAllTasks() {
        if (mImageLoaderTasks != null) {
            for (ImageLoaderTask task : mImageLoaderTasks) {
                task.cancel(false);
            }
        }
    }

    private void loadImages(int firstVisibleItem, int visibleItemCount) {
        for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) {
            String imagUrl = Constant.imgs[i];
            Bitmap bitmap = getBitmapFromMemory(imagUrl);
            if (bitmap == null) {//從網絡擷取
                ImageLoaderTask task = new ImageLoaderTask();
                mImageLoaderTasks.add(task);
                task.execute(imagUrl);
            } else {//從緩存擷取
                ImageView imageView = (ImageView) mGridView.findViewWithTag(imagUrl);
                if (imageView != null && bitmap != null) {
                    imageView.setImageBitmap(bitmap);
                }
            }
        }
    }

    private Bitmap getBitmapFromMemory(String key) {
        return mMemoryCache.get(key);
    }

    private class ImageLoaderTask extends AsyncTask<String, Void, Bitmap> {
        String url = null;

        @Override
        protected Bitmap doInBackground(String... param) {
            url = param[0];
            /**
             * 從網絡上加載的圖檔 如果bitmap不為null  就加載到記憶體之中
             */
            Bitmap bitmap = downLoadImage(url);
            if (bitmap != null) {
                // 圖檔下載下傳完成後緩存到LrcCache中
                addBitmapToMemoryCache(url, bitmap);
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            ImageView imageView = (ImageView) mGridView.findViewWithTag(url);
            if (imageView != null && bitmap != null) {
                imageView.setImageBitmap(bitmap);
            }
            /**
             * 下載下傳完了以後把該任務從集合中移除  可能有些人會說為什麼要用hashset集合  因為他可以的任務不重複
             */
            mImageLoaderTasks.remove(this);
        }
    }

    public Bitmap downLoadImage(String imgUrl) {
        Bitmap bitmap = null;
        HttpURLConnection connection = null;
        try {
            URL url = new URL(imgUrl);
            connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(5 * 1000);
            connection.setReadTimeout(10 * 1000);
            connection.setDoInput(true);
            bitmap = BitmapFactory.decodeStream(connection.getInputStream());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
        return bitmap;

    }

    /**
     * 把圖檔加載到記憶體之中
     *
     * @param url    圖檔的url
     * @param bitmap url對應的圖檔
     */
    public void addBitmapToMemoryCache(String url, Bitmap bitmap) {
        if (getBitmapFromMemory(url) == null) {
            mMemoryCache.put(url, bitmap);
        }
    }
}
           

效果圖:

Android照片牆的實作