WeakReference
용어그대로 약한 레퍼런스이다. 메모리관리를 위해 고안된것으로 레퍼런스를 WeakReference로 하면 gc에 해당 레퍼런스가 관여되지 않는다. 즉 어떤 객체에 대한 참조가 WeakReference 밖에 남아있지 않다면 그 객체는 gc(Garbage Collection) 의 대상이 된다.
private WeakReference<Launcher> mLauncher; // 변수선언
void setLauncher(Launcher launcher) {
mLauncher = new WeakReference<Launcher>(launcher); // 참조를 WeakReference로 저장함. } if (mLauncher != null) { final Launcher launcher = mLauncher.get(); // 사용시에는 get을 호출하여 null을 반드시 체크하여 사용 if (launcher != null) { launcher.loadWallpaper(); } } SoftReference
WeakReference 보다는 약간 강한 참조이다. Soft reference 만 남아있는 객체는 메모리의 요청에 의한 gc 의 대상이 된다. 메모리의 남은 공간이 넉넉하다면 gc되지 않을 것이며, 메모리의 남은 공간이 얼마 없거나 하다면 gc 의 대상이 될 것이다. 확실한건 vm 이 OutOfMemoryError 를 발생시키기 전에는 Soft reference 만 남아있는 객체가 gc의 대상이 된다는 점 정도고, 그 이외에는 vm 의 구현에 따라 다를 수 있다. |
Programming/Android
- WeakReference 2011.04.21
- Listener 버튼 클릭 리스너 방법 3가지 2011.04.21
- Customizing Android ListView Colors 2011.04.14
- Android Multithreading For Performance 2011.04.12
- URL 에 있는 이미지 출력하기 2011.04.08
- 커스텀 뷰 만들기 2011.04.06
- 무한대 Scroll 되는 Gallery 만들기 2011.03.30
- Splash Screen 만들기 2011.03.30
- Android Coverflow Widget 2011.03.16
- 안드로이드 이미지 물에 비친 효과내기 2011.03.16
WeakReference
Listener 버튼 클릭 리스너 방법 3가지
코딩을 하다보면 같은 버튼 클릭에 관한 것이지만 사람에 따라 스타일이 천차 만별인 것을 알 수 있습니다.
저도 처음 공부할때 너무 헷갈려서 정리를 하는데 한참 헤멨네요.
이번에는 가장 빈번하게 쓰이는 버튼 클릭에 대한 것을 정리해 보겠습니다.
마지막에 조금이라도 메모리를 줄일 수 있는 팁도 있으니 기대해 주세요.
먼저 아래와 같은 main.xml의 UI정의가 있습니다.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <Button android:id="@+id/btnOK" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="OK" /> </LinearLayout>
여기서 이 버튼의 클릭시 함수에 대한 실행 방법은 크게 아래와 같이 3가지가 있겠습니다 .
1. 이름 무지정 클래스
2. 이름 지정 클래스
3. activity 실행
먼저 이름 무지정 클래스 실행 코드를 보겠습니다.
public class MainActivity extends Activity { private Button btnOK; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); btnOK = (Button)findViewById(R.id.btnOK); btnOK.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "Hello!", Toast.LENGTH_LONG).show(); } }); } }
아무튼 이 방식의 장점은 아래와 같습니다.
● 버튼에 대한 구현을 개별적으로 할수 있다.
단점으로는
● 코드가 길어진다.
● Context를 취득하기 위해서 일일이 MainActivity.this를 입력해야 한다.
다음으로는 이름 지정 클래스를 보겠습니다.
public class MainActivity extends Activity { private Button btnOK; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); btnOK = (Button) findViewById(R.id.btnOK); btnOK.setOnClickListener(new BtnOKOnClick(this)); } } class BtnOKOnClick implements OnClickListener { private Context mContext; public BtnOKOnClick(Context context) { mContext = context; } @Override public void onClick(View v) { Toast.makeText(mContext, "Hello!", Toast.LENGTH_LONG).show(); } }
이 스타일의 장점은 아래와 같습니다.
● 코드가 깔끔하다.
● 버튼에 대한 구현을 개별적으로 할수 있다.
반대로 단점은 아래와 같습니다.
● 클래스가 늘어 난다.
● 버튼 클릭에 대한 activity에 대한 수정을 할때 조금 귀찮음. 즉 이래저래 손이 간다.
마지막으로 Activity의 실행에 대하여 알아 보도록 하겠습니다.
public class MainActivity extends Activity implements OnClickListener { private Button btnOK; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); btnOK = (Button) findViewById(R.id.btnOK); btnOK.setOnClickListener(this); } @Override public void onClick(View v) { switch (R.id.btnOK) { case R.id.btnOK: Toast.makeText(this, "Hello!", Toast.LENGTH_LONG).show(); break; } } }
이 코딩 스타일도 요즘에는 많이 볼 수 있습니다.
장점으로는
● 자바의 코딩 스타일과 비슷하다.
● 깔끔해서 관리, 수정하기에 편하다.
단점으로는
● 코드가 길어지는 편이다.
간단하게 각 스타일 별로 정리를 해봤습니다.
어떤 스타일을 이용하시느냐는 개인 취향에 달렸지만 저는 마지막 방식을 추천하겠습니다.
왜냐하면 마지막 방식은 코드도 깔끔하거니와 클래스를 하나만 생성하고 있습니다.
의외로 신경 안쓰시는 분들이 많으신데 스마트 폰과 같이 하드웨어 스펙이 제한되어 있는 기기에서 메모리 관리는 매우 중요한 것입니다. 아래의 내용을 항상 기억하고 계시길 바랍니다.
안드로이드에서는 새로운 클래스를 생성할때마다 1KB의 메모리를 소비합니다.
Customizing Android ListView Colors
Customizing Android ListView Colors
Below are the details for the resources required to accomplish this. These resources will give your list items a white background with a highlighted color of blue and a clicked of green.
I've highlighted the key attributes in bold. These are the attributes that differ from your default list.
File: res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView
android:id="@android:id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:listSelector="@android:color/transparent"
/>
<TextView
android:id="@android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/empty"
/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:padding="10sp"
android:background="@drawable/item_selector">
<CheckBox
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
/>
<TextView
android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textStyle="bold"
android:padding="7dip"
android:textSize="18sp"
android:layout_toRightOf="@id/checkbox"
android:textColor="@color/black"
/>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="@color/green" />
<item
android:state_selected="true"
android:drawable="@color/blue" />
<item
android:drawable="@color/white" />
</selector>
File: res/values/colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="red">#ff00</color>
<color name="green">#f0f0</color>
<color name="blue">#f00f</color>
<color name="white">#ffff</color>
<color name="black">#f000</color>
</resources>
Android Multithreading For Performance

