public Bitmap getRoundedCornerBitmap(Context context, Bitmap bitmap , int roundLevel) { 
	    Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); 
	    Canvas canvas = new Canvas(output); 
	 
	    final int color = 0xff424242; 
	    final Paint paint = new Paint(); 
	    final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); 
	    final RectF rectF = new RectF(rect); 
	    final float roundPx = convertDipsToPixels(context, roundLevel); 
	 
	    paint.setAntiAlias(true); 
	    canvas.drawARGB(0, 0, 0, 0); 
	    paint.setColor(color); 
	    canvas.drawRoundRect(rectF, roundPx, roundPx, paint); 
	 
	    paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); 
	    canvas.drawBitmap(bitmap, rect, rect, paint); 
	 
	    return output; 
	} 
	
	public static int convertDipsToPixels(Context context, int dips) { 
	    final float scale = context.getResources().getDisplayMetrics().density; 
	    return (int) (dips * scale + 0.5f); 
	}

roundLevel을 0~100까지 조절할 수 있다.
이어서...

Layout에 TextView를 추가하겠습니다.
그리드 뷰에서 스크롤이 이동될때 관련 메세지가 찍히도록 하기 위해서 아래처럼 수정합니다.

[File : main.xml]

	
	
 20개의 이미지를 대상으로 했으나 이제 40개로 하겠습니다.

아래 20개의 URL을 url.xml에 추가해줍니다.
[File : url.xml]

	
			   
	   
	   
			   
	
	
	
			   
	
	
	
	
	
	
	
	
	
	
	


그리고 Activity를 조금 손 보겠습니다.

[File : ImageList.java]
package com.yeory.gv;

import java.io.IOException;
import java.util.ArrayList;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.GridView;
import android.widget.TextView;
import android.widget.Toast;

import com.yeory.gv.common.CommonResources;
import com.yeory.gv.model.Image;
import com.yeory.gv.util.CommonUtil;
import com.yeory.gv.util.ImageAdapter;
import com.yeory.gv.util.ImageDownloader;

/**
 * =============================================================================
/            프로젝트명 :   GridView_Example
/            화  일  명 :   ImageList.java
/            기      능 :   
/            인      수 :   
/            특이  사항 :
/-----------------------------------------------------------------------------
/                               변경 사항				                     
/-----------------------------------------------------------------------------
/    변경일자       	변경자(작성자)                 		변경 내역                 
/   ----------     	--------------------------       -------------------------
/   2010. 11. 10.      jYeory<jyeory@gmail.com>         		최 초 작 성                      
/==============================================================================
 */
public class ImageList extends Activity {
	private final ImageDownloader imageDownloader = ImageDownloader.getInstance();
	
	private GridView gridView;
	private TextView textView;
	private ImageAdapter adapter;
	
	private boolean isNextPage = true;
	boolean needCached = true;
	final int size = 15;			// 한번에 보여줄 이미지 개수
	private int totalSize = 0;		// paging에 필요
	private int currentPage = 0;	// paging에 필요
	private int start = 0;			
	private int end = 0;
	private int endPage = 0;
	private int lastPosition = 0;
	private int firstPosition = 0;
	
