소스코드는 http://www.inter-fuser.com/ 을 참고하세요.
해당 소스에서 몇가지만 최적화 해주시면 될 듯 합니다.
우선 이미지가 큰 경우 out of memory가 발생 할 수 있습니다. 따라서 화면에 표시할 비트맵은
원본을 그대로 사용하지 않고, 해당 영역에 맞게 축소해 사용합니다.
(이 내용은 이 포스트에 포함되어 있습니다.)
그리고, Adapter의 getView() 에서 convertView가 정상적으로 들어오지 않습니다.
이는 안드로이드의 버그라고 알고 있으며, 이를 해결하기 위해 별도의 구조로
이미지뷰를 만들어 사용하셔야 합니다.
버그리포트 : http://code.google.com/p/android/issues/detail?id=3376
Gallery를 상속받은 객체 구현시 getChildStaticTransformation 을 오버라이드해 구현하게 되는데,
이 메쏘드는 주기적으로 계속 호출되게 됩니다.
이는 cpu 부하를 높여 다른 액티비티와 문제가 발생할 여지가 있습니다.
기존 포지션을 저장해 두고 변경이 없는 상황에는 처리하지 않도록 구현되어야 합니다.
갤러리 객체를 사용해 나만의 갤러리(커버플로우) 구현하기
- AudioGalleryAdapter -
아이팟터치나 아이폰의 음악 재생기를 보면 가로화면의 경우
커버플로우를 지원하고 있다. (올해 봄 관련 디자인으로 미국 특허 등록됨.)
안드로이드에서 이를 구현하려면 어찌해야 할까? 특허 이야긴 일단 빼고 -_-;;
성능적인 문제로 opengl을 사용하는게 가장 빠르다.
하지만 처리해야할게 많아지는 단점이 있다.
안드로이드는 이러한 썸네일을 표시하기 위해 Gallery 라는 클래스를 제공한다.
하지만 커버 플로우라기 보다는 썸네일 리스트와 같은 느낌이 강하다.
이 Gallery 를 사용해 나만의 커버 플로우를 만들어 보자.
1. Adapter
Gallery는 각 이미지를 로드하기 위한 adapter가 필요하다.
public class AudioGalleryAdapter extends BaseAdapter {
public static int COVER_WIDTH = 120;
public static int COVER_HEIGHT = 120;
public static int COVER_REFLECT = 60;
int mGalleryItemBackground;
private Context mContext;
public AudioGalleryAdapter( Context c) {
mContext = c;
}
BaseAdapter는 Gallery 객체가 이 녀석한테 뷰를 달라기도 하고,
아이템이 몇개인지 묻기도 하는 녀석이다. 리스트에서 많이 사용해 본 녀석이기에 자세한
설명은 제외하고, getView() 가 모든 작업의 핵심이다.
따라서 관련 메쏘드를 오버라이드해 구현해야 한다.
@Override
public int getCount() {
return mCount; // 표시될 아이템의 갯수를 리턴해 준다.
}
@Override
public Object getItem(int position) {
// 해당 아이템에 특정 object 값을 넣어 리턴할 수 있는데,
// 지금은 그냥 리턴.
return position;
}
@Override
public long getItemId(int position) {
return position; // id 도 일단 사용하지 않으니 걍 리턴
}
// 이 메쏘드가 각 이미지를 가지고 오는 녀석으로
// 갤러리는 이녀석이 던져주는 ImageView를 가지고 갤러리 화면을
// 구성하게 된다.
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if( convertView == null )
{
converView = new ImageView( mContext );
converView.setLayoutParams( new ViewGroup.LayoutParams ....);
}
createMyGalleryImage( (ImageView)convertView, position);
return convertView;
}
위에 보면 createMyGalleryImage라는 메쏘드를 통해 이미지뷰를 가져와 리턴하고 있다.
해당 메쏘드를 구현한다.
BaseAdapter 이므로, getView 만 존재하는데, convertView에 사용될 레이아웃이 별도 없으므로,
ImageView로 생성한다.( 물론 레이아웃을 만들어도 된다.)
원래는 이렇게 구현하면, 일반적인 adapter들 처럼 view를 공유해 사용하게 됩니다.
하지만 위에 언급한 버그로 인해 imageview가 무한정 생성됩니다.
이는 별도의 자료구조로 구현하고, 해당 뷰를 사용하는 형태로 수정해야 합니다.
저의 경우는 hashmap에 화면에 보이는 항목 수만큼 imageview를 만들어 두고, 포지션에 따라 돌아가며 사용합니다.( position%항목수 )
각 포지션별로 파일 path 정보는 별도로 저장되어 해둔다.
비트맵이 큰 경우 out of memory 가 나타날 수 있으니, 추후 BitmapFactory.Options 를 사용해
크기를 조절해 줘야 함.
public int COVER_WIDTH = 96;
public int COVER_HEIGHT = 96;
public void createMyGalleryImage(ImageView v, int pos) {
// 비트맵 읽기(pos에 따른 path 정보는 미리 지정해 둘것..)
Bitmap srcImage = BitmapFactory.decodeFile( path );
int width = srcImage.getWidth();
int height = srcImage.getHeight();
// 원본 이미지가 표시될 이미지보다 작은 경우 원본이미지를 그대로 사용
if( srcImage.getWidth() < COVER_WIDTH) width = srcImage.getWidth();
if( srcImage.getHeight() < COVER_HEIGHT) height = srcImage.getHeight();
// 이미지의 크기 조정
Bitmap workImage = Bitmap.createScaledBitmap( original, width, height,rue);
// out of memory를 방지하기 위해 원본이미지는 제거
original.recycle();
original = null;
// 스케일 조정을 위한 행렬설정(상하 반전)
Matrix matrix = new Matrix();
matrix.preScale(1,-1);
// 반사 이미지를 위한 비트맵(너비는 같고, 높이만 절반)
Bitmap reflectionImage = Bitmap.createBitmap(workImage, 0, height/2, width, height/2, matrix, false);
// 이미지를 그리기위한 백버퍼 비트맵(원본이미지 + 반사이미지)
Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + height/2), Config.ARGB_8888);
// 백버퍼 비트맵으로 캔버스 생성
Canvas canvas = new Canvas(bitmapWithReflection);
// 원본 이미지를 그림
canvas.drawBitmap(workImage, 0, 0, null);
// 페인트 객체 생성
Paint defaultPaint = new Paint();
// 반사 이미지가 들어갈 영역 그림
canvas.drawRect(0, height, width, height + reflectionGap, defaultPaint);
// 반사 이미지 그림
canvas.drawBitmap( reflectionImage, 0, height + reflectionGap , null);
// 반사 이미지의 그라데이션을 위한 설정
Paint paint = new Paint();
LinearGradient shader = new LinearGradient( 0, originalImage.getHeight(), 0,
bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff, 0x00ffffff,
TileMode.CLAMP);
paint.setShader(shader);
// 픽셀 전송모드 변경
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
// 캔버스에 그리기
canvas.drawRect(0, height, width, bitmapWithReflection.getHeight() + reflectionGap, paint );
// 그려진 백버퍼를 ImageView로 변환
v.setImageBitmap(bitmapWithReflection);
v.setScaleType(ScaleType.MATRIX);
workImage.recycle();
reflectionImage.recycle();
}
애플의 그것과 같은 효과를 나타내기 위해 이미지 하단에 반사 이미지와 같은 효과가
나타나도록 했다. 이러면 일단 adapter의 작성은 완료된다.
이미지의 경우 생성 루틴을 AsyncTask 및 SoftReference 혹은 캐쉬파일로 백그라운드로 구성해야
부드러운 동작을 이끌어낼 수 있다.
이제 이 이미지를 가져와 에니메이션 하는 Gallery 객체를 작성해야 한다.
갤러리 객체의 구현은 별도로 포스팅하지 않음. 단, 몇가지 필요한 기능에 대해서만 기술함.
public MyGallery extends Gallery {
.
.
}
>> 이미지 기본 간격
myGallery.setSpacing( 간격 ) ;
>> 한장씩 넘기기 :
갤러리 아이템은 onFling 이벤트에 따라 스크롤 처리가 이루어진다.
만약 한장씩 넘겨야 하는 경우 지금의 fling 이벤트로는 문제가 있는데, 아래와 같이 간단히 처리하는 방법이 있어 소개한다.
갤러리가 좌우로 KeyEvent.KEYCODE_DPAD_LEFT , KeyEvent.KEYCODE_DPAD_RIGHT 에 반응 한다는 것을 이용한 방법이다.. ^^
출처 : http://stackoverflow.com/questions/2373617/how-to-stop-scrolling-in-a-gallery-widget
private boolean isScrollingLeft(MotionEvent e1, MotionEvent e2){
return e2.getX() > e1.getX();
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY){
int kEvent;
if(isScrollingLeft(e1, e2)){ //Check if scrolling left
kEvent = KeyEvent.KEYCODE_DPAD_LEFT;
}
else{ //Otherwise scrolling right
kEvent = KeyEvent.KEYCODE_DPAD_RIGHT;
}
onKeyDown(kEvent, null);
return true;
}
'프로그래밍 > Android' 카테고리의 다른 글
ffmpeg 라이브러리 빌드 #1 (0) | 2011.06.30 |
---|---|
Drag&drop listview (0) | 2010.10.06 |
뷰 전환시 3차원 에니메이션 적용하기 (0) | 2010.09.17 |
안드로이드 위젯 배경 이미지, 나인패치 (0) | 2010.08.17 |
옵션메뉴 배경 변경 (0) | 2010.08.10 |
AppWidget 여러 개의 위젯을 가진 앱위젯 (0) | 2010.07.09 |
OpenGL 사용하기 (0) | 2010.06.27 |
소켓통신 기본사항 (0) | 2010.06.11 |
OpenCore Codec 연동 (0) | 2010.03.27 |
GDB 기본환경 (0) | 2010.03.22 |