static Bitmap downloadBitmap(String url) {
final AndroidHttpClient client = 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();
final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap;
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
// Could provide a more explicit error message for IOException or IllegalStateException
getRequest.abort();
Log.w("ImageDownloader", "Error while retrieving bitmap from " + url, e.toString());
} finally {
if (client != null) {
client.close();
}
}
return null;
}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) {
int byte = read();
if (byte < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}public class ImageDownloader {
public void download(String url, ImageView imageView) {
BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
task.execute(url);
}
}
/* class BitmapDownloaderTask, see below */
}class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private String url;
private final WeakReference<ImageView> imageViewReference;
public BitmapDownloaderTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
@Override
// Actual download method, run in the task thread
protected Bitmap doInBackground(String... params) {
// params comes from the execute() call: params[0] is the url.
return downloadBitmap(params[0]);
}
@Override
// Once the image is downloaded, associates it to the imageView
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}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 download(String url, ImageView imageView) {
if (cancelPotentialDownload(url, imageView)) {
BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
imageView.setImageDrawable(downloadedDrawable);
task.execute(url, cookie);
}
}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;
}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;
}if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
// Change bitmap only if this process is still associated with it
if (this == bitmapDownloaderTask) {
imageView.setImageBitmap(bitmap);
}
}
URL 에 있는 이미지 출력하기
커스텀 뷰 만들기
What I've done is create an activity that contains my custom view already in the xml file, and then programmatically create a new one and add it to the layout. What I find is the one that is made in the xml has the proper styling, and the one I create programmatically doesn't.
The difference between the two as far as I can tell is that the system uses theCustomLayout1(Context context, AttributeSet attrs) constructor. The problem is I can't figure out how to get the AttributeSet for the application to pass to this custom view when I create it programmatically.
Here's the Activity:
import android.app.Activity; import android.os.Bundle; import android.widget.LinearLayout;
public class ThemeOne extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout layout = (LinearLayout) findViewById(R.id.mainlayout);
layout.addView(new CustomLayout1(getApplicationContext()));
}
}
Here's the main xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:id="@+id/mainlayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.clearsync.test.theme1.CustomLayout1 android:id="@+id/maincustom"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
The custom view class:
import com.clearsync.test.theme1.R;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
public class CustomLayout1 extends LinearLayout {
private Context context = null;
public CustomLayout1(Context context) {
super(context);
this.context = context;
create();
}
public CustomLayout1(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
create();
}
private void create(){
LayoutInflater layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layoutInflater.inflate(R.layout.inflateme, this, true);
}
}
and finally, the custom view xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Oh, Hewroh..."
style="?textview_style1" />
</LinearLayout>
무한대 Scroll 되는 Gallery 만들기
Gallery에 Item 들을 추가하게 되면 0번 인덱스나 마지막 인덱스로 아이템들을 스크롤 시키게되면 외쪽 또는 오른쪽으로 더이상 스크롤을 할 수 없게 된다.
여기서 구현 하고자 하는 내용은 0번 또는 마지막 인덱스로 스크롤 되더라도 큐와 같이 무한 스크롤 되도록 구현하는 방법.
핵심 포인트
Gallery Adapter에 실제 가지고 있는 Item의 개수의 3배를 가지고 있는 것처럼 보여 지게 하고
Gallery에 setOnItemSelectedListener를 추가하여 OnItemSelectedListener의 onItemSelected에서 가운데 범위를 넘어 서는 경우
Selected Item을 변경해 주면 무한 스크롤을 구현 할 수 있다.
Sample)
//-------------------------------------------------------------------------------------
// GalleryAdapter Class
//-------------------------------------------------------------------------------------
package reno.gallerytest;
import android.content.*;
import android.view.*;
import android.widget.*;
public class GalleryAdapter extends BaseAdapter {
private Context mContext;
private String[] resString = {"1111", "2222", "3333", "4444", "5555"};
public GalleryAdapter (Context c)
{
this.mContext = c;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return resString.length*3; // 총 Item 개수를 X 3으로 리턴한다. 이유는 Index의 맨 끝으로 이동시 좌,우에 Item이 연속되는 것 처럼 보기기 위함.
}
@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return resString[arg0%resString.length];
}
@Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return arg0%resString.length;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
TextView txtView;
position = position % resString.length;
if(convertView == null)
{
txtView = new TextView(mContext);
}else
{
txtView = (TextView)convertView;
}
txtView.setText(resString[position]);
txtView.setLayoutParams(new Gallery.LayoutParams(75, 75));
return txtView;
}
}
//------------------------------------------------------------------------------------------------
// GalleryTest Class
//------------------------------------------------------------------------------------------------
package reno.gallerytest;
import android.app.Activity;
import android.graphics.*;
import android.os.Bundle;
import android.widget.*;
import android.util.*;
import android.view.*;
public class GalleryTest extends Activity {
/** Called when the activity is first created. */
private RGallery unitGallery;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
unitGallery = (RGallery)findViewById(R.id.galUit);
unitGallery.setAdapter(new GalleryAdapter(this));
unitGallery.setSpacing(0);
unitGallery.setSelection(6);
unitGallery.setBackgroundColor(Color.GRAY);
unitGallery.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener(){
public void onItemSelected (AdapterView<?> parent, View view, int position, long id)
{
Log.d("GalleryTest", "onItemSelected index=" + String.valueOf(position));
if(position < 5)
unitGallery.setSelection(position + 5);
else if(position >= 10)
unitGallery.setSelection(position - 5);
// 이 부분이 무한 스크롤을 해주는 부분으로 가운데 영역을 벗어 나는 경우 setSelection으로 Position을 이동시켜 마치 무한 스크롤이 되게 보여 준다.
}
public void onNothingSelected (AdapterView<?> parent)
{
}
});
}
}
//----------------------------------------------------------------------------------------
// Main.xml
//----------------------------------------------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Gallery
android:id="@+id/galUit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
</Gallery>
</LinearLayout>
Splash Screen 만들기
<스플래쉬 액티비티>
스플래쉬 액티비티는 일반 프로그램에서 로딩화면쯤으로 생각하면 된다. 로딩화면의 역할은 프로그램에 필요한 자원을
불러들이는데 필요한 시간을 벌어주는 역할을 하는데 그것과 같게 생각하면 될 듯허다.
... 즉 , 프로그램을 구동전에 프로그램에 필요한 데이터를 읽어들일동안 보여주는 화면이다
<소스코드>
핸들러는 나중에 쓰레드(Thread)와 설명하기로 하고 일단 postDelayed라는 함수는 정해진 시간후에 Runnable 객체를 실행하는
함수이다.
밑에 splashandler라는 클래스는 Runnable인터페이스를 구현하는것을 볼 수 있는데 Runnable객체는 안드로이드에서 스케줄링을
할 수 있도록 한다. 즉, 정해진 일정시간후에 run함수를 실행시킬수 있다는 말이다.
즉 , 이 코드를 보면 5000밀리세컨드후에 run()함수를 실행한다. run()함수 내부에서는 새로운 인텐트(메인화면)을 실행한다.
그리고 현재 Splash액티비티는 종료된다.
(프로그램중에는 많은 액티비티를 이용할텐데 액티비티를 이동한다고 해서 이전 액티비티가
자동 종료되는 것이 아니다. 다만 사용자에게 보이지 않는 백그라운드(background)상태가 될 뿐이다. 물론 메모리가 부족하거나 하며
자동으로 우선순위에 따라서 액티비티는 종료되긴하지만...)
<xml 파일>
ImageView는 화면에 이미지를 보여주는 뷰이다. 안드로이드 내에 res->drawable폴더에 loading이라는 jpg파일을 삽입한 후
다음과 같은 코드를 통하여 이미지를 불러올 수 있다.
즉, 맨위에 그림과 같은 로딩시에 보여주고 싶은 화면을 설정하는 것이다.
Android Coverflow Widget
Android Coverflow Widget
The Video shows the Coverflow widget running on a HTC G1 (Dream). I can't remember the exact spec's of the phone but it's a fairly old one and is only running version 1.1 of Android. I'm moving the images using the track ball and then I also move them with screen touches. It is possible to drag, fling and touch on individual image to bring them to the centre. With a few tweaks I've managed to get the responsiveness and performance on this to be pretty good, it seems to be comparable to the standard gallery widget, and in my quick test the motion tracking was fluid and fast. I'm not sure how well it would run on newer versions of hardware and the Android OS, but I can only assume that things should be better. How much better I don't know, so if you try this on a different device or a newer OS I would love to hear how things worked.
참조사이트
http://www.inter-fuser.com/2010/01/android-coverflow-widget.html
소스
Here's the zip of the coverflow example: Coverflow.zip
안드로이드 이미지 물에 비친 효과내기
이미지 효과로 이미지 크기의 절반을 반대 되는 이미지를 만들고,
그 반대된 이미지에 대해서는 반사 된 듯한 효과를 주는 소스 입니다.
package com.android.reflection2;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.Bitmap.Config;
import android.graphics.PorterDuff.Mode;
import android.graphics.Shader.TileMode;
import android.os.Bundle;
import android.widget.ImageView;
public class Reflection extends Activity {
/** Called when the activity is first created. */
ImageView view1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
view1 = (ImageView) findViewById(R.id.ImageView01);
Bitmap bitmapOriginal = BitmapFactory.decodeResource(getResources(),
R.drawable.android);
int nWidth = bitmapOriginal.getWidth();
int nHeight = bitmapOriginal.getHeight();
Matrix matrix = new Matrix();
matrix.preScale(1, -1);
Bitmap bitmapReflection = Bitmap.createBitmap(bitmapOriginal, 0,
nHeight / 2, nWidth, nHeight / 2, matrix, false);
Bitmap bitmapOrigianlAndReflection = Bitmap.createBitmap(nWidth,
(nHeight + nHeight / 2), Config.ARGB_8888);
Canvas canvas = new Canvas(bitmapOrigianlAndReflection);
canvas.drawBitmap(bitmapOriginal, 0, 0, null);
Paint deafaultPaint = new Paint();
canvas.drawRect(0, nHeight, nWidth, nHeight + 5, deafaultPaint);
canvas.drawBitmap(bitmapReflection, 0, nHeight + 5, null);
Paint paint = new Paint();
LinearGradient shader = new LinearGradient(0, bitmapOriginal
.getHeight(), 0, bitmapOrigianlAndReflection.getHeight() + 5,
0x70ffffff, 0x00ffffff, TileMode.CLAMP);
paint.setShader(shader);
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
canvas.drawRect(0, nHeight, nWidth, bitmapOrigianlAndReflection
.getHeight() + 5, paint);
view1.setImageBitmap(bitmapOrigianlAndReflection);
}
}
결과 화면은 이런 식으로 나옵니다.