	private ProgressDialog pd;
	private Thread thread;
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if(keyCode == 4) CommonUtil.getInstance().requestKillProcess(this);
		return super.onKeyDown(keyCode, event);
	}
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        CommonResources.COMMON_CONTEXT = this;
        
        init(1, size);
        
        textView = (TextView) findViewById(R.id.logText);
        
        gridView = (GridView) findViewById(R.id.gridView);
        adapter = new ImageAdapter(this, imageList);
        
        gridView.setOnScrollListener(new OnScrollListener() {
			@Override
			public void onScrollStateChanged(AbsListView view, int scrollState) {
				isNextPage = imageList.size() < totalSize;
				try{
					switch (scrollState) {
					case 1:
						lastPosition = view.getLastVisiblePosition();
						firstPosition = view.getFirstVisiblePosition();
						if( lastPosition == (imageList.size()-1) && isNextPage ){
							pd = ProgressDialog.show(ImageList.this,  null, "데이터 수신중 입니다.\n잠시만 기다려 주세요...", false, true);
							
							thread = new Thread(new Runnable() {
								public void run() {
									start = (++currentPage - 1) * size;
									if(start < totalSize){
										endPage = ((currentPage*size) < totalSize)?(currentPage*size):totalSize;
										imageList.addAll(wholeImageList.subList(start, endPage));
									}
									// 아래 코드를 주석처리할 시 AsyncTask를 확인할 수 있다.
//									downloadImages(imageList);
									handler.sendEmptyMessage(0);
								}
							});
							thread.start();
						}
						break;
					default:
						break;
					}
				}catch(Exception e){
					Log.i("error", "gridView.setOnScrollListener : "+e.getCause().toString());
				}
			}
			
//			firstVisibleItem : 화면 최상단 이미지의 IDX
//			visibleItemCount : 현재 화면에 보여지는 이미지 개수
			@Override
			public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
				textView.setText("onScroll : "+"view.getLastVisiblePosition() - "+view.getLastVisiblePosition()+" / "+totalItemCount);
			}
		});
        
        gridView.setAdapter(adapter);
        
        Toast.makeText(this, "Yeory - www.yeroy.com", Toast.LENGTH_LONG).show();
    }
    
    /**
	 * ProgressDialog 표시 중 해당 이미지들을 다운받는다.
	 * AsyncTask를 사용할 경우 호출할 필요 없음. 
	 * @param images
	 */
	private void downloadImages(ArrayList images){
		for(Image image : images){
			imageDownloader.callDownloadImage(image.getThumbNail());
		}
	}
	
	private Handler handler= new Handler() {
		public void handleMessage(Message msg) {
			if(adapter == null) adapter = new ImageAdapter(ImageList.this, imageList);
			else{
				adapter.setList(imageList);
			}
			gridView.setAdapter(adapter);
			
			gridView.setSelection(firstPosition);
			pd.dismiss();
		}
	};
    
    private ArrayList wholeImageList = new ArrayList();	// 전체 이미지 담을 것.
	private ArrayList imageList = new ArrayList();	// 부분 이미지 담을 것.
	private XmlPullParser xpp;
	
    /**
	 * XML을 읽어들여 전체 이미지를 ArrayList에 넣는다.
	 * 처음으로 호출 되기에 15개만 다른 list에 담아 캐싱. 
	 * @param page
	 * @param size
	 */
	private void init(final int page, final int size) {
		xpp = getResources().getXml(R.xml.url);
		try{
			while ( xpp.getEventType() != XmlPullParser.END_DOCUMENT ){  // 마지막 문서까지
				if ( xpp.getEventType() == XmlPullParser.START_TAG ){
					if ( xpp.getName().equals("image") ){
						wholeImageList.add(new Image(xpp.getAttributeValue(0), xpp.getAttributeValue(1), xpp.getAttributeValue(2)));
					}
				}
				xpp.next();
			}
			
			totalSize = wholeImageList.size();
			currentPage = page;

			start = (page - 1) * size; 
			end = (page*size);
			imageList.addAll(wholeImageList.subList(start, end));
			
		}catch(XmlPullParserException xppe){
			Log.i("error", "init()"+xppe);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

새롭게 그리드 뷰 스크롤에 관련된 이벤트가 추가되었습니다.
또한 이미지 캐시 기능을 제공하는 클래스인 ImageDownloader도 추가되어있습니다.

먼저 ImageDownloader는 아래 주소에 번역된 글이 있으니 참조 바랍니다.
번역 : 
http://huewu.blog.me/110090363656
원본 : http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html

저는 위 코드를 입맛에 맞게 뜯어고쳐(?)서 사용하고 있습니다.

ImageDownloader.java의 코드는 너무 길어 적지 않고 글의 흐름상 필요한 메서드만 기술합니다.

Activity에서 GridView의 adapter를 set해주는 곳이 있었는데 실제 그 부분은 ImageAdapter.java 였습니다.
캐시 기능을 위해 아래처럼 변경되었습니다.

[File : ImageAdapter.java]
package com.yeory.gv.util;

import java.util.ArrayList;

import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;

import com.yeory.gv.model.Image;
import com.yeory.gv.util.ImageDownloader.Mode;
/**
 * =============================================================================
/            프로젝트명 :   GridView_Example
/            화  일  명 :   ImageAdapter.java
/            기      능 :   
/            인      수 :   
/            특이  사항 :
/-----------------------------------------------------------------------------
/                               변경 사항				                     
/-----------------------------------------------------------------------------
/    변경일자       	변경자(작성자)                 		변경 내역                 
/   ----------     	--------------------------       -------------------------
/   2010. 11. 10.      jYeory<jyeory@gmail.com>         		최 초 작 성                      
/==============================================================================
 */
public class ImageAdapter extends BaseAdapter{
	private final ImageDownloader imageDownloader = ImageDownloader.getInstance();
	private final String LOG_TAG = "ImageAdapter";
	private Context mContext;
    private ImageView imageView;
    private ArrayList list;
    
    public ImageAdapter(Context c) {
        mContext = c;
    }
    
    public ImageAdapter(Context c, ArrayList list){
    	mContext = c;
    	this.setList(list);
    }
	
	
	@Override
	public int getCount() {
		Log.i(LOG_TAG, "called getCount()");
		return getList().size();
	}

	@Override
	public Object getItem(int position) {
		Log.i(LOG_TAG, "called getItem()");
		return null;
	}

	@Override
	public long getItemId(int position) {
		Log.i(LOG_TAG, "called getItemId()");
		return 0;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		Log.i(LOG_TAG, "called getView()");
		
		if (convertView == null) {  // if it's not recycled, initialize some attributes.
    		imageView = new ImageView(mContext);
    		imageView.setLayoutParams(new GridView.LayoutParams(150, 150));
    		imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
    		imageView.setPadding(4, 4, 4, 4);
        } else {
            imageView = (ImageView) convertView;
        }
		
		imageDownloader.setMode(Mode.CORRECT);
		imageDownloader.download(getList().get(position).getThumbNail(), imageView);
		return imageView;
	}

	public void setList(ArrayList list) {
		this.list = list;
	}

	public ArrayList getList() {
		return list;
	}
}
코드에 살펴보시면 Mode.CORRECT 가 있는데 이 코드가 Async로 할 것인지에 대한 값입니다.

CORRECT로 할 경우 각 이미지를 개별로 다운받아 바로 사용자에게 표시합니다.
(주의 : ImageList.java에서 PrgoressDialog 표시 중일때 downloadImages(imageList); 를 주석처리 하여야 합니다.)

즉 CORRECT로 한다면 20개의 이미지를 먼저 다운로드 되는 것부터 표시할 수 있고,
주석처리할 경우 20개의 이미지가 모두 다운로드 된 후에야 사용자에게 표시하는 차이가 있습니다.

코드가 너무 많아져서 다 올리지 못하여 역시나 소스를 첨부합니다.
캐시 기능을 하는 것에 대해서는 해당 링크를 참조하시면 될 듯합니다.




다음은 그리드 뷰에서 아이템 클릭시 크게 보는 것과 드래그입니다...

  1. 2011.03.11 10:26

    비밀댓글입니다

  2. 안드로메이드 2012.02.15 11:47 신고

    그리드뷰를 어떻게 페이징으로 구현 할지 몰랐는데
    도움이 많이 되었습니다
    감사합니다 ^^

  3. 안드로이드 2012.10.17 15:55 신고

    소스다운받아서 올리면 앱이 죽네요 ㅠ

  4. 마앙 2012.11.12 13:19 신고

    썸네일 클릭시 확대하는건 언제 수정하시나요 ㅜㅠ? 제가 필요해서,....
    거기에 뷰플리퍼까지 넣는 기능을 넣어야 되는데 힘드네요

  5. 감사합니다 2013.07.06 20:01 신고

    이런 소스를 공개하긴 쉽지않을텐데. 덕분에 처리할 수 있을 것 같습니다.
    감사합니다.

    • 이직과 업무 변경으로 현재 포스트 이후 써내려 가야할 소스가 사라져 더이상 포스팅을 못하는게 아쉽네요..

      도움 되셨다면 그걸로 만족입니다.

  6. 감사합니다 2013.07.14 17:16 신고

    그런데...제가 한 100여개의 인터넷 url 을 받아와서 뿌려주고있는데요

    스크롤을 막내리다보면 죽어버리는 경우가 있습니다.(과격하게 사용하는경우)
    어찌해야할까요..........................ㅠㅠ

    현재 100장이미지인데 페이지를 100장을 초기값 잡았습니다.
    그래서 스크롤 없이 한번에 다보여준식으로....일단 이렇게 쓰면될거같긴한데

    혹시 페이지넘길때 생기는방식(카카오 스토리와같이)
    이 안정적으로 되는 추가방법 생각나시면 댓글 부탁드려요 (__*

    • 캐시를 이용하시나요??
      제 경우엔 Web 이미지를 표시를 해야할 때 캐시가 필수라 생각했었어요.

      1 ~ 20 까지 처음이고 스크롤을 내릴 시 5개씩의 이미지를 추가로 로드한다고 할 때,

      6 ~ 25 까지 이미지를 표시하고 (스크롤을 한번 내린 상태)
      기존 1 ~5의 이미지를 다시 보고자 스크롤을 올릴때 캐시를 이용하지 않으면 또 URL을 이용해서 이미지를 가지고 왔었습니다.
      이게 계속적으로 반복이 되면 메모리 부족으로 앱이 사망했구요.

      캐시를 이용할 경우로 500장까지 스크롤 테스트 했지만 문제 없었고...
      리스트에 표시되는 이미지도 미리 로컬로 다운로드 된 이미지를 가지고 오는 것이기에 즉각적으로 표시 되었구요.

  7. 감사합니다 2013.07.16 15:52 신고

    올려주신 소스가 캐시 사용을 기본으로 하는게 아닌가요?
    지금 경우 100장을 기준으로 한다고해도 3G 환경에서 실험결과,
    한 페이지내에 그림이 몇개 뜨다가 제가 스크롤 내렸다가 다시 돌아오면 전부 다시 로딩하는거같던데요....

    주석처리하는부분이 캐싱인가요?

    • 기본적으로 캐시를 합니다.
      다운로드 디렉토리만 정상적이라면 거기에 이미지를 넣어두고 가져오니깐요..

      제가 테스트 한 결과는
      첫 20여개의 이미지 로딩 완료 되고 스크롤 이동 후 다시 표시하고자 할때는 캐시된 이미지가 나왔기 때문에 바로 화면에 표시가 되었구요.

      주석처리 하는 부분은 화면상의 이미지를 한꺼번에 다 보여줄것이냐,
      다운로드 완료 된 것부터 보여줄 것이냐 하는 부분이기 때문에 해당 부분은 아닌 것 같네요..

      이미지도 썸네일을 불러오시는거라면.. 제 코드에 문제가 있을거라 보여지네요.

+ Recent posts