안드로이드 개발 질문/답변
(글 수 45,052)
우선 제가 지금까지 조사하고 삽질(?)해보면서 알아난 부분까지 적어보겠습니다.
remoteViews에서 imageView를 설정하는 메소드(?)는
setImageViewResource()
setImageViewBitmap()
setImageViewUri()
가 있는데, setImageViewResource()는 어플에 저장되어 있는 파일들(R.drawable...등에 등록되는 파일들)을 가져오는 명령이니 패스하고,
setImageViewBitmap()의 경우, 작은 사이즈(width, height)의 bitmap은 제대로 표시가 되지만 큰 사이즈(4x4cell 이상, 320dip x 400dip 이상?)의 이미지는 감당을 못하고 outOfMemory 에러를 출력하면서 이미지가 안뜨게 되더군요.
그래서 setImageViewUri()에 대해 조사해보았습니다. 로컬이나 웹 페이지에 저장되어 있는 경로에서 파일을 불러오는 방식이더군요. 일단 불러오기 위해선 파일이 있어야 되니 bitmap을 이미지 파일로 저장해야 되는데 일반적인 파일 입출력 방식으로 저장하면 퍼미션이 -rw-------로 설정되어서 setImageViewUri()에서 불러올때 권한 오류가 나더군요(permission denied)
그렇기때문에 context.openFileOutput(fileName, Context.MODE_WORLD_READABLE)을 써서 저장해야 합니다. 이걸로 저장하면 퍼미션이 -rw-rw-r--로 된 파일이 data/data/프로젝트명/files폴더에 저장됩니다.(다른 어플들에서도 이 방식을 쓰는걸까 하고, jorte와 calendar pad에서 4x4 캘린더 위젯을 띄우고 확인해보니 files폴더에 -rw-rw-r--퍼미션으로 저장된 달력 이미지 파일이 생기더군요.)
그리고 이렇게 저장한 파일을 setImageViewUri()에서 불러오면 위젯이 뜨게됩니다. 하지만! 왠일인지 실제로 설정한 사이즈보다 1.5배 작은 사이즈로 출력이 되더군요.(예를 들어서 480px x 600px로 저장한 이미지면 320px x 400px 사이즈로 뜨게 됩니다.) 이걸 보정하기 위해 실제 사이즈를 아예 가로 세로 1.5씩 곱한 720px x 900px로 줘보니 위젯 크기 자체는 의도한 크기로 설정되긴 하지만 큰 사이즈의 이미지를 압축해서 보여주기 때문에 깨지는 부분들이 생기더군요.. 그래서 아예 강제적으로 layout xml의 imageView 사이즈를 강제로 320dip x 400dip로 줘보니 아주 깨끗하게 잘나오더군요. 근데 문제는 기기마다 홈스크린 위젯의 총 px크기뿐만 아니라 dip사이즈까지도 다르게 설정되어 있는 기기들이 많아서 이쪽 기기에선 꺠끗하게 나와도 다른 기기에선 사이즈가 작게 보인다던가 하는 문제가 생깁니다.(4x4사이즈면 화면에 꽉차서 보여야 되는데 총 dip값이 더 큰 기기에선 여백이 많이 생기고, dip값이 작은 기기면 너무 커서 이미지가 짤려져서 보이게 됩니다.) 아니면 createBitmap으로 만들때 화면 크기를 계산해서 width값이랑 height값이 가변적으로 설정되게 만들수는 있지만 이번엔 xml파일에 설정한걸 수정 못하는 문제가 생기죠..
가장 깔끔한 방법은 일단 xml에서 imageView와 layout의 가로 세로 설정을 fill_parent로 맞춰놓고(fill_parent로 설정하면 이미지가 축소되서 출력은 안됩니다. 다만 이미지가 정확하게 imageView의 가로 세로 사이즈와 같지 않기 때문에 imageView에 맞춰서 확대한 뭉개진 이미지가 보일 뿐이죠..) 홈스크린에 띄워지는 widget의 imageView size(width, height)를 가져온 뒤 그리는 방식일것 같은데, 도저히 appwidget내의 imageView width height값을 가져오는 방법을 모르겠습니다.. 보통 .9. 방식의 margin이 있는 백그라운드를 쓰기 때문에 거기에 맞춰서 보정된 measuredWidth와 measuredHeight값이 필요하겠죠 ㅋ calendar pad에서 생성한 이미지 파일을 보니 기기에 따라 정확하게 계산된(백그라운드 이미지의 margin값 까지 고려된) width height값을 가지는 걸로 봐선 이 값들을 구하는 방법이 있을것 같은데 도저히 모르겠네요..
아래는 제가 지금까지 삽질하며 만든 소스코드입니다 ㅋ(위에서 설명한 대로 위젯이 1.5배씩 줄어든 사이즈로 출력됩니다.)
아 참고로 위에서 Uri 선언할때 fromFile로 이미지를 불러오면 디자이어에선 축소되지 않고 정상적인 크기로 위젯이 출력되지만 x10이나 넥원, 겔럭시S등에선 축소된 이미지로 출력됩니다 --;
public class WidgetSheet extends AppWidgetProvider{ @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { context.startService(new Intent(context, UpdateService.class)); }
public static class UpdateService extends Service {
RemoteViews views;
@Override public void onStart(Intent intent, int startId) { RemoteViews updateViews = buildUpdate(this);
ComponentName thisWidget = new ComponentName(this, WidgetSheet.class); AppWidgetManager manager = AppWidgetManager.getInstance(this); manager.updateAppWidget(thisWidget, updateViews); }
public void updateWidget(Context context) { RemoteViews updateViews = buildUpdate(context);
ComponentName thisWidget = new ComponentName(context, WidgetSheet.class); AppWidgetManager manager = AppWidgetManager.getInstance(context); manager.updateAppWidget(thisWidget, updateViews); }
@Override public IBinder onBind(Intent intent) { return null; }
@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { updateWidget(this); } else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { updateWidget(this); } }
public RemoteViews buildUpdate(Context context) { .... RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_calendar_transparent);
display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); DisplayMetrics metrics = new DisplayMetrics(); display.getMetrics(metrics); // cell size of a widget in portrait mode int xCellSize = 80; int yCellSize = 100;
if (metrics.widthPixels > metrics.heightPixels) { // this means we're in landscape mode, so the cell sizes are adjusted xCellSize = 106; yCellSize = 74; } int xWidgetSize = 4; int yWidgetSize = 4; int width; int height;
Log.i("si", String.format("your %d x %d appwidget is %d x %d physical pixels.", xWidgetSize, yWidgetSize, (int)(xCellSize * xWidgetSize * metrics.density), (int)(yCellSize * yWidgetSize * metrics.density))); if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { width = (int)(xCellSize * xWidgetSize * metrics.density); height = (int)(yCellSize * yWidgetSize * metrics.density); } else { width = (int)(xCellSize * xWidgetSize * metrics.density); height = (int)(yCellSize * yWidgetSize * metrics.density); }
b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); canvas = new Canvas(); canvas.setBitmap(b);
DrawableSheet dsView = new DrawableSheet(context); .... ....
dsView.setBounds(0, 0, width, height); dsView.draw(canvas);
String fileName = c.getTimeInMillis() + ".png";
File file = context.getFilesDir();
// 이전에 저장했던 sheet이미지 삭제 File deleteFiles[] = file.listFiles(); for(int i = 0; i < deleteFiles.length; i++) deleteFiles[i].delete();
try { FileOutputStream filestream = context.openFileOutput(fileName, Context.MODE_WORLD_READABLE);//new FileOutputStream(file); b.compress(CompressFormat.PNG, 100, filestream); filestream.close(); } catch (FileNotFoundException e){ e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
file = context.getFileStreamPath(fileName);
Uri uri = Uri.parse(file.getAbsolutePath()); //Uri.fromFile(file);
Log.i("si", uri.toString());
views.setImageViewUri(R.id.imageViewCalendar, uri);
Log.i("si", width + " " + height+ "");
Intent intent = new Intent(context, main.class);
PendingIntent pendingIntentGoActivity = PendingIntent.getActivity( context, 1, intent, 0);
views.setOnClickPendingIntent(R.id.LinearLayoutParent, pendingIntentGoActivity);
return views; } } }
아 참고로 위에서 Uri 선언할때 fromFile로 이미지를 불러오면 디자이어에선 축소되지 않고 정상적인 크기로 위젯이 출력되지만 x10이나 넥원, 겔럭시S등에선 축소된 이미지로 출력됩니다 --;
글이 심하게 길어졌네요; 결론은 appWidget의 imageView width height값을 가져올 방법을 알고 싶습니다 ㅋ