출처 : http://withwani.tistory.com/160

Android의 ListView에서 HeaderView와 FooterView를 사용하는 것은 이전 포스트에서 정리를 하였다. 이번 포스트에서는 Thread를 사용하여 HeaderView와 FooterView를 실행했을 때 List item을 갱신 또는 추가 하는 부분을 해볼까 한다. 코드 구현 시 비교 대상은 Twitter Client로 이야기 목록에서의HeaderView와 FooterView을 흉내 내어 볼 것이다.

   

우선 Header와 Footer를 구현 할 Activity와 xml layout 파일을 구현해 보자.

접기

MoreListThreadExample.java

public class MoreListThreadExample extends Activity {
  

public static final int INC_COUNT = 15; // 추가로 보여줄 item 개수

MoreItemAdapter adtMore;

ListView lvMore;

View footer, header;

ArrayList<String> datas = new ArrayList<String>();

boolean justOnce = true, isHeader = false;

int footerId, headerId;


@Override

        public void onCreate(Bundle savedInstancestate) {

              super.onCreate(savedInstancestate);

              setContentView(R.layout.ui_listview2);

 

              System.out.println("Entered onCreate()");

 

              lvMore = (ListView)findViewById(R.id.lv_morelist);

              footer = getLayoutInflater().inflate(R.layout.ui_sub_footer, null, false);

              header = getLayoutInflater().inflate(R.layout.ui_sub_header, null, false);

 

              getData();

       }              

   

/* (non-Javadoc)

* @see android.app.Activity#onResume()

*/

@Override

protected void onResume() {

System.out.println("Entered onResume()");

 

              adtMore = new MoreItemAdapter(this, R.layout.ui_sub_list, datas);

              lvMore.addFooterView(footer);

              lvMore.addHeaderView(header);

              lvMore.setAdapter(adtMore);

              lvMore.setSelection(lvMore.getHeaderViewsCount());

              lvMore.setScrollbarFadingEnabled(true);

               

              headerId = 0;

              footerId = lvMore.getHeaderViewsCount()+datas.size();

 

              justOnce = false;

       super.onResume();

}

}

접기


접기

ui_listview2.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<ListView

        android:id="@+id/lv_morelist"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

/>

</LinearLayout>

접기


접기

ui_sub_footer.xml
 

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:gravity="center"

>

<ImageView

android:id="@+id/iv_list_footer_loading"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

/>

   

<TextView

android:id="@+id/tv_list_footer"

android:layout_width="wrap_content"

android:layout_height="50dip"

android:layout_marginLeft="10dip"

android:gravity="center"

android:text="More item ..."

android:textAppearance="?android:attr/textAppearanceLarge"

/>

</LinearLayout>

접기


접기

ui_sub_header.xml
 

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:gravity="center"

>

<ImageView

android:id="@+id/iv_list_header_loading"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

/>

   

<TextView

android:id="@+id/tv_list_header"

android:layout_width="wrap_content"

android:layout_height="50dip"

android:layout_marginLeft="10dip"

android:gravity="center"

android:text="Refresh ..."

android:textAppearance="?android:attr/textAppearanceLarge"

/>

</LinearLayout>

접기

사용된 변수 중 justOnce flag는 onScroll() event에서 Header및 Footer에 한 번 이상 접근하는 것을 막기 위해 사용한 flag 변수이다.

   

ListView에 item을 보여줄 Adapter는 Android 내장 adapter를 사용하지 않고 ArrayAdapter를 extends 해서 새로 구현하였다. 이유는 후에 item을 추가하기 위해 adapter에 add() method를 사용해야 하는데 왜인지 ListActivity를 사용하거나 Android 내장 adapter를 사용하니 add() method를 사용할 수 없었다. 뭐… 무슨 이유가 있겠지… ^^라고 생각하며 시간 관계상 그냥 만들어서 사용해버렸다.

접기

class MoreItemAdapter extends ArrayAdapter<String> { 

Context ctx;

List<String> mDatas;

int resId;

   

public MoreItemAdapter(Context context, int textViewResourceId, List<String> items) {

super(context, textViewResourceId, items);

ctx = context;

mDatas = items;

resId = textViewResourceId;

}

   

/* (non-Javadoc)

* @see android.widget.ArrayAdapter#getView(int, android.view.View, android.view.ViewGroup)

*/

@Override

public View getView(int position, View convertView, ViewGroup parent) {

View row = convertView;

TextView holder;

   

if(row == null) {

        LayoutInflater inflator = (LayoutInflater)ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

                       row = inflator.inflate(resId, null);

                        holder = (TextView)row.findViewById(R.id.tv_list_item);

                        row.setTag(holder);

        } else {

                 holder = (TextView)row.getTag();

        }

   

final String str = mDatas.get(position);

   

if(str != null) {

holder.setText(str);

}

   

return row;

}

}

접기


Adapter는 간단하게 inner class로 만들었으며 생성자의 인수 값으로 Context와 Sub View의 resourceID, 그리고 String형 배열 item을 받아 들인다. String 형 배열 item은 mString으로 테스트를 위해 만들어서 사용하였다.

   

그 다음으로 ListView adpter와 item 집합인 datas를 갱신하고 추가하는데 사용하는 몇 가지 method와 item을 load할 때 사용할 Thread로 AsyncTask를 만들어 보도록 한다.

우선 method를 구현해 보도록 하자.

접기

getData() method : Footer를 선택하면 현재 item의 수에 추가로 item을 더하는 method. 여기서는 onCreate() method에서 한 번만 실행하게 함.

private void getData() {

System.out.println("Entered getDatas()");

   

/// Method 1. 추가 개수만큼 처음부터 다시 가져와서 보여주기

        int cnt = (datas.isEmpty()) ? 0 : datas.size();

        if(cnt != 0) datas.clear();

        cnt = cnt + INC_COUNT;

 

        for(int i=0; i<cnt; i++) {

                if(i < mStrings.length)

                        datas.add(mStrings[i]);

        }

}

접기

접기

resetList() method : 새로운 adapter를 생성 및 추가 후 ListView의 selection을 1로 설정하면서 Header를 숨기게 함.

private void resetList() {

System.out.println("Entered resetList()");

   

adtMore = new MoreItemAdapter(this, R.layout.ui_sub_list, datas);

lvMore.setAdapter(adtMore);

lvMore.setSelection(lvMore.getHeaderViewsCount());

   

footerId = lvMore.getHeaderViewsCount()+datas.size();

}

접기

접기

refreshList() method : Adapter를 갱신하고 onClick() 혹은 onScroll event를 위해 footerId를 설정함. footerId는 list item의 개수에 HeaderView의 개수를 더한 값이다. ListView의 item postion은 0부터 시작하기 때문에 header가 존재하면 header가 0이 되고 footer는 위의 계산 값이 된다.

private void refreshList() {

System.out.println("Entered refreshList()");

adtMore.notifyDataSetChanged();

   

footerId = lvMore.getHeaderViewsCount()+datas.size();

}

접기

접기

LongProgress AsyncTask : Header와 Footer로 onClick 또는 onScroll event로 접근했을 때 ListView의 item을 추가 혹은 갱신할 때 사용하는 Thread. 주석 중에 Method1은 ProgressDialog를 사용하는 방법이고 Method2는 AnimationDrawable를 header 및 footer에 추가하는 방법이다. Twitter는 Method2를 사용하고 있기 때문에 그 방법을 채택하기로 한다.

class LongProgress extends AsyncTask<Void, Void, Void> {

ProgressDialog dialog = new ProgressDialog(MoreListThreadExample.this);

   

int cnt, start, end;

String view;

ArrayList<String> temp;

   

public LongProgress(String viewName) {

super();

view = viewName;

}

   

/* (non-Javadoc)

* @see android.os.AsyncTask#onPreExecute()

*/

@Override

protected void onPreExecute() {

/// Method 1. ProgressDialog 사용

//                        dialog.setMessage("Loading data...");

//                        dialog.show();

   

/// Method 2. View에 Spin animation 사용

if(view == "header") {

System.out.println("Header spin animation start.");

headerSpinAnim.start();

} else if(view == "footer") {

System.out.println("Footer spin animation start.");

footerSpinAnim.start();

}

   

temp = new ArrayList<String>();

super.onPreExecute();

}

   

@Override

protected Void doInBackground(Void... params) {

   

synchronized (datas) {

/// Method 2. 추가 개수만큼 adapter에 붙여 넣기.

cnt = (datas.isEmpty()) ? 0 : datas.size();

System.out.println("previous datas count = "+datas.size());

 

if (view == "header") { /// Header 쪽으로 스크롤링 했을 때... 아이템 및 리스트 재설정

        start = 0;

        end = cnt;

} else if(view == "footer") { /// Footer 쪽으로 스크롤링 했을 때... 아이템 추가 및 리스트 갱신

        start = cnt;

end = cnt + INC_COUNT;

}

System.out.println("Start = "+start+", end = "+end);

 

for(int i=start; i<end; i++) {

        try {

Thread.sleep(50);

} catch (InterruptedException e) {

e.printStackTrace();

}

                temp.add(mStrings[i]);

}

}

return null;

}

   

/* (non-Javadoc)

* @see android.os.AsyncTask#onPostExecute(java.lang.Object)

*/

@Override

protected void onPostExecute(Void result) {

System.out.println("Enterd onPostExecute()! result = "+result+", temp size = "+temp.size());

   

if (view == "header") { /// Header 쪽으로 스크롤링 했을 때... 아이템 및 리스트 재설정

System.out.println("Viewing Header...Called resetList()");

datas = null;

datas = temp;

        resetList();

} else if(view == "footer") { /// Footer 쪽으로 스크롤링 했을 때... 아이템 추가 및 리스트 갱신

        System.out.println("Viewing Footer...Called refreshList()");

        if(!temp.isEmpty()) {

                for(int i=0; i<temp.size(); i++) {

        adtMore.add(temp.get(i));

//                                 datas.add(temp.get(i)); /// adapter 에 추가하면 자동적으로 늘어남. 즉, 중복 추가라서 삭제함.

}

        }

refreshList();

}

   

System.out.println("result datas count = "+datas.size()+", adtMore size = "+adtMore.getCount());

   

/// Method 1. ProgressDialog 사용

//                        dialog.dismiss();

   

/// Method 2. View에 Spin animation 사용

if(view == "header") {

System.out.println("Header spin animation stop.");

headerSpinAnim.stop();

} else if(view == "footer") {

System.out.println("Footer spin animation stop.");

footerSpinAnim.stop();

}

   

justOnce = false;

super.onPostExecute(result);

}

}

접기

접기

loadData() method : Header와 Footer에 접근하여 event가 발생했을 때 AsyncTask를 실행하여 item을 추가 및 갱신 시킬 때 사용하는 method.

private void loadData(String who) {

System.out.println("Entered loadData(), who = "+who);

new LongProgress(who).execute();

}

접기

접기

OnItemClickListener : 만약 Header와 Footer를 onClick() event를 이용해서 동작시킬 때 사용. 여기서는 사용하지 않는다.

OnItemClickListener mClickListener = new OnItemClickListener() {

@Override

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

System.out.println("position = "+position+", datas.size() = "+datas.size());

   

if(position == footerId) {

if(!justOnce) {

System.out.println("Last position!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");

justOnce = true;

loadData("footer");

}

} else if(position == headerId) {

if(!justOnce) {

System.out.println("First position!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");

justOnce = true;

loadData("header");

}

}

}

};

접기

접기

OnScrollListener : ListView에서 onScroll() event를 통해 Header와 Footer를 동작시키기 위해 필요한 listener.

OnScrollListener mOnScrollListener = new OnScrollListener() {

   

@Override

public void onScrollStateChanged(AbsListView view, int scrollState) {

// TODO Auto-generated method stub

}

   

@Override

public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

//                        System.out.println("first : "+firstVisibleItem+" items : "+visibleItemCount+" last : "+totalItemCount);

if((firstVisibleItem+visibleItemCount) == totalItemCount){ /// footer

if(!justOnce) {

System.out.println("Last position!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");

justOnce = true;

loadData("footer");

}

} else if((firstVisibleItem == 0)) { /// header

if(!justOnce) {

System.out.println("First position!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");

justOnce = true;

isHeader = true;

//                                        loadData("header"); // Touch event에서 손을 떼었을 때 이벤트 발생하게끔 onTouch로 넘김

}

} else {

//                                justOnce = false;

isHeader = false;

}

   

}

};

접기

접기

OnTouchListener : Twitter를 흉내내기 위해 Header에서 onScroll() event에서 Thread를 발생시키지 않고 onScroll() event 체크 후 onTouch() event에서 ACTION_UP 시에(Touch 후 손을 떼었을 때) 발생시키기 위해 추가한 listener.

OnTouchListener mOnTouchListener = new OnTouchListener() {

   

@Override

public boolean onTouch(View v, MotionEvent event) {

if(event.getAction() == MotionEvent.ACTION_UP) {

if(isHeader) {

System.out.println("onTouch() entered! event : "+event);

loadData("header");

isHeader = false;

}

   

}

   

return false;

}

};

접기


자, 준비가 거의 끝났기 때문에 추가로 해줘야 할 일 몇 개만 하면 실행이 가능하다. 새로 생성한 method를 onResume() method에 적용하고 spin animation을 사용하기 위해 animation xml 파일을 만들고 onCreate() method에 적용하자.

접기

@Override

protected void onResume() {

       System.out.println("Entered onResume()");

 

       adtMore = new MoreItemAdapter(this, R.layout.ui_sub_list, datas);

       lvMore.addFooterView(footer);

       lvMore.addHeaderView(header);

       lvMore.setAdapter(adtMore);

       lvMore.setSelection(lvMore.getHeaderViewsCount());

       lvMore.setScrollbarFadingEnabled(true);

       // lvMore.setOnItemClickListener(mClickListener); // Concept에 따라 사용 안함.

       lvMore.setOnScrollListener(mOnScrollListener);

       lvMore.setOnTouchListener(mOnTouchListener);

 

       headerId = 0;

       footerId = lvMore.getHeaderViewsCount()+datas.size();

 

       justOnce = false;

       super.onResume();

}

접기

접기

@Override

public void onCreate(Bundle savedInstancestate) {

       super.onCreate(savedInstancestate);

       setContentView(R.layout.ui_listview2);

 

       System.out.println("Entered onCreate()");

 

       lvMore = (ListView)findViewById(R.id.lv_morelist);

       footer = getLayoutInflater().inflate(R.layout.ui_sub_footer, null, false);

       /// Spin animation 관련 ImageView 구현

       loadFooter = (ImageView)footer.findViewById(R.id.iv_list_footer_loading);

       loadFooter.setBackgroundResource(R.anim.anim_spiner);

       footerSpinAnim = (AnimationDrawable)loadFooter.getBackground();

       header = getLayoutInflater().inflate(R.layout.ui_sub_header, null, false);

       /// Spin animation 관련 ImageView 구현

       loadHeader = (ImageView)header.findViewById(R.id.iv_list_header_loading);

       loadHeader.setBackgroundResource(R.anim.anim_spiner);

       headerSpinAnim = (AnimationDrawable)loadHeader.getBackground();

 

       getData();

}

접기

접기

anim_spiner.xml : drawable에 존재하는 image는 Android에서 사용하는 ProgressDialog spin image 혹은 popup sync image를 사용하면 된다.
 

<?xml version="1.0" encoding="utf-8"?>

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"

android:oneshot="false">

<item android:drawable="@drawable/img_intro_loading_a"

android:duration="150">

</item>

<item android:drawable="@drawable/img_intro_loading_b"

android:duration="150">

</item>

<item android:drawable="@drawable/img_intro_loading_c"

android:duration="150">

</item>

<item android:drawable="@drawable/img_intro_loading_d"

android:duration="150">

</item>

<item android:drawable="@drawable/img_intro_loading_e"

android:duration="150">

</item>

<item android:drawable="@drawable/img_intro_loading_f"

android:duration="150">

</item>

<item android:drawable="@drawable/img_intro_loading_g"

android:duration="150">

</item>

<item android:drawable="@drawable/img_intro_loading_h"

android:duration="150">

</item>

<item android:drawable="@drawable/img_intro_loading_i"

android:duration="150">

</item>

<item android:drawable="@drawable/img_intro_loading_j"

android:duration="150">

</item>

<item android:drawable="@drawable/img_intro_loading_k"

android:duration="150">

</item>

<item android:drawable="@drawable/img_intro_loading_l"

android:duration="150">

</item>

</animation-list>

접기


자~ 모든 구현이 끝났다. Twitter의 이야기 메뉴의 ListView를 보이는 것만 보고 따라하기 한 것이므로 구조적인 문제는 좀 더 다듬어야 하겠지만 기본적은 구현 부는 정리가 되었으므로 나머지는 이걸 어떻게 효율적으로 변형하여 사용하느냐~ 가 될 것이다. 오늘 포스트와 같이 구현하면 다음과 같은 실행 화면을 볼 수 있다. 쓰고 보니 글이 참 길어진 것 같다. 1,2 편으로 나눌까? 했지만 필자가 그런 것을 싫어하므로 그냥 한 화면에서 볼 수 있게 적었다. 불편했다면 양해 바란다.

 초기 실행 시 화면

 Scroll up 한 후 Touch up 했을 때 화면

 Scroll down 했을 때 화면

세상에는 날고 기는 사람들이 참 많은 것 같다..


안드로이드 리스트 async(비동기) 구성 안드로이드

2010/09/01 23:33

복사 http://blog.naver.com/wono77/140113961263

출처: 

안드로이드 리스트 async(비동기) 구성

 

문서 히스토리:

2010년 9월 1일 글 최초 작성(비공개)

2010년 10월 1일 공개전환

 

원문:

http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed:+blogspot/hsDu+(Android+Developers+Blog

 

안드로이드와 같은 모바일 프로그램이 웹의 자원을 가져올때,

여러 화면에 걸쳐 출력되는 리스트나 그리드뷰에 용량이 좀 되는 이미지들이 많이 존재한다면

전체 리소스를 가져오기 위해 대기시간이 엄청나게 길어지게 된다.

 

이 문제를 해결하기 위해 안드로이드 2.2에서는 아래와 같은 방법으로

자원을 대기하지 않고 한 화면단위로 async(비동기)하게 가져올 수 있다.

 

아래 예제에서 라디오 버튼을 제거한 async 리스트를 만드려면 시작부분을 아래처럼 고치면 된다.

첨부 파일을 참조할 것.

 

public class Async  extends ListActivity{
    /** Called when the activity is first created. */
 
  @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
        
         setContentView(R.layout.imagelist);

         ImageDownloader.Mode mode = ImageDownloader.Mode.CORRECT;
         setListAdapter(new ImageAdapter());
         ((ImageAdapter) getListAdapter()).getImageDownloader().setMode(mode);
     }
}

 

예제에 대한 프로젝트 파일 첨부함. by wono77

 

도움이 되셨다면 댓글을 남겨주시는 당신은 센스쟁이~^^*

 

아래에 원문 글을 소개합니다.

----------------------------------------------------------------------------------------

Multithreading For Performance

[This post is by Gilles Debunne, an engineer in the Android group who loves to get multitasked. ? Tim Bray]

A good practice in creating responsive applications is to make sure your main UI thread does the minimum amount of work. Any potentially long task that may hang your application should be handled in a different thread. Typical examples of such tasks are network operations, which involve unpredictable delays. Users will tolerate some pauses, especially if you provide feedback that something is in progress, but a frozen application gives them no clue.

In this article, we will create a simple image downloader that illustrates this pattern. We will populate a ListView with thumbnail images downloaded from the internet. Creating an asynchronous task that downloads in the background will keep our application fast.

An Image downloader

Downloading an image from the web is fairly simple, using the HTTP-related classes provided by the framework. Here is a possible implementation:

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;
}

A client and an HTTP request are created. If the request succeeds, the response entity stream containing the image is decoded to create the resulting Bitmap. Your applications' manifest must ask for the INTERNET to make this possible.

Note: a bug in the previous versions of BitmapFactory.decodeStream may prevent this code from working over a slow connection. Decode a new FlushedInputStream(inputStream) instead to fix the problem. Here is the implementation of this helper class:

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;
   
}
}

This ensures that skip() actually skips the provided number of bytes, unless we reach the end of file.

If you were to directly use this method in your ListAdapter's getView method, the resulting scrolling would be unpleasantly jaggy. Each display of a new view has to wait for an image download, which prevents smooth scrolling.

Indeed, this is such a bad idea that the AndroidHttpClient does not allow itself to be started from the main thread. The above code will display "This thread forbids HTTP requests" error messages instead. Use the DefaultHttpClient instead if you really want to shoot yourself in the foot.

Introducing asynchronous tasks

The AsyncTask class provides one of the simplest ways to fire off a new task from the UI thread. Let's create an ImageDownloader class which will be in charge of creating these tasks. It will provide a download method which will assign an image downloaded from its URL to an ImageView:

public class ImageDownloader {

   
public void download(String url, ImageView imageView) {
           
BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
            task
.execute(url);
       
}
   
}

   
/* class BitmapDownloaderTask, see below */
}

The BitmapDownloaderTask is the AsyncTask which will actually download the image. It is started using execute, which returns immediately hence making this method really fast which is the whole purpose since it will be called from the UI thread. Here is the implementation of this class:

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);
           
}
       
}
   
}
}

The doInBackground method is the one which is actually run in its own process by the task. It simply uses the downloadBitmap method we implemented at the beginning of this article.

onPostExecute is run in the calling UI thread when the task is finished. It takes the resulting Bitmap as a parameter, which is simply associated with the imageView that was provided to download and was stored in the BitmapDownloaderTask. Note that this ImageView is stored as a WeakReference, so that a download in progress does not prevent a killed activity's ImageView from being garbage collected. This explains why we have to check that both the weak reference and the imageView are not null (i.e. were not collected) before using them in onPostExecute.

This simplified example illustrates the use on an AsyncTask, and if you try it, you'll see that these few lines of code actually dramatically improved the performance of the ListView which now scrolls smoothly. Read Painless threading for more details on AsyncTasks.

However, a ListView-specific behavior reveals a problem with our current implementation. Indeed, for memory efficiency reasons, ListView recycles the views that are displayed when the user scrolls. If one flings the list, a given ImageView object will be used many times. Each time it is displayed the ImageView correctly triggers an image download task, which will eventually change its image. So where is the problem? As with most parallel applications, the key issue is in the ordering. In our case, there's no guarantee that the download tasks will finish in the order in which they were started. The result is that the image finally displayed in the list may come from a previous item, which simply happened to have taken longer to download. This is not an issue if the images you download are bound once and for all to given ImageViews, but let's fix it for the common case where they are used in a list.

Handling concurrency

To solve this issue, we should remember the order of the downloads, so that the last started one is the one that will effectively be displayed. It is indeed sufficient for each ImageView to remember its last download. We will add this extra information in the ImageView using a dedicated Drawable subclass, which will be temporarily bind to the ImageView while the download is in progress. Here is the code of our DownloadedDrawable class:

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();
   
}
}

This implementation is backed by a ColorDrawable, which will result in the ImageView displaying a black background while its download is in progress. One could use a “download in progress” image instead, which would provide feedback to the user. Once again, note the use of a WeakReference to limit object dependencies.

Let's change our code to take this new class into account. First, the download method will now create an instance of this class and associate it with the imageView:

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);
     
}
}

The cancelPotentialDownload method will stop the possible download in progress on this imageView since a new one is about to start. Note that this is not sufficient to guarantee that the newest download is always displayed, since the task may be finished, waiting in its onPostExecute method, which may still may be executed after the one of this new download.

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;
}

cancelPotentialDownload uses the cancel method of the AsyncTask class to stop the download in progress. It returns true most of the time, so that the download can be started in download. The only reason we don't want this to happen is when a download is already in progress on the same URL in which case we let it continue. Note that with this implementation, if an ImageView is garbage collected, its associated download is not stopped. A RecyclerListener might be used for that.

This method uses a helper getBitmapDownloaderTask function, which is pretty straigthforward:

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;
}

Finally, onPostExecute has to be modified so that it will bind the Bitmap only if this ImageView is still associated with this download process:

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);
   
}
}

With these modifications, our ImageDownloader class provides the basic services we expect from it. Feel free to use it or the asynchronous pattern it illustrates in your applications to ensure their responsiveness.

Demo

The source code of this article is available online on Google Code. You can switch between and compare the three different implementations that are described in this article (no asynchronous task, no bitmap to task association and the final correct version). Note that the cache size has been limited to 10 images to better demonstrate the issues.

Future work

This code was simplified to focus on its parallel aspects and many useful features are missing from our implementation. The ImageDownloader class would first clearly benefit from a cache, especially if it is used in conjuction with a ListView, which will probably display the same image many times as the user scrolls back and forth. This can easily be implemented using a Least Recently Used cache backed by a LinkedHashMap of URL to Bitmap SoftReferences. More involved cache mechanism could also rely on a local disk storage of the image. Thumbnails creation and image resizing could also be added if needed.

Download errors and time-outs are correctly handled by our implementation, which will return a null Bitmap in these case. One may want to display an error image instead.

Our HTTP request is pretty simple. One may want to add parameters or cookies to the request as required by certain web sites.

The AsyncTask class used in this article is a really convenient and easy way to defer some work from the UI thread. You may want to use the Handler class to have a finer control on what you do, such as controlling the total number of download threads which are running in parallel in this case.

출처 : http://mainia.tistory.com/555

(1) 여러 개의 멀티선택 옵션으로 표현해 주기

 

다중선택을 위한 다이얼로그 창을 띄운다. 리스트에는 제목과 radio button

이 있다. AlertDialog.Builder 클래스로 구현하며 리스트중 특정행을 클릭했을 때

이벤트는 setSingleChoiceItems 에 등록한다. Ok 버튼클릭 이벤트는

setPositiveButton , Cancel 버튼클릭 이벤트는 setNegativeButton 에 등록하고

기능을 구현하면 된다.


01 private void DialogSelectOption() {
02     final String items[] = { "item1", "item2", "item3" };
03     AlertDialog.Builder ab = new AlertDialog.Builder(DialogSample.this);
04     ab.setTitle("Title");
05     ab.setSingleChoiceItems(items, 0,
06         new DialogInterface.OnClickListener() {
07         public void onClick(DialogInterface dialog, int whichButton) {
08             // 각 리스트를 선택했을때 
09         }
10         }).setPositiveButton("Ok",
11         new DialogInterface.OnClickListener() {
12         public void onClick(DialogInterface dialog, int whichButton) {
13             // OK 버튼 클릭시 , 여기서 선택한 값을 메인 Activity 로 넘기면 된다.
14         }
15         }).setNegativeButton("Cancel",
16         new DialogInterface.OnClickListener() {
17         public void onClick(DialogInterface dialog, int whichButton) {
18             // Cancel 버튼 클릭시
19         }
20         });
21     ab.show();
22 }

 

(2) html 로 구현한 text 넣기

 

다이얼로그 창을 띄울 때 공지나 경고성 창이라면 내용이 들어갈것이다. 이 내용을

HTML 태그를 적용해 넣을 수가 있다. 색깔은 제대로 바뀌는 것 같은데 <strong>, <b>

등 글자에 대한 bold 처리가 한거나 안한거나 똑같이 보여서 제대로 표현되는지는

좀더 테스트 해봐야할것같다.

01 private void DialogHtmlView(){
02 AlertDialog.Builder ab=new AlertDialog.Builder(DialogSample.this);
03     ab.setMessage(Html.fromHtml("<STRONG><FONT color=#ff0000> " +
04         "Html 표현여부 " +"</FONT></STRONG><BR>
05   
06   
07 HTML 이 제대로 표현되는지 본다."));
08         ab.setPositiveButton("ok", null);
09         ab.show();
10 }

 

(3) 프로그레시브(Progress) 다이얼로그 구현

 

안드로이드에서 프로그레시브바를 구현할수 있다. 이것을 구현하기 위한 클래스는

ProgressDialog 를 사용해야 한다. 다이얼로그 창의 중지는 ProgressDialog

dismiss 를 쓰면된다.

1 private void DialogProgress(){
2        ProgressDialog dialog = ProgressDialog.show(DialogSample.this, "",
3                         "잠시만 기다려 주세요 ...", true);
4       // 창을 내린다.
5       // dialog.dismiss();
6 }
aaa

 

(4) Radio 버튼을 포함한 다중선택 다이얼로그 창

 

1번 예제와 비슷하게 Radio 버튼이 추가되어있는 다중선택 다이얼로그 창이다.

차이가 있다면 창 타이틀에 setIcon 을 써서 아이콘을 집어넣은것과

선택한 행 번호를 알아와 Toast 로 화면에 문자열을 표시해주는 내용이다.

창을 닫고 싶다면 onclick 함수에 넘어온 DialogInterface 객체로 cancel 함수를

호출해 닫으면 된다.

01 private void DialogRadio(){
02 final CharSequence[] PhoneModels = {"iPhone", "Nokia", "Android"};
03         AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
04         alt_bld.setIcon(R.drawable.icon);
05         alt_bld.setTitle("Select a Phone Model");
06         alt_bld.setSingleChoiceItems(PhoneModels, -1, new DialogInterface.OnClickListener() {
07             public void onClick(DialogInterface dialog, int item) {
08                 Toast.makeText(getApplicationContext(), "Phone Model = "+PhoneModels[item], Toast.LENGTH_SHORT).show();
09                 // dialog.cancel();
10             }
11         });
12         AlertDialog alert = alt_bld.create();
13         alert.show();
14 }

 

(5) 선택에 따른 로직구현을 위한 다이얼로그 창 구현

 

예를 들어 Yes/No 중 하나를 선택함으로서 특정 기능을 구현해주고자 할 때

쓰일만한 예제이다. 샘플에서는 Yes/No 일때를 구분하여 코드를 구현할수

있도록 나누어져 있다. 우리가 흔히 보는 대표적인 다이얼로그 창일것이다.

01 private void DialogSimple(){
02     AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
03     alt_bld.setMessage("Do you want to close this window ?").setCancelable(
04         false).setPositiveButton("Yes",
05         new DialogInterface.OnClickListener() {
06         public void onClick(DialogInterface dialog, int id) {
07             // Action for 'Yes' Button
08         }
09         }).setNegativeButton("No",
10         new DialogInterface.OnClickListener() {
11         public void onClick(DialogInterface dialog, int id) {
12             // Action for 'NO' Button
13             dialog.cancel();
14         }
15         });
16     AlertDialog alert = alt_bld.create();
17     // Title for AlertDialog
18     alert.setTitle("Title");
19     // Icon for AlertDialog
20     alert.setIcon(R.drawable.icon);
21     alert.show();
22 }
aaaa

 

(6) Time Picker 시간선택 컨트롤을 다이얼로그에 구현

 

Time Picker 컨트롤을 다이얼로그 창에 구현한 예제이다. 그리고 다이얼로그에

구현되어있는 Time Picker 에서 선택한 값을 부모 화면에서 받아 그 값을 Toast

로 보여준다
01 private void DialogTimePicker(){
02     TimePickerDialog.OnTimeSetListener mTimeSetListener = 
03     new TimePickerDialog.OnTimeSetListener() {
04         public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
05             Toast.makeText(DialogSample.this,
06             "Time is=" + hourOfDay + ":" + minute, Toast.LENGTH_SHORT)
07             .show();
08         }
09     };
10     TimePickerDialog alert = new TimePickerDialog(this
11 mTimeSetListener, 0, 0, false);
12     alert.show();
13 }

 

(7) Date Picker 날짜선택 컨트롤을 다이얼로그에 구현

 

Date Picker 컨트롤을 다이얼로그 창에 구현한 예제이다. 그리고 다이얼로그에

구현되어있는 Date Picker 에서 선택한 값을 부모 화면에서 받아 그 값을 Toast

로 보여준다. 창을 띄우기 전에 현재 날짜정보를 넘겨주고 Date Picker 에서는

그것을 받아 표시해 준다.

01 private void DialogDatePicker(){
02     Calendar c = Calendar.getInstance();
03     int cyear = c.get(Calendar.YEAR);
04     int cmonth = c.get(Calendar.MONTH);
05     int cday = c.get(Calendar.DAY_OF_MONTH);
06       
07     DatePickerDialog.OnDateSetListener mDateSetListener = 
08     new DatePickerDialog.OnDateSetListener() {
09     // onDateSet method
10     public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
11          String date_selected = String.valueOf(monthOfYear+1)+
12                 " /"+String.valueOf(dayOfMonth)+" /"+String.valueOf(year);
13          Toast.makeText(DialogSample.this
14         "Selected Date is ="+date_selected, Toast.LENGTH_SHORT).show();
15     }
16      };
17      DatePickerDialog alert = new DatePickerDialog(this,  mDateSetListener,  
18      cyear, cmonth, cday);
19      alert.show();
20 }
aaaa

 

(8) 전체 소스

 

001 import java.util.ArrayList;
002 import java.util.Calendar;
003 import java.util.HashMap;
004 import java.util.List;
005 import java.util.Map;
006   
007 import android.app.AlertDialog;
008 import android.app.DatePickerDialog;
009 import android.app.ListActivity;
010 import android.app.ProgressDialog;
011 import android.app.TimePickerDialog;
012 import android.content.DialogInterface;
013 import android.os.Bundle;
014 import android.text.Html;
015 import android.util.Log;
016 import android.view.View;
017 import android.widget.DatePicker;
018 import android.widget.ListView;
019 import android.widget.SimpleAdapter;
020 import android.widget.TimePicker;
021 import android.widget.Toast;
022   
023 import com.sample.R;
024   
025 public class DialogSample extends ListActivity {
026   
027     private String[] mMenuText;
028     private String[] mMenuSummary;
029   
030     private String keyName = "name";
031     private String keyDesc = "desc";
032     private String TAG;
033       
034     /** Called when the activity is first created. */
035     @Override
036     public void onCreate(Bundle savedInstanceState) {
037         super.onCreate(savedInstanceState);
038         TAG = getClass().getName();
039   
040         int length = 7;
041         mMenuText = new String[length];
042         mMenuSummary = new String[length];
043   
044         mMenuText[0] = "다중선택 새창";
045         mMenuSummary[0] = "다중선택을 할수 있는 Alert 예제구현이다.";
046         mMenuText[1] = "HTML 적용 새창";
047         mMenuSummary[1] = "Text 에 HTML 을 적용하는 Alert 예제구현이다.";
048         mMenuText[2] = "프로그레시브바 새창";
049         mMenuSummary[2] = "진행중을 나타내는 프로그레시브바 Alert 예제구현다.";
050         mMenuText[3] = "Radio 버튼 새창";
051         mMenuSummary[3] = "Radio 버튼이 들어간 새창 이며 선택하면 창이 닫힌다. ";
052         mMenuText[4] = "Simple Dialog";
053         mMenuSummary[4] = "선택에 따른 로직구현을 위한 다이얼로그 창 구현";
054         mMenuText[5] = "Time Picker";
055         mMenuSummary[5] = "Time Picker 시간선택 컨트롤을 다이얼로그에 구현";
056         mMenuText[6] = "Date Picker";
057         mMenuSummary[6] = "Date Picker 날짜선택 컨트롤을 다이얼로그에 구현";
058   
059         setListAdapter(new SimpleAdapter(this, getListValues(),
060                 android.R.layout.simple_list_item_2, new String[] { keyName,
061                         keyDesc }, new int[] { android.R.id.text1,
062                         android.R.id.text2 }));
063     }
064   
065     private List<MAP><STRING, String="">> getListValues() {
066         List<MAP><STRING, String="">> values = new ArrayList<MAP><STRING, String="">>();
067         int length = mMenuText.length;
068         for (int i = 0; i < length; i++) {
069             Map<STRING, String=""> v = new HashMap<STRING, String="">();
070             v.put(keyName, mMenuText[i]);
071             v.put(keyDesc, mMenuSummary[i]);
072             values.add(v);
073         }
074         return values;
075     }
076   
077     @Override
078     protected void onListItemClick(ListView l, View v, int position, long id) {
079         super.onListItemClick(l, v, position, id);
080         Log.d(TAG, "id : " + id + ", position : " + position);
081         switch (position) {
082         case 0:
083             // 다중선택 옵션창 
084             this.DialogSelectOption();
085             break;
086         case 1:
087             // HTML 구현 
088             this.DialogHtmlView();
089             break;
090         case 2:
091             // 프로그레시브바 구현
092             this.DialogProgress();
093             break;
094         case 3:
095             //  Radio 버튼이 추가된 다중선택 창
096             this.DialogRadio();
097             break;
098         case 4:
099             // 가장 일반적인 Yes/NO기능구현 Dialog
100             this.DialogSimple();
101             break;
102         case 5:
103             this.DialogTimePicker();
104             break;
105         case 6:
106             // 날짜 선택 Dialog 구현 
107             this.DialogDatePicker();
108             break;
109         default:
110             break;
111         }
112     }
113   
114     private void DialogSelectOption() {
115         final String items[] = { "item1", "item2", "item3" };
116         AlertDialog.Builder ab = new AlertDialog.Builder(DialogSample.this);
117         ab.setTitle("Title");
118         ab.setSingleChoiceItems(items, 0,
119             new DialogInterface.OnClickListener() {
120                 public void onClick(DialogInterface dialog, int whichButton) {
121                     // 각 리스트를 선택했을때 
122                 }
123             }).setPositiveButton("Ok",
124             new DialogInterface.OnClickListener() {
125                 public void onClick(DialogInterface dialog, int whichButton) {
126                     // OK 버튼 클릭시 , 여기서 선택한 값을 메인 Activity 로 넘기면 된다.
127                 }
128             }).setNegativeButton("Cancel",
129             new DialogInterface.OnClickListener() {
130                 public void onClick(DialogInterface dialog, int whichButton) {
131                     // Cancel 버튼 클릭시
132                 }
133             });
134         ab.show();
135     }
136       
137     private void DialogHtmlView(){
138         AlertDialog.Builder ab=new AlertDialog.Builder(DialogSample.this);
139         ab.setMessage(Html.fromHtml("<STRONG><FONT color=#ff0000> " +
140                 "Html 표현여부 " +"</FONT></STRONG><BR>
141   
142   
143   
144 HTML 이 제대로 표현되는지 본다."));
145             ab.setPositiveButton("ok", null);
146             ab.show();
147     }
148       
149     private void DialogProgress(){
150         ProgressDialog dialog = ProgressDialog.show(DialogSample.this, "",
151                                 "잠시만 기다려 주세요 ...", true);
152                 
153         // 창을 내린다.
154         // dialog.dismiss();
155     }
156       
157     private void DialogRadio(){
158         final CharSequence[] PhoneModels = {"iPhone", "Nokia", "Android"};
159         AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
160         alt_bld.setIcon(R.drawable.icon);
161         alt_bld.setTitle("Select a Phone Model");
162         alt_bld.setSingleChoiceItems(PhoneModels, -1, new DialogInterface.OnClickListener() {
163             public void onClick(DialogInterface dialog, int item) {
164                 Toast.makeText(getApplicationContext(), "Phone Model = "+PhoneModels[item], Toast.LENGTH_SHORT).show();
165                 dialog.cancel();
166             }
167         });
168         AlertDialog alert = alt_bld.create();
169         alert.show();
170     }
171       
172     private void DialogSimple(){
173         AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
174         alt_bld.setMessage("Do you want to close this window ?").setCancelable(
175                 false).setPositiveButton("Yes",
176                 new DialogInterface.OnClickListener() {
177                     public void onClick(DialogInterface dialog, int id) {
178                         // Action for 'Yes' Button
179                     }
180                 }).setNegativeButton("No",
181                 new DialogInterface.OnClickListener() {
182                     public void onClick(DialogInterface dialog, int id) {
183                         // Action for 'NO' Button
184                         dialog.cancel();
185                     }
186                 });
187         AlertDialog alert = alt_bld.create();
188         // Title for AlertDialog
189         alert.setTitle("Title");
190         // Icon for AlertDialog
191         alert.setIcon(R.drawable.icon);
192         alert.show();
193     }
194       
195     private void DialogTimePicker(){
196         TimePickerDialog.OnTimeSetListener mTimeSetListener = 
197             new TimePickerDialog.OnTimeSetListener() {
198                 public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
199                     Toast.makeText(DialogSample.this,
200                         "Time is=" + hourOfDay + ":" + minute, Toast.LENGTH_SHORT)
201                         .show();
202                 }
203         };
204         TimePickerDialog alert = new TimePickerDialog(this, mTimeSetListener, 0, 0, false);
205         alert.show();
206     }
207       
208     private void DialogDatePicker(){
209         Calendar c = Calendar.getInstance();
210         int cyear = c.get(Calendar.YEAR);
211         int cmonth = c.get(Calendar.MONTH);
212         int cday = c.get(Calendar.DAY_OF_MONTH);
213           
214         DatePickerDialog.OnDateSetListener mDateSetListener = 
215             new DatePickerDialog.OnDateSetListener() {
216                 // onDateSet method
217                 public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
218                     String date_selected = String.valueOf(monthOfYear+1)+
219                         " /"+String.valueOf(dayOfMonth)+" /"+String.valueOf(year);
220                     Toast.makeText(DialogSample.this
221                             "Selected Date is ="+date_selected, Toast.LENGTH_SHORT).show();
222                 }
223         };
224         DatePickerDialog alert = new DatePickerDialog(this,  mDateSetListener,  cyear, cmonth, cday);
225         alert.show();
226     }
227   
228


[Tip]Android에서의 RSS Reader 구현 개발 팁 / 모바일 웹

첨부파일 :



2009/03/24 21:30

 http://blog.naver.com/kippee/130044828392

네이버 블로그(RSS 2.0)를 가져올 수 있는 간단한 RSS Reader를 안드로이드 에서 구현하였다.

실제 사용하기 보단 (구글폰도 없지만) Study 개념으로 작성한 것이기 때문에

네이버 블로그만 가져올 수 있고 (물론, java 소스를 고치면 다른 것도 가능하다)

링크되어 있는 이미지 처리도 안된다

 

여기에는 몇가지 기술을 적용하였는데

  • 입력창에서 Blog ID를 입력하면 List를 볼수 있는 화면 (특정 Activity 호출)으로 전환
  • 간단한 팝업창
  • 블로그들을 Fetch하는데 지루하지 않도록 Progress Dialog를 보여주는 방법
  • ListView에서 클릭하면 별도의 Dialog Box가 보여주는 방법
  • 특정 부분을 클릭하면 안드로이드  제공 블라우저을 호출하는 것

호출한 XML문서를 Parsing 하는 방법 등이다.

 

1. 입력창에서 Blog ID를 입력하면 List를 볼수 있는 화면 (특정 Activity 호출)으로 전환

위의 그림처럼 확인 버튼을 누르면 다른 화면으로 전환하게 되는 데 버튼에 리슨너를 등록하고

   //* 확인 버튼에 리슨너 등록

  mbtStartGetRSS.setOnClickListener(mGoListner);

리슨너를 아래와 같이 Define 해주면 된다

여기서 Intent를 생성하고 실행된 Activity Class와 함께 보내줄 데이타를 지정해준다.

 

 private DialogInterface.OnClickListener mGoOKListener = new DialogInterface.OnClickListener()
 {     

  public void onClick(DialogInterface dialog, int which) {
   String mBlogId;
   

   mBlogId = metBlogId.getText().toString();
   // TODO Auto-generated method stub
   // getRSSItem();
   Intent mI = new Intent();
   mI.setClass(RSSMain.this,BlogList.class);
   mI.putExtra("Blogger",metBlogId.getText().toString());
   startActivity(mI);
   finish();
  }

  
 };

2. 간단한 팝업창

일반적으로 팝업창은 Alertailog의 Builder Class를 이용하여 할 수도 있지만

Toast를 이용하여 간단하게 작성할 수도 있다.

  private OnClickListener mGoListner = new OnClickListener()
 {      

  @Override
  public void onClick(View arg0) {
   // TODO Auto-generated method stub
   if (metBlogId.getText().toString().length() == 0){ 
    Toast.makeText(RSSMain.this,"BlogID를 입력하세요",Toast.LENGTH_SHORT )
    .show();
   }
   else
    showDialog(POPUP_DIALOG);
  }  
 };

 

3. 블로그들을 Fetch하는데 지루하지 않도록 Progress Dialog를 보여주는 방법

이 부분을 구현하는 데 가장 많이 시간이 소요 되었다.

착안점은 먼저 Progress Bar를 먼저 보여 주고 BackGroud로 Blog를 Fetch 해야 되는 데

이부분은 별도의 thread로 처리해야 된다.

일단 Progress Bar를 표시하는 방법은


onCreateDialog 에 다른 Dialog와 함께 Progress Dialog를 정의한다.

  protected Dialog onCreateDialog(int id) {
  // TODO Auto-generated method stub
  switch (id) {
  case ITEM_DIALOG:
   LayoutInflater li = LayoutInflater.from(this);
   View ItemDetailsView = li.inflate(R.layout.item_details, null);

   AlertDialog.Builder mItemDialog = new AlertDialog.Builder(this);
   mItemDialog.setTitle("블로그 정보");
   mItemDialog.setPositiveButton("확인", null);
   mItemDialog.setNegativeButton("취소", null);
   mItemDialog.setView(ItemDetailsView);
   return mItemDialog.create();
  case PROGRESS_DIALOG:
   mProgressDialog = new ProgressDialog(this);
   mProgressDialog.setMessage("Fetching Blogs ....");
   mProgressDialog.setIndeterminate(true);
   mProgressDialog.setCancelable(true);
   mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
   return mProgressDialog;


  }
  return null;
 }

보여주는 시점에서 ShowDialog(PROGRESS_DIALOG) 를 호출하면 된다.

 

위에서 언급한 것 처럼 실제로 Progress Dialog가 보여주는 동안에 별도의 Thread를 생성하여 Blog를 가져와야 되는 데 코드는 아래와 같다

 new Thread() {
   public void run() {
    try{
     // Do some Fake-Work
     if ((mDoc = readFeeder(mFeeder)) != null)
      parseBlog(mDoc);
     mProgressDialog.dismiss();
     mProgressHandler.post(mUpdateResults);


    } catch (Exception e) { 
     Log.e("RSS", "Exception : Unknown Exception ");
    }
    // Dismiss the Dialog
    //mProgressDialog.dismiss();
   }
  }.start();

여기서 중요한 것은 Backgroud로 실행된 결과는 결코 화면으로 표시되는 것이 아니라 (화면에 표시하면 에러가 발생한다) 별도의 메소드로 처리해야 되는 데 일단 핸들러로 Message Queue에 Runnable을 post 해야 한다.  mProgressHandler.post(mUpdateResults);   

그리고 실제 Runnable에서 다시 표시할 수 있는 코드을 작성해야 된다.

  private void updateResultsInUi()
 {
  mtvBlogTitle.setText(mBlogURL);
  mtvBlogLink.setText(" http://www.naver.com");
  mtvBlogDesc.setText(mBlog.getMDesc());
  mtvBlogGenerator.setText(mBlog.getMGenerator());
  SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
  mtvBlogPubdate.setText(sdf.format(mBlog.getMPubdate()));
  
  aaI.notifyDataSetChanged();
  Toast.makeText(getApplicationContext(),mBlogs+
    "개의 Blog들이 있습니다.",
    Toast.LENGTH_LONG).show();
  Log.e("RSS", "updateResultsInUi Started ");  
 } 
여기서 notifyDataSetChanged는 ListView add 된 내용을 다시 보여주는 method 이다
Android Load Image From URL Example ANDROID / 프로젝트

2010/09/05 18:59

복사 http://loadtodream.blog.me/30093256249

In android we can show image from URL (web).
Here we are going to see about how to load the image from web in simple way.
Example for Android Load Image From Web :-
Add the below code to AndroidManifest.xml for Internet Permission.

 

<uses-permission android:name="android.permission.INTERNET"></uses-permission>

 

Edit your main.xml file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/LinearLayout01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView android:id="@+id/ImageView01"
android:layout_height="wrap_content" android:layout_width="wrap_content"/>
</LinearLayout>

Edit your java file

import java.io.InputStream;
import java.net.URL;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.ImageView;

public class ImageFromUrlExample extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ImageView imgView =(ImageView)findViewById(R.id.ImageView01);
        Drawable drawable = LoadImageFromWebOperations("http://www.androidpeople.com/wp-content/uploads/2010/03/android.png");
        imgView.setImageDrawable(drawable);

    }

   private Drawable LoadImageFromWebOperations(String url)
   {
  try
  {
   InputStream is = (InputStream) new URL(url).getContent();
   Drawable d = Drawable.createFromStream(is, "src name");
   return d;
  }catch (Exception e) {
   System.out.println("Exc="+e);
   return null;
  }
 }
}

output will looks like

 

http://www.androidpeople.com/category/image/


출처 : http://dingpong.net/tt/215
 프로그램을 만들다보면 '안녕하세요. 반갑습니다.' 왼쪽과 같이 부분적으로 색상을 변경해야 되는 경우가 발생할 수 있습니다. 또는 부분적으로만 Bold처리 등을 할 수도 있습니다. 이러한 경우 아래와 같은 코드를 사용할 수 있습니다.

final SpannableStringBuilder sp = new SpannableStringBuilder("안녕하세요.");
sp.setSpan(new ForegroundColorSpan(Color.RED), 1, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.append(sp);

 안드로이드펍에 어떤 글에서는 String에 Html태그가 적용이 된다고 되어 있는데, <b><u>와 같은 태그만 적용이 되었고 색상에 대해서는 잘 안되었습니다. (가능한 방법은 [이쪽] 자료를 참고해 보시길 바랍니다). 그래서 검색해서 이 방법을 찾아서 사용하였습니다.

 위 코드를 분석하면 "안녕하세요." 라는 텍스트의 1~3위치에 있는 텍스트를 RED 색상으로 변경해서 textView에 넣는 코드입니다. 여러 가지로 색상을 바꿔야하면sp를 Clear한 다음에 append로 텍스트를 넣어서 재활용 할 수 있습니다.

- 참고 자료
http://developer.android.com/reference ··· der.html
http://anddev.tistory.com/49
http://www.mailinglistarchive.com/andr ··· 030.html
http://www.androidpub.com/android_dev_info/51697

크리에이티브 커먼즈 라이센스
Creative Commons License

'Android > Tip&Tech' 카테고리의 다른 글

[펌]listview에 progress 적용참고 팁  (0) 2010.12.10
[펌]웹상의 이미지를 ImageView로 로딩하기  (0) 2010.12.10
아이콘 추천 사이트  (0) 2010.12.09
Tservice agent파일  (0) 2010.12.09
[펌]커스텀 뷰 확장하기  (0) 2010.12.08
에물 하나 바뀔때마다 귀찮은 파일 하나를 계속 설치를 해야함


안드로이드 Custom View를 이용하여 Custom Android Button 만들기 어플 개발 / 안드로이드

2010/04/22 12:33

복사 http://blog.naver.com/mygirl2/40105201905

출처 : http://androidcore.com/index.php?option=com_content&view=article&id=235&Itemid=106

 

Making a custom Android button using a custom view

 

Creating a custom view is as simple as inheriting from the View class and overriding the methods that need to be overridden. In this example, a custom button is implemented in this way. The button shall feature a labelled image (i.e. an image with text underneath).

 

사용자 정의 뷰(View)를 이용하여 사용자 정의 버튼(안드로이드용) 만들어 보기

 

사용자 정의 뷰(View)를 생성하는 것은 뷰 클래스를 상속하는 것만큼 쉽다. 그리고 오버라이드(Override) 해야 할 메소드를 오버라이드하는 것 만큼...

의역하면, 뷰 클래스를 상속하고 오버라이드 해줘야 할 부분만 오버라이드 해주면 되니까 사용자 정의 뷰를 만드는 것이 굉장히 쉽다는 얘기이다.

예를 들면, 사용자 정의 버튼은 이런 방식으로 구현이 되는데... 다음의 예제에서는 사용자 정의 버튼이 라벨이 표시되는 이미지와 밑에 텍스트가 추가되는 특징을 갖게 된다. 그럼 예제를 보자...



1   public class CustomImageButton extends View {
2       private final static int WIDTH_PADDING = 8;
3       private final static int HEIGHT_PADDING = 10;
4       private final String label;
5       private final int imageResId;
6       private final Bitmap image;
7       private final InternalListener listenerAdapter = new InternalListener();
8
 

The constructor can take in the parameters to set the button image and label.

 

생성자는 버튼 이미지와 라벨을 설정하기 위해 파라미터를 받을 수 있다.


9       /**
10       * Constructor.
11       *

// 생성자


12       * @param context
13       *        Activity context in which the button view is being placed for.

 

// 버튼 뷰가 배치되는 액티비티 문맥(context)

 

14       *
15       * @param resImage
16       *        Image to put on the button. This image should have been placed
17       *        in the drawable resources directory.

// 버튼에 이미지를 추가하기. 이 이미지는 drawable 리소스 디렉토리에 배치(보관, 저장?)되어야 한다.

 

18       *
19       * @param label
20       *        The text label to display for the custom button.
21       */

 

// 사용자 정의 버튼에서 표시될 텍스트 라벨


22      public CustomImageButton(Context context, int resImage, String label)
23       {
24           super(context);
25           this.label = label;
26           this.imageResId = resImage;
27           this.image = BitmapFactory.decodeResource(context.getResources(),
28                  imageResId);
29 
30           setFocusable(true);
31           setBackgroundColor(Color.WHITE);
32
33           setOnClickListener(listenerAdapter);
34           setClickable(true);
35       }
36
 

With the constructor defined, there are a number of methods in the View class that needs to be overridden to this view behave like a button. Firstly, the onFocusChanged gets triggered when the focus moves onto or off the view. In the case of our custom button, we want the button to be “highlighted” when ever the focus is on the button.

 

생성자가 정의 되고 나서 뷰 클래스는 몇가지 메소드들이 있다는 것도 고려해야 한다. 그것은 뷰 클래스에서 버튼과 같은 뷰가 가져야 할 것을 오버라이드해줘야 한다는 것이다. (위에 서두에 언급했던 내용...)

 

첫번째로 onFocusChanged 메소드는 발동을 언제하냐면, 포커스가 On되거나 Off될때이다. 뷰에서 대한 포커스 말이다.

여기에서 언급되는 사용자 정의 버튼의 경우, 버튼이 하이라이트(highlighted)되는 기능이 필요한데, 포커스가 버튼에 맞춰졌을때(Focus On 되었을때) 말이다.

 


49          {
50              this.setBackgroundColor(Color.WHITE);
51          }
52      }
53     
 

The method responsible for rendering the contents of the view to the screen is the draw method. In this case, it handles placing the image and text label on to the custom view.

 

setBackgroundColor() 메소드는 뷰의 내용들을 표현하는 메소드인데, 스크린에 표현하게 된다. 이 메소드는 draw 메소드 계열이다.

여기서 소개된 이 메소드는 이미지와 텍스트 라벨을 사용자 정의 뷰에 배치하는 것을 조정(컨트롤)한다.

 


54      /**
55       * Method called on to render the view.
56       */
57      protected void onDraw(Canvas canvas)
58      {
59          Paint textPaint = new Paint();
60          textPaint.setColor(Color.BLACK);
61          canvas.drawBitmap(image, WIDTH_PADDING / 2, HEIGHT_PADDING / 2, null);
62          canvas.drawText(label, WIDTH_PADDING / 2, (HEIGHT_PADDING / 2) +
63                  image.getHeight() + 8, textPaint);
64      }
65     
 

For the elements to be displayed correctly on the screen, Android needs to know the how big the custom view is. This is done through overriding the onMeasure method. The measurement specification parameters represent dimension restrictions that are imposed by the parent view.

 

요소들(elements)들이 화면에 제대로 표현되기 위해서 안드로이드는 사용자 정의 뷰의 크기가 얼마나 되는지에 대한 정보를 필요로 한다.

아래의 경우 onMeasure 메소드를 오버라이딩한 것이다. measurement(측정) 상세 내용(스펙) 파라미터는 부모 뷰(View)로 부터 결정된(강요된? 강제로 결정된?) 치수 제한을 나타낸다.

 


 66        @Override
 67        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
 68        {
 69         setMeasuredDimension(measureWidth(widthMeasureSpec),
 70                 measureHeight(heightMeasureSpec));
 71        }
 72   
 

The call to setMeasuredDimension in the onMeasure method is important. The documentation states that the call is necessary to avoid a IllegalStateException.

 

onMeasure 메소드 내에서 setMeasuredDimension() 메소드를 호출하는 것은 중요하다. 그 이유는 illegalStateException이 발생하는 것을 피하기 위해서이다. 이 메소드의 호출이 말이다. (문서에 표시된 내용 - 어떤 문서?)

 


 73     private int measureWidth(int measureSpec)
 74     {
 75         int preferred = image.getWidth() * 2;
 76         return getMeasurement(measureSpec, preferred);
 77     }
 78   
 79     private int measureHeight(int measureSpec)
 80     {
 81         int preferred = image.getHeight() * 2;
 82         return getMeasurement(measureSpec, preferred);
 83     }
 84   
 

To calculate the width and height measurements, I’ve chosen to keep the logic simple by using a simple formula to calculate the dimensions. This simple formula computes the dimensions based on the dimensions of the image. The measureSpec parameter specifies what restrictions are imposed by the parent layout.

 

가로폭과 세로높이 측정값을 계산하기 위해서, 이 글을 쓴 사람은 치수를 계산 하기 위한(할 수 있는) 간단한 공식을 이용했고 그렇게 한 이유는 프로그램 로직을 간결하게 유지하기 위한 방법이라고 생각했기 때문이다.

이 간단한 공식은 이미지의 치수에 기초(기반)을 둔 치수를 산출한다.(compute 뜻 까먹었었는데, 긴가민가했는데 찍었는데 맞췄다. ㅇㅇ)

measureSpec 파라미터는 부모 레이아웃으로 강요된 제한들을 명시한다.

 


 85     private int getMeasurement(int measureSpec, int preferred)
 86     {
 87         int specSize = MeasureSpec.getSize(measureSpec);
 88         int measurement = 0;
 89       
 90         switch(MeasureSpec.getMode(measureSpec))
 91         {
 92             case MeasureSpec.EXACTLY:
 93                 // This means the width of this view has been given.
 

// 이것은 주어진 뷰(View)의 가로폭을 의미한다.

 

 94                 measurement = specSize;
 95                 break;
 96             case MeasureSpec.AT_MOST:
 97                 // Take the minimum of the preferred size and what
 98                 // we were told to be. 

 

// 원하는 크기(size)의 최소값을 얻어라

// 뭔가 되기 위해서? 되어햐 할 것들에 대해 듣는 것.. (코드를 봐야 이해할 것 같다. 일단 나중에..)

 

 99                 measurement = Math.min(preferred, specSize);
100                 break;
101             default:
102                 measurement = preferred;
103                 break;
104         }
105   
106         return measurement;
107     }
108
 

To make the customised button useful, it needs to trigger some kind of action when it is clicked (i.e. a listener). The view class already defines methods for setting the listener, but a more specialised listener could be better suited to the custom button. For example, the specialised listener could pass back information on the instance of the custom button.

 

사용자 정의 버튼을 유용하게 만들기 위해서는 사용자 버튼에 몇가지 종류의 액션(Action)을 발동 시키는 것이 필요한데, 예를들어 리스너(listener)등으로 클릭했을 때 말이다.

뷰(View) 클래스의 경우 이미 리스너 설정에 필요한 메소드들을 정의되어 있다.

하지만 좀 더 특별하고 뛰어나다고 할까? 이러한 리스너는 사용자 정의 버튼에 좀 더 최적화 될 수 있다.

예를 들면, 이 특별한 리스너는 정보를 돌려줄 수 있는데, 사용자 정의 버튼의 객체(instance)에 말이다.


109     /**
110      * Sets the listener object that is triggered when the view is clicked.

 

// 리스너 객체를 설정하라. 뷰가 클릭되었을때 발동되는 리스너 객체 말이다.

 

111      *
112      * @param newListener
113      *        The instance of the listener to trigger.

 

// 발동되는 리스너 객체..

 

114      */
115     public void setOnClickListener(ClickListener newListener)
116     {
117         listenerAdapter.setListener(newListener);
118     }
119   
 

If the custom listener passes information about this instance of the custom button, it may as well have accessors so listener implementation can get useful information about this custom button.

 

사용자 정의 리스너가 사용자 정의 버튼 객체에 대한 정보를 제공한다면, 사용자 정의 리스너는 접근자를 가질지도 모른다. 리스너 구현하는 것이 사용자 정의 버튼에 대한 유용한 정보를 제공 할 수 있도록 말이다.

(의역하면, 사용자 정의 리스너가 사용자 정의 버튼 객체에 대해 뭔가를 제공한다면 전달하거나... 그러면 구현하는 리스너가 사용자 정의 버튼에 대해 유용한 정보를 가질 수 있도록 접근자를 가질지도 모른다는 의미가 될 것 같다. 코드를 안 보고 번역하니 헤깔린다.)


120     /**
121      * Returns the label of the button.

 

// 라벨 버튼을 반환하기


122      */
123     public String getLabel()
124     {
125         return label;
126     }
127   
128     /**
129      * Returns the resource id of the image.
130      */

 

// 이미지의 리소스 아이디(id)를 반환하기


131     public int getImageResId()
132     {
133         return imageResId;
134     }
135   
 

Finally, for our custom button class that is using a custom listener, the custom listener class needs to be defined.

 

마지막으로 사용자 정의 리스너를 사용하고 있는 여기에 소개된 사용자 정의 버튼에 대해 사용자 정의 리스너 클래스가 정의되어야 한다는 것을 말해두고 싶다.

 


136     /**
137      * Internal click listener class. Translates a view’s click listener to
138      * one that is more appropriate for the custom image button class.

 

/* 내부의 클릭 리스너 클래스(이것이 익명 클래스라고 불리기도 하는건가? 안 찾아봐서 잘 모르겠다.)

뷰(View)의 클래스를 전환(변환)해야 하는데, 사용자 정의 버튼 클래스에 좀 더 적합한 것으로 말이다.

(위에 설명된 translate라는 단어는 번역이라기 보다는 전환(변환)이 맞을 것 같다.) */

 

 

139      *
140      * @author Kah
141      */
142     private class InternalListener implements View.OnClickListener
143     {
144         private ClickListener listener = null;
145   
146         /**
147          * Changes the listener to the given listener.

 

리스너를 주어진 리스너로 변경해라


148          *
149          * @param newListener
150          *        The listener to change to.

 

바꿀 리스너

 

151          */
152         public void setListener(ClickListener newListener)
153         {
154             listener = newListener;
155         }
156       
157         @Override
158         public void onClick(View v)
159         {
160             if (listener != null)
161             {
162                 listener.onClick(CustomImageButton.this);
163             }
164         }
165     }
166    }
167  

출처 : http://underclub.tistory.com/333

안드로이드의 커스텀 컴포넌트에 관한 글입니다.



커스텀 확장 뷰 생성하기

안드로이드의 기초 레이아웃인 뷰와 뷰그룹에서는 컴포넌트화 된 모델을 제공해 줍니다. UI 를 디자인하는데 필요한 미리 정의된 뷰와 뷰 그룹의 하위 클래스 ( 위젯과 레이아웃 ) 를 가지고 있습니다.

이런것들 중에서 새로운 것을 만들기 위해 자신만의 뷰 클래스를 만들수도 있고, 기존의 위젯-레이아웃을 조금만 수정하고 싶다면 상속받은 뒤 메소드를 오버라이드 하는 방식을 사용합니다.

순서
1. 사용할 클래스에서 기존의 뷰 또는 뷰 하위클래스를 상속 받습니다.

2. 슈퍼클래스의 메소드를 오버라이드 합니다. ( 오버라이드할 슈퍼클래스의 메소드들은 on....() 으로 시작됩니다. )

3. 새로운 클래스를 기존의 뷰클래스를 대신하여 사용합니다.
[ 상속받은 클래스는 액티비티 내부에서 이너클래스(Inner Class) 로 정의할 수 있는데요, 이것은 그 액티비티 내부에서만 쓰일 수 있으므로, 어플리케이션 내에서 더 넓게 사용할 수 있는 public 뷰를 만드는것이 더 유리합니다 ]


완전한 커스텀 컴포넌트

완전한 커스텀 컴포넌트란, 만들고자 하는 어떠한 디자인으로도 컴포넌트를 만들 수 있다는 것을 의미합니다. 예를들어, " A " 라는 디자인을 하고싶은데, 이것이 내장된 컴포넌트를 어떻게 조합하더라도 만들 수 없는 경우가 있을 수도 있습니다.

즉, 원하는 방식으로 보여지고 동작하는 컴포넌트를 만들 수 있다는 얘기인데, 구글에서는 상상력과 스크린의 크기, 사용가능한 프로세스 파워만이 제약조건이 될 수 있다고 말하고 있네요 ㅎㅎ 재미있군요 ㅎ

커스텀 컴포넌트의 생성하는 순서
1. 뷰 ( View ) 클래스를 상속합니다.

2. XML 내에 속성과 파라미터를 가지는 생성자를 만들수 있고, 그것을 사용할 수 있습니다.

3. 이벤트 리스너, 속성, 접근자 및 수정자 등을 추가하여 동작을 결정합니다.

4. onMeasure() 메소드와 onDraw() 메소드를 오버라이드하여 구현을 해야합니다. 이때 둘다 디폴트 동작구조는 onDraw() 는 아무런 일도 하지않고, onMeasure() 는 100X100 의 크기로 설정되기 때문에 알맞게 수정해야 합니다.

5. 추가적으로 필요한 메소드들을 추가합니다.


onDraw() 와 onMeasure() 구현하기

onDraw() 는 캔버스(Canvas) 를 파라미터로 전달하며, 그 캔버스위에 원하는 것. 즉, 2D 그래픽이나 기타 다른것들을 구현합니다
이것은 3D 그래픽에는 적용되지 않는데, 3D 그래픽을 쓰려면 서피스뷰(SurfaceView) 를 상속해야 하며, 별도의 스레드에서 그리기를 해야되기 때문입니다.



onMeasure() 는 정확한 값을 가지고 오버라이드 해야되는데요, 부모(parent) 의 요구조건과 측정된 너비와 높이를 가지고 setMeasuredDimension() 메소드를 호출해야만 합니다. 만약 이 메소드를 호출하지 않으면 측정 시점에서 예외가 발생합니다.





onMeasure() 메소드 구현 순서
1. onMeasure() 메소드에 너비와 높이를 파라미터로 전달합니다. ( widthMeasureSpec, heightMeasureSpec 은 정수형 값 입니다. )

2. onMeasure() 메소드에서 요구하는 너비와 높이를 계산합니다. 즉, 요구하는 값이 부모의 값보다 크다면 부모는 잘라내기, 스크롤, 예외발생, 재시도( onMeasure() 다시호출) 등을 선택할 것입니다.

3. 계산되어진 너비와 높이를 가지고 setMeasuredDimension(int width, int height) 를 호출합니다. 위에서 언급했지만 호출하지 않으면 예외가 발생합니다.


▶ 그 밖의 뷰에서 프레임워크가 호출하는 다른 메소드들..
⊙ 생성자 : 소스코드에서 뷰가 생성될 때 호출되는 형태와, 레이아웃 파일에서 뷰가 전개(inflate) 될 때 호출되는 형태 두가지가 있습니다. 두번째 형태는 레이아웃에 정의된 속성을 분석한 후 적용합니다.

onFinishInflate()뷰와 그 하위 자식들이 XML 에서 전개 된 후 호출됩니다.

onMeasure(int, int) : 뷰와 그 자식들에 대한 크기를 결정하기 위해 호출됩니다.

onLayout(boolean, int, int, int, int) : 뷰가 그 자식들에게 크기와 위치를 할당할 때 호출됩니다.

onSizeChanged(int, int, int, int) : 뷰의 크기가 바뀔 때 호출됩니다.

onDraw(Canvas) : 뷰가 컨텐츠를 그릴 때 호출되지요

onKeyDown(int, KeyEvent) : 새로운 키 이벤트 발생시 호출됩니다.

onKeyUp(int, KeyEvent) : 키 업 이벤트 발생시에 호출됩니다.

onTrackballEvent(MotionEvent) : 트랙볼 모션 이벤트 발생시에 호출됩니다.

onTouchEvnet(MotionEvent) : 터치스크린의 모션 이벤트 발생시에 호출됩니다.

onFocusChanged(boolean, int, Rect) : 뷰가 포커스를 가지거나 잃을 때 호출됩니다.

onWindowFocusChanged(boolean) : 뷰를 포함한 윈도우가 포커스를 가지거나 잃을 때 호출됩니다.

onAttachedToWindow() : 뷰가 윈도우에 포함될 때 호출됩니다.

onDetachedFromWindow() : 뷰가 윈도우에서 분리될 때 호출됩니다.

onWindowVisibillityChanged(int) : 뷰를 포함한 윈도우가 보여지는 상태가 변할 때 호출됩니다.


커스텀 뷰 예제

커스텀 뷰 예제는 안드로이드에서 제공하는 샘플들 중에서 API Demos 의 CustomView 예제가 있는데, 이 예제의 컴스텀 뷰는 LabelView 클래스에서 정의되어 있습니다.



LabelView 샘플은 커스텀 컴포넌트를 이해하기에 아주 적합합니다.
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.android.apis.view;

// Need the following import to get access to the app resources, since this
// class is in a sub-package.
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

import com.example.android.apis.R;


/**
* Example of how to write a custom subclass of View. LabelView
* is used to draw simple text views. Note that it does not handle
* styled text or right-to-left writing systems.
*
*/
public class LabelView extends View {
    private Paint mTextPaint;
    private String mText;
    private int mAscent;
    
    /**
* Constructor. This version is only needed if you will be instantiating
* the object manually (not from a layout XML file).
* @param context
*/
    public LabelView(Context context) {
        super(context);
        initLabelView();
    }

    /**
* Construct object, initializing with any attributes we understand from a
* layout file. These attributes are defined in
* SDK/assets/res/any/classes.xml.
*
* @see android.view.View#View(android.content.Context, android.util.AttributeSet)
*/
    public LabelView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initLabelView();

        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.LabelView);

        CharSequence s = a.getString(R.styleable.LabelView_text);
        if (s != null) {
            setText(s.toString());
        }

        // Retrieve the color(s) to be used for this view and apply them.
        // Note, if you only care about supporting a single color, that you
        // can instead call a.getColor() and pass that to setTextColor().
        setTextColor(a.getColor(R.styleable.LabelView_textColor, 0xFF000000));

        int textSize = a.getDimensionPixelOffset(R.styleable.LabelView_textSize, 0);
        if (textSize > 0) {
            setTextSize(textSize);
        }

        a.recycle();
    }

    private final void initLabelView() {
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(16);
        mTextPaint.setColor(0xFF000000);
        setPadding(3, 3, 3, 3);
    }

    /**
* Sets the text to display in this label
* @param text The text to display. This will be drawn as one line.
*/
    public void setText(String text) {
        mText = text;
        requestLayout();
        invalidate();
    }

    /**
* Sets the text size for this label
* @param size Font size
*/
    public void setTextSize(int size) {
        mTextPaint.setTextSize(size);
        requestLayout();
        invalidate();
    }

    /**
* Sets the text color for this label.
* @param color ARGB value for the text
*/
    public void setTextColor(int color) {
        mTextPaint.setColor(color);
        invalidate();
    }

    /**
* @see android.view.View#measure(int, int)
*/
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec),
                measureHeight(heightMeasureSpec));
    }

    /**
* Determines the width of this view
* @param measureSpec A measureSpec packed into an int
* @return The width of the view, honoring constraints from measureSpec
*/
    private int measureWidth(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            // We were told how big to be
            result = specSize;
        } else {
            // Measure the text
            result = (int) mTextPaint.measureText(mText) + getPaddingLeft()
                    + getPaddingRight();
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by measureSpec
                result = Math.min(result, specSize);
            }
        }

        return result;
    }

    /**
* Determines the height of this view
* @param measureSpec A measureSpec packed into an int
* @return The height of the view, honoring constraints from measureSpec
*/
    private int measureHeight(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        mAscent = (int) mTextPaint.ascent();
        if (specMode == MeasureSpec.EXACTLY) {
            // We were told how big to be
            result = specSize;
        } else {
            // Measure the text (beware: ascent is a negative number)
            result = (int) (-mAscent + mTextPaint.descent()) + getPaddingTop()
                    + getPaddingBottom();
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by measureSpec
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    /**
* Render the text
*
* @see android.view.View#onDraw(android.graphics.Canvas)
*/
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent, mTextPaint);
    }
}


뜯어보자면...

1. 완전한 커스텀 컴포넌트를 위해 뷰 클래스를 상속 받았습니다.

2. 생성자는 inflation 파라미터를 가지고 있습니다 ( XML 에서 정의됨 )
또한 LabelView 를 위해 정의된 속성들을 가지고 있지요.

3. 라벨에 표시할 public 메소드를 사용하고 있습니다
( setText(), setTextColor() 등 )

4. 그리기를 수행할 때 필요한 크기를 설정하기 위하여 onMeasure() 메소드가 있습니다.
이 예제의 실제 작업처리는 private measureWidth() 메소드에서 수행하도록 되어 있네요

5. 캔버스에 라벨을 그리기 위해 onDraw() 메소드를 오버라이드 했습니다. 추가로 custom_view_1.xml 에 보면 android: 네임스페이스 와 app: 네임스페이스가 있는데요, app: 네임스페이스는 LabelView 가 작업하는 커스텀 파라미터이며, R 클래스의 styleable 내부클래스에서 정의됩니다.


합성 ( Compound ) 컴포넌트

완전한 커스텀 컴포넌트를 만들기 보다는, 기존의 컨트롤들을 조합하여 구성하는 컴포넌트를 만들려면 합성 컴포넌트를 사용하면 됩니다.

이것은 하나로 취급될 수 있는 아이템들을 그룹안에서 합쳐놓는 방식이며, 안드로이드에는 이런것을 위해 제공되는 두 개의 뷰가 존재합니다.
바로 SpinnerAutoCompleteTextView 가 그것이지요.

합성 컴포넌트 만들기
1. 일반적인 시작점은 레이아웃이 됩니다. 따라서 하나의 레이아웃을 확장하는 클래스를 만들고, 컴포넌트가 구조화 될 수 있도록 다른 레이아웃을 포함합니다. 컴포넌트에 포함된 레이아웃은 XML 에서 선언하거나 소스코드에서 추가할 수 있습니다.

2. 새로운 클래스의 생성자에서 슈퍼클래스의 생성자를 먼저 호출해 줍니다. 그 다음에 새로운 컴포넌트 안에서 다른 뷰를 구성합니다. 이것 또한 XML 로 선언할 수 있으며, 속성과 파라미터를 만들어 사용할 수도 있습니다.

3. 알맞은 이벤트 리스너를 구현해 줍니다.

4. 레이아웃을 상속하는 경우에는 onDraw() 나 onMeasure() 메소드를 오버라이드 하지 않아도 됩니다. 왜냐하면 레이아웃은 디폴트 동작 구조가 있기 때문입니다. 만약 필요하다면 오버라이드 해도 무방합니다.

5. onKeyDown() 메소드 같은 다른 on....() 메소드를 오버라이드 할 수 있습니다.


▶ 커스텀 컴포넌트를 레이아웃으로 사용하면서의 장점은 무엇일까요?
1. XML 로 레이아웃을 만들 수 있고, 소스코드에서 동적으로 만들어서 추가할 수도 있습니다.

2. onDraw() 와 onMeasure() 메소드를 오버라이드 하지 않아도 됩니다.

3. 복잡한 뷰를 생성할 수도 있고, 그것들이 하나의 컴포넌트처럼 재사용 할 수도 있습니다.

합성 컴포넌트 예제

합성 컴포넌트도 마찬가지로 안드로이드 샘플의 API Demos 의 Views/Lists 안에 있는 Example4 와 Example6 클래스가 해당되는 예제입니다.

이것들은 SpeechView 기능을 합니다. SpeechView 란 말 그대로 연설문 보여주기 같은 기능입니다.

자바 클래스는 List4.java 와 List6.java 클래스이고, 이 클래스는 합성 컴포넌트 생성을 위해 LinearLayout 을 상속했네요~







커스텀 컴포넌트에 관한 글이었습니다.
꽤 긴글이 되어 버렸네요...;;

다음 포스팅엔 커스텀 컴포넌트에 이어지는 글인 이미 존재하는 뷰를 수정해서 커스텀 뷰를 만드는 것을 살펴보죠


[안드로이드] 오픈소스 받아서 실행해 보기 (리코더) Android

2010/11/15 11:42

복사 http://blog.naver.com/dythmall/30097179951

주의) SVN을 좀 아셔야 이 내용이 이해가 될 수 있습니다


안드로이드의 전체 소스는 공개가 되어 있습니다.

누구나 다운 받아서 보고 쓸 수 있지요.

이번에는 안드로이드 오픈소스를 받아서 빌드하고 실행해 보는 방법을 알아보겟습니다.


안드로이드 오픈소스라고 하면 부트로더, 리눅스 커널, 안드로이드 런타임, 등등 여러 가지의 Software Stack이 같이 있는데요

개발자들이 쉽게 보고 따라할 수 있는 Apps (안드로이드에 제공되는 기본엡)를 다운받아서 빌드해 보겟습니다.

예제는 SoundRecorder를 쓰겟습니다 (이게 젤 쉽고 간단합니다 ㅎㅎ)


윈도우 사용자라면 git을 직접 쓰기엔 좀 많은 작업이 필요 함으로 gitweb을로 snapshot을 받아오겟습니다

http://android.git.kernel.org/


이 경로로 가면 안드로이드 오픈소스의 천체 트리가 보입니다.

우리가 원하는건 그중 

platform/packages/apps/SoundRecorder.git

이것!

 

우선 처음에는 좀 헷 갈릴 수도 있습니다

보아야 할 곳은 shorLog 정도를 보면 되겟습니다.

이곳이 중요한 release들을 모아놓은 곳 입니다.

이중에도 더 중요한 release는 녹색 바탕으로 label을 부쳐 놓았습니다.




 오른쪽에 보이는 commit은 commit햇을때의 정보와 자세 로그를 볼 수 있습니다.

commitdiff는 말 그대로 commit된 파일들의 diff

tree는 그 revision때의 소스 트리 구조

snapshot - (이 부분이 저희에게 중요한 부분입니다) commit당시의 소스를 받아볼 수 있습니다.


그럼 eclair (안드로이드 2.1) 소스를 받아 봅니다.




snapshot을 누르면 tar.gz로 압축된 파일이 받아 집니다.

이것 (빵집이나 7zip으로 풀 수 있습니다) 압축을 풀면 SoundRecorder라는 폴더가 생성 됩니다.

이제 Eclipse project를 이 소스를 이용해서 만듭니다.

[File]->[new]->[Android Project]




New Android Project 다이얼로그에서 위와 같이 세팅을 해야 합니다.

ProjectName은 그대로 두시고

create Project from existing source를 선택 합니다.

그리고 soundrecorder가 있는 폴더를 선택 하면 모든 내용이 자동으로 입력 됩니다.

여기서 안드로이드 build target은 직접 선택 해야 합니다. (2.1로 선택해 주세요)

Finish를 누르면 SoundRecorder 프로젝트가 생성 됩니다.


우선은 빨간 x가 뜹니다..

찾아가 보면 SoundRecorder.java의 다음과 같은 라인에서 에러가 납니다.


mRecorder.startRecording(MediaRecorder.OutputFormat.AMR_NB, ".amr");


AMR_NB가 바뀌었나 봅니다.


mRecorder.startRecording(MediaRecorder.OutputFormat.RAW_AMR, ".amr");


이것으로 바꾸어 주면 에러가 사라집니다.

오 벌써 되는건가? 하고 build 버튼을 누르면 다음과 같은 메시지가 뜰겁니다.


 Re-installation failed due to different application signatures.

 You must perform a full uninstall of the application. WARNING: This will remove the application data!

 Please execute 'adb uninstall com.android.soundrecorder' in a shell.

 Launch canceled!


이미 같은 프로그램이 존재한답니다 (그쵸... 안드로이드 기본 어플이니 있겟죠 ㅎㅎㅎ)

근데 제 signing key가 구글의 key랑 달라서 지금 있는 어플을 없애고 하랍니다....

이건 불가능 합니다.  그럼 어덯게 할까요?


간단합니다 안드로이드는 2개의 앱이 같은 앱인가 아닌가를 package 이름을 보고 압니다. 

그래서  꼭꼭 내 어플을 만들때는 나만의 URL을 이용해서 만들라고 주의를 줍니다 (안그러면 이런 일이 많이 일어나겟죠)

그럼 package 이름을 바꾸죠 refactoring을 합니다. 저는 com.android.soundrecorder2 라고 바꾸었습니다.

http://blog.naver.com/dythmall/30096460128 - refactoring 하는 방법은 여기에 설명이 되어 있습니다.


xml부분도 고쳐야 하는데 그 부분은 다음과 같은 옵션을 주면 바꿀 수 있습니다.






OK를 누르면 package이름이 바뀌게 됩니다.

그런데 또 x가 나옵니다. 이번에는 


import com.android.soundrecorder.R;


이 부분인데요, 이곳은 수동으로 고칠 수 밖에 없습니다.

에러가 없어지면 이제 다시 빌드해서 실행해 봅니다.

근데 이번에는 인스톨은 되는데 실행이 안됩니다. ㅡ.ㅡ


AndroidManifest.xml을 열어봅니다. 

가만히 살펴보면 자주 보이던 intent filter가 보이지 않습니다.


            

            

            

            


이 필터를 넣고 다시 시도합니다!





앗! 이제야 되는 군요.


성공입니다.


사실 이 예제는 쉬운 축에 속합니다. 겔러리나 음악 프로그램은 조금더 신경써야하는 부분들이 있더군요


[펌] Android 개발 팁 50선 Android

2010/05/10 10:15

복사 http://blog.naver.com/kopidat/40106280563

 
개발중에 메모해둔 팁/힌트입니다.
한참을 헤매다가 때로는 3일에 1개 만들기도 하고, 하루에 1개 겨우 구현하기도 했던 
검색력 향상(?)를 꾀한 개발.
50개가 되었기에 포스팅을 걸어둡니다.
또 50개 모으면 2탄을;
(요약 부분이 있는데 잘려서 입력되네..)

번호

제목

출처

요약

1

이미지와 텍스트가 같이 들어간 버튼 만들기

http://www.androidpub.com/15765

<Button
android:layout_width="fill_parent"
android:layout_width="wrap_content"
android:drawableLeft="@drawable/ic_settings"
android:drawablePadding="4dp"
android:text="Settings"
/>
 
Button button = new Button(mContext);
button.setText("Close");
Drawable close = Drawable.createFromPath("/data/icon/image.png");
close.setBounds(0, 0, close.getIntrinsicWidth(), close.getIntrinsicHeight());

button.setCompoundDrawables(close, null, null, null);

2

버튼 색깔 바꾸기

http://stackoverflow.com/questions/1521640/standard-android-button-with-a-different-color

 

<?xml version="1.0" encoding="utf-8"?>    
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:drawable="@drawable/red_button_pressed" />
    <item android:state_focused="true" android:drawable="@drawable/red_button_focus" />
    <item android:drawable="@drawable/red_button_rest" />
</selector>

3

전체화면 사용하기 (Status bar, Title bar 숨기기)

http://www.androidpub.com/4710

 

android:theme="@android:style/Theme.NoTitleBar.Fullscreen"

android:theme="@android:style/Theme.NoTitleBar"

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,

 WindowManager.LayoutParams.FLAG_FULLSCREEN);

 

4

ImageButton의 투명 효과 사용하기

http://joywoni.egloos.com/2847047

 

android:background="#a0000000" 를 설정하면 배경이 투명해지므로 버튼 모양을 안봐도 된다.

5

Android 정리문서

http://sgap.springnote.com/pages/5076381

 

SurfaceView와 SurfaceHolder.Callback, thread

6

네이티브 안드로이드 개발 관련 블로그

http://hybridego.net/

 

 

7

안드로이드 개발 각종 예제 소스

http://www.androidpeople.com/2010/01/

 

 

8

메뉴별 이미지 처리

http://stackoverflow.com/questions/2065430/fixed-android-detecting-focus-pressed-color

 

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true"
        android:drawable="@android:drawable/btn_default" ></item>
    <item android:state_focused="true"
        android:drawable="@android:drawable/btn_default" ></item>
    <item android:drawable="@drawable/btn_default_red" ></item>
</selector>

9

객체 Style 처리

http://www.anddev.org/viewtopic.php?p=37330

 

 

10

Button Highlight

http://www.androidpeople.com/category/android-tutorial/

 

 

11

SurfaceView

http://vissel.tistory.com/92

 

 

12

android:configChanges

http://www.androidpub.com/52338

 

android:configChanges="orientation"

onConfigurationChanged()

13

전원관리

http://samse.tistory.com/entry/AlarmManager-PowerManager

 

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);

 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");

 wl.acquire();

   ..screen will stay on during this section..

 wl.release();

14

하드웨어 콘트롤 관련 PDF 문서

http://mikechen.com/classes/2009-11-27%20NTU%20Mobile%20Phone%20Programming%20-%20Mike%20Chen%20-%2010%20-%20Security,%20Camera,%20Audio%20and%20Video.pdf

 

 

15

unique device ID

고유값 가져오기

http://developer.android.com/reference/android/telephony/TelephonyManager.html#getDeviceId%28%29

 

TelephonyManager mTelephonyMgr = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);

String imei = mTelephonyMgr.getDeviceId();

16

안드로이드 네이티브 라이브러리Ⅰ

http://www.imaso.co.kr/?doc=bbs/gnuboard.php&bo_table=article&wr_id=34284

 

[다른블로그] http://infodev.tistory.com/322

17

Introduction android

http://yotteum.tistory.com/entry/Introduction-Android

 

안드로이드 소개

바인딩 설명

18

안드로이드 - 버튼 OnClickListener 인터페이스 구현

http://woosa7.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EB%B2%84%ED%8A%BC-OnClickListener-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EA%B5%AC%ED%98%84

http://www.cyworld.com/kkjw1801/3222534

 

 

19

Android - Change Tab Background

http://escomic.net/218

 

TabWidget에서 추가되는 Tab의 Background변경하기

Tab마다 View를 얻어와서 직접 BackgroundDrawable을 지정하고

아래 막대부분은 reflection을 이용하여 꽁수로 바꿔치기 한다

 

tab_indicator.xml, tab_bar_left.xml, tab_bar_right.xml 내용은 <selector>로 정의

20

KH5200 드라이버 설치

http://kil.imradriss.co.cc:8000/tc/30

 

 

21

DrawableTop 이미지 변경하기

http://www.androidpub.com/10154

 

보기 1 ================================================
Drawable img = context.getResources().getDrawable(R.drawable.filename);
Drawable img2 = img ;

보기 2 ================================================
Drawable img = context.getResources().getDrawable(R.drawable.filename);
Drawable img2 = context.getResources().getDrawable(R.drawable.filename);

보 기
 3 ================================================
Drawable img = context.getResources().getDrawable(R.drawable.filename);
Drawable img2 = context.getResources().getDrawable(R.drawable.filename2);
================================================

22

Layout 사이즈 동적변경

http://www.androidpub.com/40481

http://gall.dcinside.com/list.php?id=accident2&no=1195485

LinearLayout ll = (LinearLayout)findViewById(R.id.write_LinearLayout);

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,

400);  // 400 이라는 높이를 지정

ll.setLayoutParams(params);

23

Android UI 개발기: XML 안쓰고 UI 코딩하기

http://xrath.com/2009/11/android-ui-%EA%B0%9C%EB%B0%9C%EA%B8%B0-xml-%EC%95%88%EC%93%B0%EA%B3%A0-ui-%EC%BD%94%EB%94%A9%ED%95%98%EA%B8%B0/

 

 

24

전화상태 변화감지 리스너

PhoneStateListener 예제

http://www.kandroid.org/board/board.php?board=AndroidTechQnA&page=124&command=body&no=432

 

MyPhoneStateListener phoneListener=new MyPhoneStateListener();

TelephonyManager telephonyManager  =(TelephonyManager)getSystemService(TELEPHONY_SERVICE);

telephonyManager.listen(phoneListener,PhoneStateListener.LISTEN_CALL_STATE);

 

public class MyPhoneStateListener extends PhoneStateListener {...}

25

안드로이드 하드웨어관련 자료(통화,폰상태,네트워크,카메라,센서)

http://gtko.springnote.com/pages/5396297

http://developer.android.com/reference/android/content/Intent.html

http://developer.android.com/reference/android/net/ConnectivityManager.html

android.net.conn.CONNECTIVITY_CHANGE

 

26

sms 수신해서 요약내용 보여주기

http://www.anddev.org/recognize-react_on_incoming_sms-t295.html

http://flytop.tistory.com/62

android.provider.Telephony.SMS_RECEIVED

 

// SMS 수신 감지 등록

IntentFilter smsRcvFilter = new IntentFilter(CSmsReceiver .ACTION);

smsReceiver =  new CSmsReceiver();

registerReceiver(smsReceiver, smsRcvFilter);

 

//if(smsReceiver != null) {

//    unregisterReceiver(smsReceiver);

//}

 

<!-- SMS Broadcast Receiver 등록 -->

<receiver android:name=".common.CSmsReceiver">

<intent-filter>

<action android:name="android.provider.Telephony.SMS_RECEIVED" />

</intent-filter>

</receiver>

27

BroadcastReceiver XML설정하기

http://www.androidpub.com/186727

<receiver android:name="리시버클래스" android:enabled="true">
<intent-filter><action android:name="android.net.conn.CONNECTIVITY_CHANGE" /></intent-filter>
</receiver>

28

각종 Management 클래스

http://www.imaso.co.kr/?doc=bbs/gnuboard.php&bo_table=article&page=10&wr_id=34565

29

Dialog 구조 분석

(아이콘,텍스트 위치등)

http://sgap.springnote.com/pages/5235569

 

30

SMS 수신시 Toast 메시지 출력

http://www.androidpub.com/138352

 

Intent sendIntent = new Intent(Intent.ACTION_SEND); sendIntent.addCategory("android.intent.category.DEFAULT");      

sendIntent.putExtra("address", PhoneNumberUtils.formatNumber(phoneNumber));

sendIntent.putExtra("exit_on_sent", true);

sendIntent.putExtra("subject", "TEST MMS");

sendIntent.putExtra("sms_body", "MMS 테스트입니다.");

context.startActivity(sendIntent);

31

Broadcast Receiver :네트워크상태 체크

http://www.anddev.org/viewtopic.php?p=32088

 

OnReceive 메소드 내에서..

ConnectivityManager connec=  (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);

if(connec.getNetworkInfo(0).getState()==NetworkInfo.State.CONNECTED||connec.getNetworkInfo(1).getState()==NetworkInfo.State.CONNECTING ) {

text.setText("hey your online!!!");

//Do something in here when we are connected

}

elseif(connec.getNetworkInfo(0).getState()== NetworkInfo.State.DISCONNECTED||connec.getNetworkInfo(1).getState() == NetworkInfo.State.DISCONNECTED ) {

text.setText("Look your not online");

}

32

안드로이드 API기능 설명

http://www.jopenbusiness.com/mediawiki/index.php/Android

 

 

33

Actions for BroadcastReceiver

http://www.dearsoft.org/tag/broadcastreceiver/

 

 

34

Layout.out.xml 생성되는 문제

http://www.androidpub.com/101585

main.out.xml 파일은 삭제해 주세효~
그건 xml 을 펼쳐둔 상태에서 Run 을 하면 만들어 지는데, 그럼 제대로 실행이 되지 않아효~

35

Multi Touch

http://www.mentby.com/naya/multitouch-support-in-android-20.html

http://gist.github.com/324166

2.0 부터 지원

36

ScrollView 스크롤 하단으로 내리기

http://www.anddev.org/viewtopic.php?p=36823

sv.post(new Runnable() {

             public void run() {

                           sv.fullScroll(ScrollView.FOCUS_DOWN);

             }

});

37

Timer 만들기

http://developer.android.com/intl/de/resources/articles/timed-ui-updates.html

http://www.developer.com/java/ent/print.php/3589961

http://www.androidpub.com/4374

http://blog.inculab.net/25

 

38

Logcat 동작안하는 에러 발생시 처리

(Could not create the view: For input string: "")

http://www.mail-archive.com/android-developers@googlegroups.com/msg60683.html

hide details Aug 18
 
I have had the same problem.
 
The logcat view crashes after I inserted a filter containing a ":" in
the filtername.
I solved the problem by changing the settings in the file
".metadata/.plugins/org.eclipse.core.runtime/.settings/
com.android.ide.eclipse.ddms.prefs" placed in the workspace of
eclipse.
com.android.ide.eclipse.ddms.logcat.filters= was the key of the fault
setting.

 

39

SSL 인증서 등록하기

http://www.java2go.net/blog/197?TSSESSION=1202a1a23fa67bae15ce3ab15a5a0cea

http://www.virgo81.net/70

http://crazybob.org/2010/02/android-trusting-ssl-certificates.html

http://www.bouncycastle.org/latest_releases.html

keytool -import -keystore cacerts -file C:\cert\TrialRootCA.cer -alias afcert

40

Signing By Private Key

http://www.devx.com/wireless/Article/39972/1954

 

41

영상 녹화하기

http://www.anddev.org/viewtopic.php?p=24723#24723

 

42

SurfaceView 의 이해

http://androidhuman.tistory.com/entry/카메라를-이용하자-SurfaceView에-대한-이해

 

43

안드로이드 JAVA 소스

http://anddev.tistory.com/77

http://anddev.tistory.com/50

http://anddev.tistory.com/42

{SDK_LOCATION}/platforms/1.5/sources

44

SSL 인증서 우회하기

http://www.experts-exchange.com/Programming/Languages/Java/Q_23063074.html

http://7bee.j2ee.us/blog/2008/03/28/1206704820000.html

 

 

45

JAVA SSL 관련 공식문서

http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html

http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#CreateKeystore

http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#CodeExamples

http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/samples/sockets/client/SSLSocketClient.java

 

46

SSL 인증서 증명에러 해결하기

http://blog.naver.com/PostView.nhn?blogId=fidelis98&logNo=140103425406&redirect=Dlog&widgetTypeCall=true

http://code.google.com/p/android/issues/detail?id=1946

http://developer.android.com/intl/de/reference/javax/net/ssl/TrustManager.html

http://mail-archives.apache.org/mod_mbox/hc-httpclient-users/200906.mbox/

http://www.exampledepot.com/egs/javax.net.ssl/GetCert.html?l=rel

http://www.android-portal.com/2007/12/20/secure-server-socket-with-tlsssl-on-android-fails/

http://www.exampledepot.com/egs/javax.net.ssl/TrustAll.html?

http://blog.keduall.co.kr/lsb76/entry/자바-SSL-접속-오류

해결방법: http://www.exampledepot.com/egs/javax.net.ssl/TrustAll.html

 

// Create a trust manager that does not validate certificate chains

TrustManager[] trustAllCerts = new TrustManager[]{

   new X509TrustManager() {

        public java.security.cert.X509Certificate[] getAcceptedIssuers() {

             return null;

        }

        public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) {

        }

        public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) {

        }

    }

};

47

안드로이드 Service 에서 Activity 를 실행하는 방법 

http://blog.naver.com/huewu/110084868855

 

Intent i = new Intent(this, ServiceTest.class);

PendingIntent p = PendingIntent.getActivity(this, 0, i, 0);

try {

           p.send();

} catch (CanceledException e) {

           e.printStackTrace();

}

 

48

안드로이드 이미지(사진) 불러오기

http://shinluckyarchive.tistory.com/469

http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html

 

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
...
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;  
Bitmap src = BitmapFactory.decodeFile(fileListSDCard[i], options);
Bitmap resized = Bitmap.createScaledBitmap(src, 100, 100, true);

 

49

SSL 인증키 무조건 우회하기

http://groups.google.com/group/android-developers/browse_thread/thread/62d856cdcfa9f16e

public class _FakeX509TrustManager implements X509TrustManager { 
        private static TrustManager[] trustManagers; 
        private static final X509Certificate[] _AcceptedIssuers = new 
X509Certificate[] {};
 
        @Override 
        public void checkClientTrusted(X509Certificate[] chain, String 
authType) throws CertificateException {
 
        } 
        @Override 
        public void checkServerTrusted(X509Certificate[] chain, String 
authType) throws CertificateException {
 
        } 
        public boolean isClientTrusted(X509Certificate[] chain) { 
                return true; 
        } 
        public boolean isServerTrusted(X509Certificate[] chain) { 
                return true; 
        } 
        @Override 
        public X509Certificate[] getAcceptedIssuers() { 
                return _AcceptedIssuers; 
        } 
        public static void allowAllSSL() { 
                HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() 
             {
 
                        @Override 
                        public boolean verify(String hostname, SSLSession session) { 
                                return true; 
                        } 
                }); 
                SSLContext context = null; 
                if (trustManagers == null) { 
                        trustManagers = new TrustManager[] { new _FakeX509TrustManager() }; 
                } 
                try { 
                        context = SSLContext.getInstance("TLS"); 
                        context.init(null, trustManagers, new SecureRandom()); 
                } catch (NoSuchAlgorithmException e) { 
                        e.printStackTrace(); 
                } catch (KeyManagementException e) { 
                        e.printStackTrace(); 
                } 
                 
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory()); 
        } 
}

50

효과음 관련 자료(Creating Sound Effects in Android)

http://www.androidpub.com/257540#4

public void playSound(int index) {
               float streamCurrent = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);  
               float streamMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);      
               float streamVolume = streamCurrent / streamMax;   
               mSoundPool.play(mSoundPoolMap.get(index), streamVolume, streamVolume, 1, 0, 1f); 
}

 

 

필요성을 느낄때 : Activity에서 Service를 바인딩 한후 Activity에서 서비스 메소드는 간단히 호출되지만..


Service에서 Activity 메소드를 호출하는 건 힘들다는 걸 느꼈다..


그래서 공부한게 이 방법인 것 같다.^^;


출처 : 

http://blog.naver.com/dlgusrb5061?Redirect=Log&logNo=120101130132


아래의 설명은 안드로이드 프로그래밍을 기본적으로 한다고 생각하고 쓴다

 

1. 기본적으로 안드로이드 프로젝트를 생성한다.

 

2. 액티비티 에서 생성할 서비스 클래스를 만든다

 

3. .aidl 파일을 만든다

package com.service;                     // 패키지 이름 따라서..

 

interface state{                               // state 라는 인터페이스를 만든다  파일명도 state.aidl

         int getState();                        // 값을 주기 위한 함수
         int setState(int k);                  // 값을 입력하기 위한 함수

}

 

4. 위의 인터페이스를 이용하여 서비스에 bind를 생성한다.   Stub를 이용하여 만들수 있다.

// state.Stub 클래스

private final state.Stub binder = new state.Stub() {
           @Override
           public int getState() throws RemoteException {   
                      return 0;

           }
  
           @Override
           public int setState(int k) throws RemoteException {
                      return 0;
            }

};

 

synchronized private int getStatePageImpl() {
           return 0;

}

 

5. 서비스 클래스 에서 onBind함수의 return을 위의 binder로 해 준다

@Override
public IBinder onBind(Intent intent) {
           return binder;
}

 

6. 액티비티 클래스 에서 ServiceConnection 을 이용하여 서비스가 시작될 때와 종료될 때 상황을 구현한다

 

import android.content.ServiceConnection;

 

private ServiceConnection serviceConn = new ServiceConnection() {
     public void onServiceConnected(ComponentName className, IBinder binder) {
            service = state.Stub.asInterface(binder);
     }

 

     public void onServiceDisconnected(ComponentName className) {
            

     }

};

 

7. 서비스 시작과 종료를 구현한다

 

<시작>

serviceintent = new Intent(this, mainService.class);
bindService(serviceintent, serviceConn, BIND_AUTO_CREATE);

 

<종료>

unbindService(serviceConn);


==========================자세한 설명


AIDL를 사용하여 원격 언터페이스를 사용하거나 설계(디자인)하기   - 번역 2008년 2월 20일 김문섭 (Easygn Institute)


그 동안 각각의 어플리케이션은 자신이 해당되는 프로세스에서 실행되며, 다른 프로세스가 실행시되는
응용프로그램 인터페이스나 프로세스 사이에 개체를 전달해야 할 경우에 당신은 서비스를 쓰기가 가능합니다.
기본 적으로 안드로이드 플랫폼 상에서 한 프로세스는 메모리상에서, 다른 프로세스에 대한 정상적인 액세스가 불가능합니다. 
한마디로, 그들은 오브젝트 안에서 운영체제 시스템이 이해할 수 있도록 세분화된 개체를 필요로 합니다,
그리고 오브젝트 간의 구획화 작업을 당신이 하도록 합니다.

그 코드를 구획화 하는 작업은 골치아픈 작업이고, 그래서 우리는 당신을 위해 AIDL 도구를 제공하게 되었습니다.

AIDL (안드로이드 인터페이스 정의 언어)는 하나의 안드로이드 장치 위에서 잠김프로세스 통신규약(IPC)에 의해 
두 프로세스들 사이에 대화가 가능하도록 하기위한 목적으로 생성되어진 코드를 사용할 수 있게하는 IDL 언어 입니다. 


만약 당신이 한 프로세스(예: Acitivity객체) 안의 코드가 
또 다른 프로세스(예: Service 객체) 안의 한 오브젝트 위의것을 필요로 하게 된다면 
당신은 AIDL 을 사용하여 구획화 파라미터들의 코드를 생성하게 될 것입니다.


그 AIDL IPC 메커니즘은 COM or Corba 와 유사한 인터페이스를 기반으로 하고 있습니다.
그러나 보다 가볍고 빠르게 만들어졌습니다.
이것은 한 프록시 클래스와 클라이언트 클라이언트 사이에 값을 넘길 수 있도록 구현하는데 사용되어 집니다.


이 페이지는 다음의 주요 단원으로 나눠졌습니다:

* AIDL를 사용하여 IPC를 구현합니다. 
* .aidl 포맷의 IPC Class 를 호출해 봅니다.

 


◎ AIDL 를 통해 IPC 구현하기


AIDL를 사용하여 IPC 서비스를 구현하는 순서는 다음과 같습니다.

1. 내 .aidl 파일을 작성하기 
   - 이 파일은 미리 정의된 메소드와 필드를 클라이언트에 사용할 수 있도록 
     인터페이스(내인터페이스.aidl)를 정의합니다.

2. 내가 만든 .aidl 파일을 추가하기
   - (당신에 의해 이클립스 플러그인이 관리하도록 합니다) 컴파일러로 안드로이드에 인클루드 하고
     AIDL를 호출하고, tool/ 안에 넣습니다.

3. 내 인터페이스 메소드를 구현하기
   - AIDL 컴파일러는 당신의 AIDL 인터페이스로부터 자바프로그래밍 언어로 구현된 것을 생성합니다.
     이 인터페이스는 한 인터페이스(그리고 IPC 호출를 위해 필수적인 약간의 메소드가 추가된)를 상속한
     하나의 추상화 클래스로 명명된 조각을 가집니다.

4. 내 인터페이스를 클라이언트에 통보하기 
   - 만약 당신이 서비스에 기재한다면, 한 당신이 구현된 인터페이스 클래스의 인스턴스를 
     Service 로 확장 오버라이드된 Service.onBind(Intent) 에게 반환할 것입니다.


● .aidl 파일 작성하기

AIDL 은 하나 이상의 메소드를 함께 사용하는 인터페이스를 당신이 선언한 수 있도록 간단한 문법을 쓰며,
패러미터나 반환값을 가질 수 있습니다. 그 패러미터들과 반환값은 다양한 타입이 될 수 있으며,
심지어는 AIDL로 생성된 인터페이스도 될 수 있습니다.
그러나 이 점을 중요하게 참고해야 합니다. 생성되지 않은 모든 타입,
심지어 만약 같은 패키지에 정의된 당신의 인터페이스를 포함해서 당신은 반드시 import 해야 합니다. 
여기에 AIDL 가 자체 지원할 수 있는 데이터 타입이 있습니다.


* 원시형태의 자바 프로그래밍 언어타입(int, boolean 기타) - import 명시가 필요하지 않습니다.)
* 다음에 해당되는 클래스(import 명시가 필요치 않습니다.)
  String

  List   - List 내부의 모든 엘리먼트는 반드시 하나의 이 아래에 있는 리스트 내부의 한 속성이어야 합니다.
           다른 AIDL로 생성된 인터페이스와 세부 가능화된 것들을 인클루드 합니다. 리스트는 제네릭 클래스 (예: List<String>)를
           사용하는 방법을 써도 됩니다.   다른 측면의 현실적이고 구체적인 클래스라면, 비록 메소드는 리스트 인터페이스를 사용하여 
           생성된 것일지라도, 항상 ArrayList 를 통해 받을 수 있도록 하는 것입니다,

  Map    - 맵 객체 안의 모든 엘리먼트들은 반드시 리스트 안에있는 하나 이상의 속성들 이어야 하며,
           다른 AIDL로 생성된 인터페이스와 세부 가능화된 것들을 인클루드 합니다.
           제너릭 맵 (예: Map(String, Integer)은 지원하지 않습니다. 다른 측면의 현실적이고 구체적인 클래스라면, 
           비록 메소드는 맵 인터페이스를 사용해 생성된 것일지라도 항상 해쉬맵을 통해 받는 것입니다.

  CharSeq- TextView 나 다른 위젯 오브젝트들로 CharSequence 속성들을 사용하기에 매우 유용합니다.
  uence

  다른 AIDL 생성된 인터페이스는 언제나 레퍼런스로 통과됩니다. import 를 명시하는 방법은 아래와 같습니다.
   사용자 정의를 구현 클래스는 세분화가능한 프로토콜을 하거나 값 별로 전달합니다.  import를 명시하는 방법은 아래와 같습니다.

  여기 기본 AIDL 문법이 있습니다 - [ 문서 내의 소스코드 참조]

 

● 인터페이스 구현

AIDL는 당신과 함께 비슷한 이름을 가진 당신의 .aidl file 를 위해 인터페이스 파일을 생성합니다
만약 당신이 이클립스 플러그인을 사용한다면 AIDL은 빌드 프로세스(프로젝트 빌드를 위해 미리 AIDL을 실행시킬 필요가 없습니다)
파트에 자동으로 실행되도록 합니다.
만약 이 플러그인을 쓰게되면 당신은 AIDL을 먼저 실행하게 될 것입니다.

생성된 인터페이스를 포함하여 한 추상화 내부 클래스 명명 조각에 모든 당신이 선언한 .aidl 파일 그 메소드를을 선언합니다.
조각은 또한 약간의 헬퍼 메소드, 그 중에 가장 많은 asInterface(), 어느 곳에 가져온 IBinder ( application.Context.bindService()가
성공했을 때의 클라이언트 onServiceConnected() 구현체 ), 그리고 IPC메소드를 호출하는데 사용된 인터페이스의 인스턴스의 리턴 값 
들로 선언되어 있습니다. Calling an IPC 섹션을 참고하면 좀더 캐스트하는 것에 대한 더 많은 사항을 알 수 있습니다.

당신의 인터페이스를 구현할 때, YourInterface.Stub 를 확장(상속), 그리고 메소드를 구현 (.aidl 파일과 stub 구현 메소드를
안드로이드 빌드프로세스 가 .Java 파일을 프로세스하기 전에 먼저 만들도록 할 수 있습니다. )

여기 간단한 IRemoteService 호출에 대한 인터페이스 구현 예제가 있으며, 단일 메소드로 분리되고
getPid 를 사용한 익명의 인스턴스의 경우입니다. (참고할 것):

// No need to import IRemoteService if it's in the same project.
private final IRemoteService.Stub mBinder = new IRemoteService.Stub(){
    public int getPid(){
        return Process.myPid();
    }
}

 

● 인터페이스 구현에는 몇 가지 규칙이 있습니다.

* 당신의 호출 전송에 대하여 미리 예외검사를 하지 않습니다.
* IPC는 동시에 호출 됩니다. 만약 당신이 IPC 서비스가 보다 많은 작은 밀리초 단위로 처리해나갈 필요가 있다고 느끼면 
 알고 있다면, 당신은 되도록 Acitivity/View 쓰레드를 호출하지 말아야 하는데, 그 이유는 CPU가 특정 어플리케이션(안드로이드는  "Application not Responding" 다이얼로그를 표시할 수도 있습니다)에 매달려 정체될 될 수도 있기 입니다.
 다른 쓰레드로 나눠서 처리하는 방법을 시도해 봅니다.
* 메소드들은 이렇게만 지원됩니다 " 당신은 static 필드 형태로 AIDL 인터페이스를 선언할 수 없습니다.

 


● 인터페이스를 클라이언트에 공개하기

 이제 당신은 인터페이스 구현법을 알게 되었고 클라이언트에 공개할 차례 입니다. 당신의 서비스에 발표 한단 뜻이기도 합니다.
 Service를 상속받고 Service.onBind(Intent) 를 구현하도록 받아서 구현된 인터페이스 클래스 의 인스턴스를 반환합니다.

 여기 IRemoteService 인터페이스를 클라이언트 공개하는 일부 코드가 있습니다. (문서 소스코드 참조)

 


● 패러미터 값을 Parcelables(꾸러미객체) 를 이용해서 인수값으로 넘겨주기
 
 주의 :
   Parcelables 는 현재 당신이 이클립스 플러그인을 사용할시에 작동하지 않습니다. 
   강제로 시도할 경우 다음과 같은 에러들을 보게 될 것입니다. :
    .aidl files that only declare parcelables don't need to go in the makefile
     aidl can only generate code for interface, not parcelables
  이것으로 한계가 있음을 알게 되었습니다. Parcelable은 역시 build.xml 파일들 이나 당신이 가진
  사용자정의 빌드 시스템만을 사용할 수 있습니다.
  작업공간은 당신은 위해 aidl tool 로 손안의 모든 인터페이스를 구현하고 추가할 수 있도록 
  당신의 이클립스 프로젝트 상에서 실행되어 집니다.
  왜 이클립스가 aidl 파일 컴파일 시도가 불가능한지를 5장을 통해 알아보세요.
 

  만약 당신이 만든 클래스가 한 프로세스 가 다른 AIDL 인터페이스를 경유하여 보내게 되는 일을 수행한다면, 그일도 가능합니다.
  당신은 당신의 클래스가 다른 측면의 IPC 에게도 유효하다면. 그 코드를 안전하게 보호해야 합니다.
  일반적으로 그것은 당신이 시작과 동시에 서비스와 의사소통을 할 수 있음을 의미합니다.

  여기에 5개 파티의 Parcelable 프로토콜을 지원하는 클래스 생성법이 있습니다.

  1. Parcelable 인터페이스로 당신의 클래스를 구현하세요.
  2. public void writeToParcel(Parcel out) : 오브젝트와 꾸러미에 쓰기의 현재 상태를 보존할 수 있도록  다음과 같이 구현하세요.
  3. public void readToParcel(Parcel in) : 오브젝트로부터 꾸러미 값을 읽을 수 있도록 다음과 같이 구현합니다.
  4. CREATOR를 호출할 static 필드를 추가합니다. 당신의 클래스에 이것은 Parcelable.Create 인터페이스로 구현할 수 있습니다.
  5. 마지막이지만 남은 것이 있습니다. aidl 파일을 AIDL 도구가 찾을 수 있도록 parcelable 클래스에 추가하되,
     하지만 당신의 빌드에 추가하지 마세요. 이것은 C 헤더파일을 쓰는 것과 비슷합니다.
     당신은 aidl을 컴파일할 수 없습니다. parcelable 로 하여금 마치 헤더파일을 기본적으로 컴파일할수 없는 것 처럼 말입니다.

  AIDL 는 이 메소드와 필드를 당신의 오브젝트들의 구획화와 비구획화 된 곳에 생성할 것입니다.

  여기에 Rect 클래스를 Parcelable 프로토콜을 통해 구현한 예제가 있습니다.  (문서 코드 참조)

 
  Rect.aidl 예제가 있습니다.
  package android.graphics;

  // Declare Rect so AIDL can find it and knows that it implements
  // the parcelable protocol.
  parcelable Rect;

 
  구획화하는 예쁘고 간단한 Rect 클래스 입니다. 다른 메소드에서 당신이 꾸러미에 쓴 값이 
  꾸러미 위에서 어떤 식으로 보여지는지를 살펴보세요.


  주의 : 다른 프로세스로 부터 데이터를 받는 것에 보안이 되어있음을 잊지마세요.
         예로, Rect는 꾸러미로 부터 4개의 값을 읽지만 이것은 동의 하에 안전하게 올려졌습니다.
         하지만 이것은 위로 당신에게 안전하게 동의가능한 범위내의 무엇이든지 호출해서 시도하도록 만들어져 있습니다.
         구획화로부터 안전하게 보호하는 방법을 알기 위해 안드로이드에서 보안, 권한에 관한 문서를 자주 참고하세요.


● IPC 메쏘드를 호출하기

 여기엔 당신의 원격 인터페이스를 호출할 수 있도록 클래스를 호출하는 단계 입니다.

 1. 당신이 정의해둔 .aidl 파일을 인터페이스 속성 변수로 선언합니다.

 2. ServiceConnection 을 구현합니다.

 3. ApplicationContext.bindService() 를 호출합니다. 서비스연결 완성된 것의 인수를 넘깁니다.

 4. 당신의 완성된 ServiceConnection.onServiceConnected() 것으로 IBinder 인스턴스 값을 받을 수 있습니다.
    YourInterfaceName.Stub.asInterface((IBinder)Service) 를 호출해서 Yourinterface 속성으로 변환후 넘깁니다.

 5. 메소드를 당신이 정의한 인터페이스로 호출합니다. 당신은 언제나 예외 점검에 잡힐 수 있습니다. DeadObjectException,
    어느것은 연결이 끊어질 수도 있습니다 : 이것은 오직 원격메소드를 통해 예외처리 검사를 해야 합니다.

 6. 연결을 해제합니다. ApplicationContext.unbindService() 를 인터페이스의 인스턴스와 함께 호출합니다.

 
 약간의 IPC 서비스 호출에 대해 덧붙이자면.

  * 오브젝트는 맞은편의 프로세스의 갯수를 참조합니다.
  * 당신은 익명의 오브젝트에 메소드 arguments를 보낼 수 있습니다.


  여기에 AIDL 로 만들어진 서비스를 호출하는 약간의 코드 예제가 있습니다. ApiDemos project로부터 원격 Activity 샘플을 참조하세요


[Android] 안드로이드 인디케이터 설정 - NotificationManager Android / Goal

2010/08/02 13:34

[출처] 
http://blog.naver.com/oh4zzang/40111693903

안드로이드에서 NotificationManager를 통해 인디케이터 바를 설정하거나 출력(notify)할 수 있다.

흔히들 인디케이터 바를 status 바라도 말하기도 한다.

우선 간단하게 getSystemService() 메소드를 통해 NotificationManager을 받아올수 있다.  

이후 flags 값을 셋팅하고, setLatestEventInfo를 통해 noti 선택 시 실행될 Activity를 설정해 주면된다. 

 private void showNotify() {

    NotificationManager nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
 int _ID = 0xffff0001;
       
Notification notification = null;      
   notification = new Notification(R.drawable.icon, "start notify", System.currentTimeMillis());  
notification.flags = Notification.FLAG_ONGOING_EVENT;
  
   PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, ServiceSample.class), 0);
   notification.setLatestEventInfo(this, "Test Notify", "hello notification", contentIntent);
   nm.notify(_ID, notification);
 }

 

위 메소드 showNotify() 를 호출 하게 되면 아래와 같이 인디케이터 영역이 초기화되면서 등록한 아이콘과 메세지가 출력된후

 

사용자가 지우기 이전까지 인디케이터 영역에 존재하게 된다.

       

위와 같이 호출 시 다음과 같이 인디케이터 영역에 아이콘을 출력하고 notify 할 수 있다. 

왼쪽 이미지 인디케이터 영역에 안드로이드 아이콘이 출력된 것을 확인할 수 있다.

이 이미지는 Notification 객체 생성 시 지정해 줄수 있다. 여기서는 R.drawable.icon 과 같이 기본 아이콘을 사용 하였다.

new Notification(R.drawable.icon, "start notify", System.currentTimeMillis());   

 

오른쪽 이미지는 인디케이터 바를 아래로 드레그한 화면이다. 알림에 해당 noti가 등록된 것을 볼 수 있다.

여기서는 아래와 같이 등록을 하고 클릭 시 연결(link)될 인텐트, class 를 PandingIntent 로 생성 후 setLatestEventInfo() 메소드를 사용하여 연결했다.

PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, ServiceSample.class), 0);
notification.setLatestEventInfo(this, "Test Notify", "hello notification", contentIntent);

 

알림 영역이 아닌 진행중 영역에 notify 를 등록 하고 싶다면 flag  를 아래와 같이 설정해 주면 된다.

   notification.flags = Notification.FLAG_ONGOING_EVENT;        

 

이전 이미지와 다르게 알림영역이 아닌 진행중 영역으로 등록 되어 있는 것을 볼 수 있다.

출처 : http://sje0114121.blog.me/150089240741

background에서 작업하기

<Service >

     예) 미디어 플레이어 : 꼭 Service로 만들어야 하는건 아니다. Activity로도 만들수있음.

          장시간으로 하면서 user Interface가 거의 없는 것을 Service로 만드는 게 좋다.

 

* intent filter 존재 : intent로 시작됨.

Intent i = new Intent(명시적 or 암시적)

startService(i);  // 한 life cycle 돌게됨.

  - 객체는 singleton이라서 다른곳에서 startService()를 다시 호출해도 onCreate()는 되지않고 onStart()만 다시불린다.

 

stopService(i);

 

bindService(i);  // 내 액티비티에서 저 서비스 연결해서 뭔가 뽑아내고자 할때 

unbindService(i);

 

* Life Cycle

          onCreate()

      |                       |

onStart()               onBind()

                 ...

             thread

      (장시간 이용할시)

                  ...

 onStop()               unBind()

      |                       |

          onDestroy()

 

 

onCreate() -> onStart() -> onStop()  -> onDestroy();

Binding할때 : onCreate() ->onBind() -> unBind() -> onDestroy();

 

onBind()

{

return new My();

}

 

class My extends Binder {

service getService();

return 0;

}

 

ServiceConnection타입의 객체 (interface) 를 inner class로 만듬.

 -  onServiceConnected(Binder b);, onServiceDisConnected() 두개 overriding해야됨.

    Connection완료된 다음에 Binding정보 넘겨주는 게 맞음.

    b.getService() 이런식으로..

 

BindService할때 매개변수로 ServiceConnection을 매개변수로 넘겨줌.

 

쓰레드를 얼마나 깔끔하게 짜느냐도 관건..

 

* Background Thread  (<-> Main Thread(UI Thread))

오래걸리는 작업이 있을 시 Background thread로 돌린다.

왜냐하면 5초이상 반응해주지 못하면 에러이기때문.

Binder-post()

 

[실습]

프로젝트 새로 생성 후,

res폴더밑에 폴더 raw하나 만들어서 kara.mp3, start.png, stop.png파일을 넣는다.

 

[AndroidManifest.xml]

Service 하나 추가한다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="chap8.Service"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".Main"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    <service android:name="PlayService"></service>
</application>
    <uses-sdk android:minSdkVersion="7" />

</manifest>

 

[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">
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="horizontal" android:layout_width="fill_parent"
  android:layout_height="wrap_content">
  <ImageButton android:id="@+id/ImageButton01"
   android:layout_width="wrap_content" android:layout_height="wrap_content"
   android:src="@raw/start"></ImageButton>
  <ImageButton android:id="@+id/ImageButton02"
   android:layout_width="wrap_content" android:layout_height="wrap_content"
   android:src="@raw/stop"></ImageButton>
 </LinearLayout>

 <ProgressBar
  android:id="@+id/ProgressBar01"
  android:layout_width="fill_parent" android:layout_height="wrap_content"
  style="?android:attr/progressBarStyleHorizontal"></ProgressBar>
</LinearLayout>

 

 

[PlayService.java]

package chap8.Service;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;

public class PlayService extends Service implements Runnable {

 MediaPlayer player;
 
 boolean flag = true;
 private int duration;
 private int currentPosition;
 
 // getter, setter함수

 public boolean isFlag() {
  return flag;
 }

 public void setFlag(boolean flag) {
  this.flag = flag;
 }

 public int getDuration() {
  return duration;
 }

 public void setDuration(int duration) {
  this.duration = duration;
 }

 public int getCurrentPosition() {
  return currentPosition;
 }

 public void setCurrentPosition(int currentPosition) {
  this.currentPosition = currentPosition;
 }
 
 // thread 에 의해 실행되는 코드...
 // Bind되는 순간부터 run이 실행되면서 매 초마다 현재 Position를 설정한다.
 public void run() {
  // run을 빠져나가면 thread는 끝난다.
  // 장시간 해야되는건 주로 loop를 만들어 놓는다.
  while(flag) // 음원이 끝나면 나가라.
  {
   setCurrentPosition(player.getCurrentPosition());
   
   try {
    // sleep은 항상 try~cath와 같이 쓰여야 한다.
    Thread.sleep(1000); // milisecond unit
   } catch (Exception e) {
    // TODO: handle exception
   }   
  }  
 }
 
 @Override
 public IBinder onBind(Intent arg) {
  // TODO Auto-generated method stub
  player = MediaPlayer.create(this, R.raw.kara);
  setDuration(player.getDuration());
  player.start();
  
  // MediaPlayer의 음원 플레이 시간을 얻어내서 service의 데이터를
  // 업데이트 시키는 작업을 하는 thread
  //(null: 특별히 group값이 필요없음.
  // this: thread 돌릴 source, player는 이름
  Thread t = new Thread(null, this, "player");
  t.start(); // bind되는 순간 PlayService의 run()이 시작됨.
  
  return new MyBinder(); // bind될 객체 리턴해야됨.
 }

 @Override
 public void onDestroy() {
  // TODO Auto-generated method stub
  super.onDestroy();
  player.stop(); // service 멈추기
  flag = false; // thread가 멈출 수 있게 flag값 설정
 }

 @Override
 public void onStart(Intent intent, int startId) {
  // TODO Auto-generated method stub
  super.onStart(intent, startId);
  
//  player = MediaPlayer.create(this, R.raw.kara);
//  player.start();
  // SD카드의 여러 음악프로그램중 어떤걸 play할 것인지?
  // 음악이 끝났다는 신호를 받을 수 있다. 그러면 다음곡 실행시키기.
 }
 
 // Activity bind시 Activity에 전달시킬 객체
 public class MyBinder extends Binder {
  PlayService getService() {
   return PlayService.this;
  }
 }

}

 

[Main.java]

package chap8.Service;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.ProgressBar;

public class Main extends Activity implements OnClickListener, Runnable{
    /** Called when the activity is first created. */
 private ImageButton bt1;
 private ImageButton bt2;
 private ProgressBar bar;
 
 // service랑 bind해서 service 객체를 얻어 service 의 데이터에 접근하기 위해
 private PlayService pService;
 
 // UI Thread에 UI 변경작업을 지시하기 위해 선언
 private Handler handler = new Handler();
 
 // user event와 상관없이 장시간 동안 service에 접근해서 데이터 획득하는 역할
 private Thread background;
 
    @Override
    // 1. Main이 실행되면서 onCreate()함수가 가장 먼저 실행된다.
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        bt1 = (ImageButton)findViewById(R.id.ImageButton01);
        bt2 = (ImageButton)findViewById(R.id.ImageButton02);
       
        bt1.setOnClickListener(this);
        bt2.setOnClickListener(this);
       
        bar = (ProgressBar)findViewById(R.id.ProgressBar01);
        //bar.setOnClickListener(this);
    }

   
 public void onClick(View v) {
  // TODO Auto-generated method stub
  if(v == bt1)
  {
   Intent intent = new Intent(this, PlayService.class); // 명시적
   //startService(intent);
   // 2. 재생버튼을 누르면 OnClick이 실행되고,
   // 여기서 service를 bind시킨다.
   // Service에서는 onCreate(), onBind()까지 실행되게된다.
   bindService(intent, connection, Context.BIND_AUTO_CREATE);
   background = new Thread(null, this, "test");
   
  } else if(v == bt2)  {
   Intent intent = new Intent(this, PlayService.class);
   //stopService(intent);
   // stop버튼을 누르면 binding이 끊긴다.
   unbindService(connection);   
  } else if(v == bar) {
   //handler.post(updateBar);
   // UserEvent받아들일 수 있는 거는 SeekBar로만 할 수 있다.
   // ProgressBar는 시스템에 의해서만 동작된다.
  }
   
  
 }
 
 // bind 상태에 따라 자동 호출되는 메서드를 가지는 객체 선언
 private ServiceConnection connection =
  new ServiceConnection() {
   
   public void onServiceDisconnected(ComponentName name) {
    // TODO Auto-generated method stub
    pService = null;
   }
   
   public void onServiceConnected(ComponentName name, IBinder service) {
    // TODO Auto-generated method stub
    // IBinder타입을 PlayService.MyBinder타입으로 type-casting하고
    // getService로 서비스 가져온다.
    //3. Binding이 완료되면 Service 정보를 가져오고
    pService = ((PlayService.MyBinder)service).getService();
    //4. background로 thread를 실행시킨다.
    background.start(); // 이때부터 run()이 실행된다.
   }
 };
  
 // UI Thread 에게 넘겨줄 작업
 // Inner class - Runnable이 inteface이므로..
 private Runnable updateBar = new Runnable() {
  
  public void run() {
   // UI thread는 간단하게 짠다. loop문같은거 넣지않는다.
   // 6. bar가 새롭게 그려진다.
   bar.setProgress(pService.getCurrentPosition());
  }
 };
 
 // background thread가 작업할 내용을 가지는 메서드
 // 5. background thread는 돌면서 1초마다
 // UI updateBar thread한테 progress를 표시하라고 post한다.
 public void run() {
  // 전체 음원의 시간값으로 bar의 Max를 설정한다.
  bar.setMax(pService.getDuration());
  while(pService.isFlag())
  {
   try {
    Thread.sleep(1000);
    // 오늘의 key point!!
    // 1초마다 main thread한테 update progress bar하라고 날린다.

    handler.post(updateBar);
   } catch (Exception e) {
    // TODO: handle exception
   }
  }
  
 }
}



Class Overview

MediaPlayer class can be used to control playback of audio/video files and streams. An example on how to use the methods in this class can be found in VideoView. Please see Audio and Video for additional help using MediaPlayer.

Topics covered here are:

  1. State Diagram
  2. Valid and Invalid States
  3. Permissions

State Diagram

Playback control of audio/video files and streams is managed as a state machine. The following diagram shows the life cycle and the states of a MediaPlayer object driven by the supported playback control operations. The ovals represent the states a MediaPlayer object may reside in. The arcs represent the playback control operations that drive the object state transition. There are two types of arcs. The arcs with a single arrow head represent synchronous method calls, while those with a double arrow head represent asynchronous method calls.

MediaPlayer State diagram

From this state diagram, one can see that a MediaPlayer object has the following states

개발 Q&A에서 가끔 보던 질문이고 저 또한 질문 했던 내용 입니다.

이미지 버튼을 사용시 버튼을 클릭했을때와 땟을때의 처리를 해결 못해서 기본 안드로이드 버튼만
사용중이었습니다.

해결을 하려고 setBackgroundResource도 생각했었습니다만...

setBackgroundResource를 사용했을경우에는 버튼을 클릭했을경우엔 이미지가 바뀌지만
땟을경우에 그이미지가 그대로 남아있어서 스레드를 돌릴까도 생각도 했었는데요...

몇주전까지만해도 구글링을 해서도 찾지 못했던 내용 이었는데 오늘 그 해답을 찾았습니다 ㅠ.ㅠ

다른 api내용 찾다가 우연히 발견하게 되었습니다.

res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:gravity="center" 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="@string/hello" />

    <ImageButton android:id="@+id/imgBtn"
    android:background="@+drawable/playbutton_click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    </ImageButton>
</LinearLayout>


res/drawable/playbutton_click.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="false" android:state_pressed="true"
        android:drawable="@drawable/btn1" />
    <item android:drawable="@drawable/btn" />
</selector>

drawable의폴더에는
버튼이 눌러졌을때의이미지와 버튼을 땟을때의 이미지파일이 존재하여야 합니다.




이렇게 간단하게 해결될 문제였답니다 ㅠ.ㅠ

많이 허접한 내용이지만 아직도 해결 못하신분들을위해 적어 봤습니다.

Service - 백그라운드 음악 실행 토요특강 / 안드로이드

2010/11/20 11:31

복사 http://blog.naver.com/jeggange/90100530068

Service

 

사용자와 상호작용 없이 내부적으로 실행되는 프로세스

- UI없이 오랫동안 살아있으면서 실행되는 코드들

- media player activity는 Context.startService()를 이용하여 백그라운드로 음악이 계속 재생되는 서비스 구동

- 서비스에 접근할 때, 서비스에 의해 나타난 인터페이스를 통해 서비스와 통신할 수 있다.

 

 

 

 

 

 

 

 

이미지 버튼과 음악 재생에 사용할 파일을 미리 준비해 둔다

미디어 파일은 /res/raw 폴더 생성

 

 

xml 버튼에 onClick 이벤트 달기

 


 android:id="@+id/btnStop" 
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="10dp"
    android:onClick="btnProcess" />

 

 

 

 

 

main.xml

 


http://schemas.android.com/apk/res/android"

    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >

    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="음악  즐기기" />

 android:id="@+id/btnStart" 
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" 
    android:src="@drawable/pic"
    android:layout_marginTop="20dp"
    android:onClick="btnProcess" />

 android:id="@+id/btnStop" 
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="서비스 중지"
    android:layout_marginTop="10dp"
    android:onClick="btnProcess" />

 android:id="@+id/btnExit" 
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="어플 종료"
    android:layout_marginTop="10dp"
    android:onClick="btnProcess" />                   

 

 

 

 

 

Manifest.xml

 

       
       


        
 

MainApp.java

 

package aa.serviceEx;

import android.app.*;
import android.content.*;
import android.os.*;
import android.view.*;
import android.widget.*;

public class MainApp extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
    public void btnProcess(View v) {
      Toast.makeText(this, "success", 2000).show();
      }
}

 

 

 

 

 

 

버튼 테스트를 위해 토스트를 달았다.

세가지 버튼을 누르게 되면 모두 같은 프로세스를 호출하기 때문에

같은 success 메시지가 나온다.

 

 

 

 

음악 재생 코드 추가

MainApp.java

 

package aa.serviceEx;

import android.app.*;
import android.content.*;
import android.os.*;
import android.view.*;
import android.view.View.*;
import android.widget.*;

public class MainApp extends Activity implements OnClickListener {
    /** Called when the activity is first created. */
 ImageButton btnStart;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        btnStart = (ImageButton)findViewById(R.id.btnStart);
        Button btnStop = (Button)findViewById(R.id.btnStop);
        Button btnExit = (Button)findViewById(R.id.btnExit);
        btnStart.setOnClickListener(this);
        btnStop.setOnClickListener(this);
        btnExit.setOnClickListener(this);
    }
   
    @Override
    public void onClick(View v) {
     if(v.getId() == R.id.btnStart) {
      startService(new Intent("aa.bb.cc.mbc"));
      btnStart.setEnabled(false);
      
     }
     else if(v.getId() == R.id.btnStop) {
      stopService(new Intent("aa.bb.cc.mbc"));
      btnStart.setEnabled(true);
      
     }
     else if(v.getId() == R.id.btnExit) {
      stopService(new Intent("aa.bb.cc.mbc"));
      btnStart.setEnabled(true);
      finish();
      
     }
    }
}

 



음악이 재생중인 상태이며,

백그라운드로 돌렸기 때문에 멀티태스킹이 가능하다.

 

 

 

 

알림 표시바 사용하기

- 시간이 나온 제일 윗 부분 드래그 해서 나오는 부분 Notification

 

MainApp.java에서 onclick 메소드 수정

 

    @Override
    public void onClick(View v) {
     // 알림 표시바 사용
     NotificationManager nm =
      (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
     
     if(v.getId() == R.id.btnStart) {
      startService(new Intent("aa.bb.cc.mbc"));
      btnStart.setEnabled(false);
      
      // Notification 처리
      Notification notification =
       new Notification(R.drawable.noti, "최신음악", System.currentTimeMillis());
      Intent intent = new Intent(this, MainApp.class);
      PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, 0);
      notification.setLatestEventInfo(this, "노래 서비스", "점심은 맛있게", pendingIntent);
      nm.notify(0, notification); // 상태표시바에 등장
     }else if(v.getId() == R.id.btnStop) {
      stopService(new Intent("aa.bb.cc.mbc"));
      btnStart.setEnabled(true);
      nm.cancel(0); // 상태 표시바에서 해제      
     }else if(v.getId() == R.id.btnExit) {
      stopService(new Intent("aa.bb.cc.mbc"));
      btnStart.setEnabled(true);
      nm.cancel(0); 
      finish();      
     }

 



 

 

 

 

MpPlayer.java

 

package aa.serviceEx;

import android.app.*;
import android.content.*;
import android.media.*;
import android.os.*;

public class MpPlayer extends Service {
 MediaPlayer mp;
 
 @Override
 public IBinder onBind(Intent intent) {
  return null;
 }
 
 @Override
 public void onStart(Intent intent, int startId) {
  super.onStart(intent, startId);
  mp = MediaPlayer.create(this, R.raw.man);
  mp.start();
 }
 
 @Override
 public void onDestroy() {
  super.onDestroy();
  mp.stop();  
  mp.release(); // 자원 반납
 }

} 


2010/08/04 16:11

출처 :  http://blog.naver.com/romistudio/50093624442

 http://blog.naver.com/romistudio/50093624442

질답 게시판에도 올려봤지만... 저만 이런 경우를 만난 것인지..아직도 알 수가 없네요.
에러 메시지는 Prepare failed status = 0x1 입니다.

인터넷을 검색해 봐도...뾰족한 답변이 없었습니다.
그러다...갑자기 하나 보인... 검색 결과물이 있었는데요..

http://www.anddev.org/viewtopic.php?p=16478

에서 ... 이런 글을 보게 되었습니다.

you store your tempfile in the application cache directory and the problem is that the MediaPlayer doesn't have access rights to application directories.

Instead you should passing a FileDescriptor to your MediaPlayer with setDataSource(FileDescriptor fd).

Something like this:
...
FileInputStream fis = new FileInputStream(bufferedFile);
FileDescriptor fd = fis.getFD();
mediaPlayer.setDataSource(fd);


결국... 저의 경우가 tempfile 이고. 어플의 캐쉬 폴더라는 것인지... 확신은 안가지만.. 위의 코드로 문제는 해결되었습니다.
http url 로 다운 받아.. /data/data/.../files 가 어플의 캐쉬 폴더?? 그리고. 내가 받은 파일이 임시 파일이라고?
논리는 안맞지만..여튼 위의 코드로 작동이 되었습니다.

먼저 게시판에 흔하게 보이는 코드이며..저 역시 사용한 코드는 다음과 같으며.. 그뒤 두번째가...위의 충고(?)를 바탕으로
작성된 코드입니다. 참고로... 흔히 보이는 코드 (아래 첫번째꺼) 로는 http:// .... 로 직접 접근해서 플레이하는건 잘됩니다.


01.try {
02. MediaPlayer mediaPlayer = new MediaPlayer();
03. mediaPlayer.setDataSource(pathinlocal+FileName);
04. mediaPlayer.prepare();   ---- 여기에서 런타임 에러 발생 Prepare failed : status = 0x1
05. mediaPlayer.start();
06.} catch (IOException e) {
07. // handle exception
08. //((EditText) ((Activity) mMain).findViewById(R.id.txtStatus)).setText(e.getMessage());
09.}



아래는 위의 충고를 바탕으로 작성되어 문제가 해결된 코드입니다.
01.try {
02. MediaPlayer mediaPlayer = new MediaPlayer();
03. FileInputStream fis = new FileInputStream(pathinlocal+FileName);
04. FileDescriptor fd = fis.getFD();
05. mediaPlayer.setDataSource(fd);
06. mediaPlayer.prepare();
07. mediaPlayer.start();
08.} catch (IOException e) {
09. // handle exception
10. //((EditText) ((Activity) mMain).findViewById(R.id.txtStatus)).setText(e.getMessage());

안드로이드_개발_팁

<merge> 태그 설명
(android:sharedUserId) 하나의 프로세스에서 여러 애플리케이션 실행하기
(showDialog 사용중) is not valid is your activity running
(링크) 안드로이드 샘플로 들어 있는 NodePad (DB 사용예등..)
(링크) 안드로이드 스터디 Jni 발표 자료...
[번역] 안드로이드 2.0 Service API 변화
AccountManager 관련 내용
Activity Launch Mode
Activity가 화면에 보여질때 발생하는 이벤트
Activity내 배경을 투명하게 하기
Activity를 FullScreen으로 띄우기
Activity에 Dialog 적용하기
adb install이 잘 안될때..
adb 명령어 옵션들
adb 명령어로 응용프로그램 설치및 삭제
addContentView 사용예제
AlarmManager을 이용해서 특정시간뒤 서비스 실행하기
Android 1.5 소스 다운로드
Android 2.1 update and source code release
Android API Demo 사용해보기
Android Application Framework FAQ
Android comparing phone number to contacts list
Android cpu usage / running linux command
Android getting SMS message cursor
Android MD5 encryption
Android Orientation System 이미지
Android SDK 1.5 설치
Android SDK 화면 사이즈 변경하기
Android Search person’s name by contact id
Android send email gmail activity
Android sms received and sent event
android 데이터 베이스 변경시 ContentObserver활용하여 체크하기
Android 문서
android 바탕화면 소스 위치( )
Android 에서 Ant로 빌드시 참고할만한 내용
android 용 package와 jar 어떻게 만드나요.. !
android-log-collector (Locat의 로그를 이메일로 전송해주는 어플)
android.provider.ContactsContract 사용예
AndroidHttpClient 사용예제
Android에서 AutoCompleteTextView와 SQLite 연동하는 방법
andtweet / 안드로이드 트위터 오픈소스
AppWidget 예제
ArrayAdapter의 내용 변경 통지하는 notifyDataSetChanged() 사용예제
AssetManager (getAssets)을 이용해서 파일 읽어오기
AsyncTask에서 cancel 처리 예제
Background에 Shader 사용하기
Button 클릭하기
C/C++ 개발을 위한 Eclipse CDT 설치
Calendar 목록 읽어오기
Check phone is using roaming network
ClipboardManager 사용예제
Contact Provider Db에 접근하기
ContentProvider 사용예제
Cursor 내용을 AutoCompleteTextView 자동완성 하기
Dialog 사용예제
DOM과 SAX 파서의 차이점
Draw 9-patch
EditText / TextView 숫자만 허용
EditText 글자 출력 위치
EditText 글자수 제한하는 메소드
EditText에 대한 IME 옵션 설정및 옵션 클릭시 이벤트 처리
EditText에서 커서 위치를 지정하기
EditText와 TextView에 대해서 경계선 없애기
Email 데이터베이스에 접근
GestureDetector 사용예
getContentResolver()에 limit 추가하기
getDir()을 사용해서 디렉터리 생성하기
getSharedPreferences() 로 저장시 파일위치
glScissor, glEnable, glDepthMask 사용예제
GLSurfaceView 사용예제
Google Android developers resources / API tutorials and guides
GPS 상태 변화 감지 소스
GPS 시뮬레이터 사용방법
GPS 이용한 위치정보 얻어오기
GPS 이용한 위치정보 얻어오기
GUI 컴포넌트의 색깔과 동작변경하기 ( TextView의 attribute, java method 사용 )
Handler(핸들러) 사용예제
HttpClient 예제
HttpClient 예제
ImageButton의 테두리 영역 투명하게 하기
IME 관련
inbox내 SMS 문자 삭제하기
Intent 링크
Intent 사용예제
Intent 사용예제2
JNI 관련 참고할만한 사이트
JNI 작업시, Log 클래스 이용해서 디버깅 내용 출력하기
Keyguard 끄기(화면을 켰을 때 나오는 잠금장치를 없애기)
Language(랭귀지) 코드
Language(랭귀지) 코드
Layout with ListView and Buttons
ListView 우측의 Fast Scroll Thumb
ListView내 한Row로 TableLayout을 사용한 예제
ListView내 항목 클릭시 선택색상(주황색) 없애기
ListView에 Header 붙이기
ListView에 invalidate가 안먹는다면...
ListView에 Selector에 커스텀 색상부여하기
ListView에 교차로 행마다 색상 부여하기
Log, Trace 정보를 원격으로 관리해주는 (nullwire)
Logcat에 색상 부여하기
LogCat의 로그를 파일로 저장하기
Manifest 에서 VersionName 가져오기
MediaStore 사용하여 이미지의 폴더명 알아내기
Menu 생성방법
Menu에서 xml 로 추가한 메뉴에 대한 MenuItem 접근하기
mp3 음악 목록 출력
MP3 플레이어 (오픈소스 프로젝트)
Multicolumn ListView (멀티칼럼)
NDK (JNI) 설치 관련 정보
Notification Manager 사용예제
onInterceptTouchEvent 와 onTouchEvent 차이
onSaveInstanceState / onRestoreInstanceState
openFileOutput()으로 저장한 파일의 절대 패스 구하기
openGL ES 관련 내용 링크
OpenGL ES 관련 자료 링크
OpenGL ES 예제
OpenGL ES 함수정리
OpenGL 레퍼런스 사이트
OpenGL 삼각형 회원 예제
Orientation Sensor Tips in Android
PackageManager / getPackageArchiveInfo 로컬파일(apk)의 패키지 정보 알아내기
Parcelable Interface를 이용한 Activity 간에 데이터 전송
Post로 웹페이지 읽어서 WebView에 출력하기
Process 별 사용 메모리 정보 구하기
ProgressDialog 와 AsyncTask 사용예제
R.java 파일 고치기 ( Resource 파일 고치기 )
raw 폴더내 파일에 접근하기
Relative Layout 사용예제
res/raw 폴더내 리소스를 파일로 저장하기
SAXParser (XML 파서) 사용예제
Screen Orientation Change 될때, 임시적으로 데이터 보관하기
ScrollView에 ListView 넣기
ScrollView에 ListView 넣기
ScrollView에 ListView 넣을수 없다.
SDCard 경로
sdcard 사용 가능 여부
sdcard 사용하기
sdcard 상태 확인하기
sdcard내 특정 폴더가 쓰기 가능한지 체크하기
sdcard내 파일 저장
Service 란
Service내 비동기 호출 가능한 oneway 메소드 선언
SharedPreferences 데이터 추가/삭제 사용예제
SharedPreferences 변화 감지하기
SharedPreferences은 멀티 프로세서에 안전하지 않다
SimpleDateFormat 사용시 주의점
SimpleOnGestureListener에 ACTION_UP 이벤트 감지하기
SMS 메시지 보내기
SMS 메시지가 들어왔을때 처리 (IntentReceiver)
Socket 타임아웃 지정하기
sqlite3 사용예
style 관련 참조할만한 내용
SwitchView 사용해서 실행중 화면 전환하기
TextView 사용관련 여러가지 팁
TextView 한줄 높이 변경하기
TextView(EditText) 사용시 IME에 "Done" "Next" 표기
TextView/EditText에 숫자만 입력받게 하기
TextView내 글자가 흘러가는 효과(marquee) 주기
TextView내 글자에 하이퍼링크 속성 부여하기
TextView를 한줄로 표시 / 한줄로 표시 안될때 ... 로 표기하기
TextView에 Ticker (marquee) 효과주기
TextView에 커스텀하게 link 상태 만들기 (정규표현식 이용)
TextView의 테두리에 색상주기
TextView의 테두리에 색상주기2 (selector, shape 사용)
TextView의 테두리에 색상주기3
TimePickerDialog / DatePickerDialog 사용예제
TitleBar에 Progress Indicator 표기하기
Toast 사용예제
Toast 출력시간 연장하기
Toolchain 이란
Touch Simulate
Traceview (Graphical Log Viewer)
UI 다자이너 (사이트)
UI 쓰레드에서 Handler 사용
Uncaught Exception 핸들러
UriMatcher
vcard 관련 오픈소스
VideoView 사용예제
View mode를 Portrait 나 Landscape로 고정하고 싶을때
View.getTag/setTag and IllegalArgumentException 가 발생하면...
ViewBinder 사용예제
ViewFlipper "Receiver not registered" Error
ViewSwitcher 사용예
View에 뭔가 데이터를 집어넣을때 setTag / getTag
Webview , WebViewClient 사용예
WebView auto fit 하기
WebView 로딩시 타이틀바에 진행중 아이콘 보이게 하기
webview 스크롤
WebView 에서 자바 스크립트 사용예
WebView 웹페이지 로딩시간 구하기
WebView 클래스 기능
WebView 페이지가 로딩되었을때 처리
WebView, SimpleGestureListener 사용예
WebView를 사용한 앨범 구현
WebView에 멀티터치 관련
WebView에 새창(href)이 뜰때 웹브라우져가 아닌 현재 WebView로 출력하기
WebView에서 alert 기능 추가
WebView에서 Html 태그 추출하기
WebView에서 loadData 사용시 주의점
WebView에서 멀티 터치 가능하게 하기
WebView에서 멀티 터치 줌 가능하게 하기
WebView에서 문자열 선택하기
webview에서 유튜브 동작시키기
WebView에서 쿠키 처리 참고할 내용
WebView의 loadUrl, loadData, loadDataWithBaseURL 사용예제
Wifi 연결 상태 감지하기
wifi(와이파이)로 접속한 ip 주소 알아내기
Windows XP에서 안드로이드 SDK 설치하기
XML pull parser을 이용한 RSS Feed 분석하기
XmlPullParser를 이용해서 rss내 title 항목 읽기
YouTube Data API을 사용한 검색
간단한 TouchEvent 사용예제
간단한 소스코드 정리해놓은 사이트
개발시 성능에 대해서 고려해야 할사항
개발한 안드로이드 소프트웨어를 배포하려면
계정(Account) 목록 읽어오기
관련소스1
구글 안드로이드 마켓 입점 따라 하기 !!
구글맵 api키 받기
내장된 어플로 SMS 메시지 보내기
네트워크(network) 연결 상태 감지하기 (이벤트)
네트웍 연결, 와이파이, 3G 설정하기
넥서스 원(안드로이드) 소스 다운로드
다른 Activity 호출하기
단말기 폰번호 알아내기
동적으로 Locale 설정 변경하기
동적으로 외부의 jar 파일 로딩하기
라이브러리 프로젝트 생성하기
랭귀지 설정
레이아웃 xml 편집시 빈줄로 시작하면 안된다.
리소스 사용관련 참고 내용
리소스 참조할 새로운 라이브러리 추가하기
리소스내 String 배열 읽어오기
리소스명으로 해당 리소스 ID 구하기
마켓에서 패키지명에 해당하는 어플 찾도록 StartActivity 시키기
멀티터치 (Multi Touch) 구현 소스
멀티터치(Multi Touch) 상태 여부
모델 정보 알아내기
문자 입력기 IME 보이기 / 감추기
바코드 스캐너 오픈소스 정보
바탕 화면의 아이콘 모양 변경하기
바탕화면 배경(wallpaper) 관리
밧데리 상태 확인하기
배터리 잔량 조회
버튼 클릭시 설치된 이메일(email) 프로그램 열기
버튼 클릭하기(프로그래밍 상에서..)
버튼에 Selector 사용해서 Focus, Select, Default 등에 따라 배경설정하는 예제
볼륨 높이기 (setVolume)
부팅시 서비스 실행하기
서비스 라이프사이클 관련...
서비스 사용예제
서비스에서 Timer로 작업 처리하기
설치된 프로그램 삭제 (adb shell 사용)
소프트키에 대한 설정
스크롤(Scrollable)되는 TextView
스크린(screen) On / OFF 인덴트 수신하기
시간경과후 다이얼로그 자동 종료
시스템에서 날리는 주요 브로드캐스트 인텐트
실제 핸드폰에서 실행시키기
실행중인 프로세스 목록 알아내기 (ActivityManager)
쓰레드에서 UI 업데이트 하기
아날로그 시계/ 디지털 시계
아이폰용 OpenGL ES 강좌
안드로이드 2.1 샘플 목록
안드로이드 AIDL 문법(Android AIDL Syntax)
안드로이드 e-book /andbook (영문)
안드로이드 OCR (wordsnap-ocr)
안드로이드 opengl 소스 링크
안드로이드 SDK 1.5를 설치해보자!
안드로이드 SDK 다운로드및 설치
안드로이드 SDK와 안드로이드폰 연동하여 프로그램 설치 스샷
안드로이드 SMS 메시지
안드로이드 Transparency, Dim 처리 방법 (투명, 희미한)
안드로이드 개발환경 구축
안드로이드 게임 [Android_Virus_Mania]
안드로이드 관련 라이브러리 모음
안드로이드 기기별 고유 ID
안드로이드 기본 어플에는 종료 기능이 없다
안드로이드 멀티랭귀지 구현
안드로이드 모바일 네트웍 관리 프로젝트(APN data access toggle for Android)
안드로이드 바이너리 파일을 역컴파일 하기
안드로이드 바코드 스캐너 (zxing)
안드로이드 샘플 예제
안드로이드 소스받기
안드로이드 시스템 정보 출력하기
안드로이드 아키텍쳐 구조
안드로이드 아키텍쳐 구조 - 그림으로
안드로이드 애플리케이션 배포방법
안드로이드 어플 고속 드로잉 튜닝요령( )
안드로이드 오픈소스 다운로드
안드로이드 폰과 이클립스 연동시 스샷
안드로이드 프로그램을 디버깅하는 방법
안드로이드를 공부하려면
안드로이드에서 JNI 사용하기 링크 정리
안드로이드에서 동적으로 클래스 파일 로딩하기
안드로이드에서 지원하는 자바 표준 패키지
안테나 영역 없애기
애플리케이션내 아이콘 추가하기
액티버티 상태 정보 저장
액티버티(Activity)에서 애니메이션 효과주기
어플내의 화면을 캡쳐하기
여러 다이얼로그 사용예제
여러 컴포넌트 레이아웃에서 사용예
연결되어 있는 네트웍상태(3G, Wifi 인지) 확인
오디오 / 비디오 실행
외국 안드로이드 OCR 엔진(무료아님)
외장(sdcard)의 멀티미디어 파일(이미지, 오디오)의 변화 감지하기
원격지에서 Stack Trace 정보 받기
웹뷰에서 줌버튼 안보이게 하기
웹브라우져 띄우기
유일한 Android device ID
이메일(email) 보내기
이메일(email)로 이미지 보내기
이뮬내 DB 파일 가져오기
이뮬레이어터에서 SD-Card 이뮬레이터 하기
이뮬레이터 단축키
이뮬레이터 초기화
이뮬레이터 튜토리얼
이뮬레이터(AVD)의 사용 메모리 늘리기
이뮬레이터에 구글 계정등록하기
이뮬에서 Failed to open ringtone content://settings/system/notification_sound 에러가 발생하면...
이미지를 Stretch 하기 (nine patch 사용하여, 이미지가 컴포넌트에 가득차게)
일정 시간후 강제로 대기모드로 들어가게 하기
일정(Calendar) 화면(Activity) 띄우기
임시파일 생성
자신에게서 onClickListener 대신에 부모 레이아웃(컨테이너)에서 클릭처리
자주 사용되는 Intent Protocal 목록
전화 수신 감지하기 (recognize incoming phonecall)
전화가 왔을때 이벤트 수신하기
접촉식 한글 자판 최신 바이너리 (.apk)
제스쳐 (Gesture 관련정보)
주소록 사진 목록 읽어오기
주소록(contacts) 상세창 띄위기
주소록(contacts) 편집창 띄우기
주요 Intent 사용예제
증감현실을 Android 에뮬레이터에서 구동하는 toolkit - NyArToolkit
지도상(구글맵)에 현재 자신의 위치 표기하기 ( startActivity 사용)
찍은 사진에 대해서 영역을 자르기
차트(chart) 그래프 오픈소스
출력화면 캡쳐 참고할만한 소스
캘린더(일정) 삭제하기
캘린더(일정) 정보 읽어오기
커스텀(Custom) 위젯 만들기
클립보드내 텍스트가 존재하는지 체크
파일(File) 사용
폰 부팅후 현재까지 경과한 시간
폰(이뮬)내 설치된 SDK 버젼알기
폰에서 디버깅하기
폰의 랭귀지(언어) 설정 읽어오기
폰의 전원꺼짐및 Screen On/Off 제어 (PowerManager. newWakeLock)
폰트 변경하기
폴더 생성 및 파일 생성
프로그램 배포하기
프로그램 시작시 Locale 변경해서 멀티랭귀지 적용하기
프로그램 종료하기
한글 IME
해당 Action을 수행할수가 있는 액티버티(어플)이 존재하는지 찾기
헬로, 안드로이드 자료 분할압축1
헬로, 안드로이드 자료 분할압축2
현재 디바이스(폰)의 IP 주소 알아내기
현재 사용 가능한 메모리정보 얻어오기
현재 연주중인 오디오 어플을 음소거 하기
현재 폰의 orientation 값 구하기(portrait, landscape)
화면 portrait / landscape 제어하기
화면 모드( portrait / landscape) 가 전환될때, OrientationEventListener 이벤트 받아내기
화면 사이즈 구하기
화면 해상도 구하기
화면 회전(Landscape / Portrait)
화면복귀시 ListActivity에서 선택한 항목으로 이동시키기
화면이 가로/세로 모드로 전환시 onDestroy()가 호출되지 않도록 하기
효과음 관련 자료


 
---------------------------------------------------------------------

소스코드


SD 카드에 저장되어 있는 이미지의 Thumbnails를 표시하기
Http connection , SaxParser and SharedPreferences 사용예제
Progressbar 사용예제
간단한 숫자 더하기 소스
계산기 소스
Custom View - HorizontalSlider
GridView 사용예제
ArrayAdapter 클래스를 사용해서 Gallery 클래스에 데이터 등록
Spinner 사용예제 (ArrayAdapter 사용)
버튼 사용예제
ListView 사용예제(ArrayAdapter 클래스를 사용)
ImageButton 사용예제 (리소스아이디 사용)
ImageButton 사용예제
ImageView내 이미지 출력 부분 지정하기
ImageView에 투명도(알파값) 지정하기
안드로이드 진행바 다이얼로그 예제
웹캠과 연동 예제
wifi 사용예제
ListActivity 예제 (동적으로 내용변경)
AutoCompleteTextView 사용예제
Menu 사용예제
Activity에서 전화걸기
ImageView 사용예제
Busy Coder's Guide 에서 나온 예제들
친구찾기 (FriendFind)
안드로이드 SDK - 바이오리듬 예제
위치정보 API를 사용하여 GPS 정보 취득 예제
구글 날짜 API 사용
화면에 볼튕기기
ImageView 사용예제
투명윈도우
MapView 사용예제2
MapView 사용예제1
WebView 내의 열람이력을 지운다
WebView 내에서 웹페이지 화면 확대/축소
WebView를 이용해서, 이전 웹페이지로 이동
WebView 사용예제
타이틀바의 글자색 변경
이미지를 crop해서 Bitmap 객체 생성하기
SurfaceView & Double Buffering (더블버퍼링)
AlertDialog 예제
Splash Screen
이미지파일 다운로드
URL을 통한 이미지 드로잉
Android SDK에 포함된 소스 구글링크
PopupWindow 사용예
WebView 사용예 (loadUrl, loadData)
UI Thread 사용예제
음악 스트리밍 소스
MP3 파일내 정보 추출
바이오리듬 자바 소스
동적으로 LinearLayout 화면에 이미지 추가하기2
동적으로 LinearLayout 화면에 이미지 추가하기
WebView 사용예
Android / SurfaceView 사용예제
카메라 제어
RSS Reader
SD 카드안의 음악파일 목록 출력하기
SD 카드에 저장되어 있는 비디오 목록 출력및 비디오 실행하기
3D 테트리스 게임 소스(1)
3D 테트리스 게임 소스(2)
탐색기
RSS Reader (리더) 소스
연락처(Contact) 계정 관리자
미디어 스트리밍(Custom Media Streaming with MediaPlayer)
로또(Lotto) 소스
간단한 MP3 플레이어 예제
폰내 음악 파일 목록 읽어오기
AutoCompleteTextView 사용예제
Twitter
MP3 파일 실행하기
XOR 예제
Service 예제
Gallery, ImageSwitcher 사용예제

Hey everyone,

Haven’t posted in a while – sorry school has been busy. Any who, this little code snippet/example will be on how to deal with the Intent.ACTION_SCREEN_OFF and the Intent.ACTION_SCREEN_ON, which will come in nifty if you’re making an application that might need to save state or respond to the user’s screen going to sleep/waking up, etc.

First, unlike other broad casted intents, for Intent.ACTION_SCREEN_OFF and Intent.ACTION_SCREEN_ON you CANNOT declare them in your Android Manifest! I’m not sure exactly why, but they must be registered in an IntentFilter in your JAVA code. And so, for this example we are going to have a receiver called ScreenReceiver, and I’m going to walk you through the differences between implementing it in a Service vs. in an Activity.

So, the receiver will simply look like:

01 public class ScreenReceiver extends BroadcastReceiver {
02  
03     // THANKS JASON
04     public static boolean wasScreenOn = true;
05  
06     @Override
07     public void onReceive(Context context, Intent intent) {
08         if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
09             // DO WHATEVER YOU NEED TO DO HERE
10             wasScreenOn = false;
11         else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
12             // AND DO WHATEVER YOU NEED TO DO HERE
13             wasScreenOn = true;
14         }
15     }
16  
17 }

Now, the first example will be for an Activity. Because of the life-cycle of an Activity, an Activity is actually easier to deal with as right before the screen turns off onPause() is called and right when the screen turns on onResume() is called, and so naturally we will handle the screen on/off events here:

01 public class ExampleActivity extends Activity {
02  
03    @Override
04     protected void onCreate() {
05         // INITIALIZE RECEIVER
06         IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
07         filter.addAction(Intent.ACTION_SCREEN_OFF);
08         BroadcastReceiver mReceiver = new ScreenReceiver();
09         registerReceiver(mReceiver, filter);
10         // YOUR CODE
11     }
12  
13     @Override
14     protected void onPause() {
15         // WHEN THE SCREEN IS ABOUT TO TURN OFF
16         if (ScreenReceiver.wasScreenOn) {
17             // THIS IS THE CASE WHEN ONPAUSE() IS CALLED BY THE SYSTEM DUE TO A SCREEN STATE CHANGE
18             System.out.println("SCREEN TURNED OFF");
19         else {
20             // THIS IS WHEN ONPAUSE() IS CALLED WHEN THE SCREEN STATE HAS NOT CHANGED
21         }
22         super.onPause();
23     }
24  
25     @Override
26     protected void onResume() {
27         // ONLY WHEN SCREEN TURNS ON
28         if (!ScreenReceiver.wasScreenOn) {
29             // THIS IS WHEN ONRESUME() IS CALLED DUE TO A SCREEN STATE CHANGE
30             System.out.println("SCREEN TURNED ON");
31         else {
32             // THIS IS WHEN ONRESUME() IS CALLED WHEN THE SCREEN STATE HAS NOT CHANGED
33         }
34         super.onResume();
35     }
36  
37 }

Now, note that in my onPause() and onResume() methods I run a check to see that the method was called DUE TO A SCREEN STATE CHANGE. This is important as often onPause() or onResume() will get called because of other reasons – i.e. a new activity is being started on top of this one, or an incoming call might be coming in, etc – and you want to make sure that your screen change logic is only called when the screen has actually changed.

Now, something to keep in mind, is that the order of events before the system screen turns off is:

ExampleActivity.onPause() –> ScreenReceiver.onReceive()

Which is a little unintuitive as you’d think the receiver would get hit first – and so when you play around with setting booleans, etc, be aware of this little fact, and likewise when the screen turns on the order of events is:

ExampleActivity.onResume() –> ScreenReceiver.onReceive()

And so again the order of events seems a little “backwards”.

Now, for a Service, it’s a little bit different since there is no onResume() or onPause() that gets called as the Service is always “running” in the background, and so instead what you’re going to have to do is modify your receiver a little to look like:

01 public class ScreenReceiver extends BroadcastReceiver {
02  
03     private boolean screenOff;
04  
05     @Override
06     public void onReceive(Context context, Intent intent) {
07         if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
08             screenOff = true;
09         else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
10             screenOff = false;
11         }
12         Intent i = new Intent(context, UpdateService.class);
13         i.putExtra("screen_state", screenOff);
14         context.startService(i);
15     }
16  
17 }

And your service will look like:

01 public static class UpdateService extends Service {
02  
03         @Override
04         public void onCreate() {
05             super.onCreate();
06             // REGISTER RECEIVER THAT HANDLES SCREEN ON AND SCREEN OFF LOGIC
07             IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
08             filter.addAction(Intent.ACTION_SCREEN_OFF);
09             BroadcastReceiver mReceiver = new ScreenReceiver();
10             registerReceiver(mReceiver, filter);
11         }
12  
13         @Override
14         public void onStart(Intent intent, int startId) {
15             boolean screenOn = intent.getBooleanExtra("screen_state"false);
16             if (!screenOn) {
17                 // YOUR CODE
18             else {
19                 // YOUR CODE
20             }
21         }
22 }

And so this is pretty self explanatory. When the screen state changes, it will notify your ScreenReceiver and from there you can set the state information into an Intent and send that data to your Service which can then handle it appropriately.

Hopefully this was useful. Let me know if you have questions.

Happy coding.

- jwei

[출처] http://cafe.naver.com/ccjmaster.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=168

<리니어 레이아웃>

 

 방향 설정

 

  차일드 뷰를 일렬로 배치하는 레이아웃이다.

 

  부모 뷰는 화면 전체, 차일드 뷰는 화면 하나하나를 구성하는 요소라고 보면 된다.

 

  ...

 

  리니어의 가장 중요한 속성은 배치 방향을 결정하는 orientation이다.

 

  차일드를 배치하는 방법은 수평(horizontal)·수직(vertical) 2가지가 있다.

 

  별도로 지정하지 않으면, default 속성은 수평(horizontal)이 적용된다.

 

<Button&Edit>

 

  앞서 우리가 했던 Button&Edit 에제에서

 

  orientation 속성만 vertical에서 horizontal로 바꾸어보자.

 

  Layout/Buttonedit.xml

 

  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:orientation="vertical" // horizontal로 바꾸어보자.
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      >
  <EditText
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content"
      android:id="@+id/edit" 
      android:text="여기다 입력"
      />
  <Button
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:id="@+id/btn" 
      android:text="입력 완료"
      />
  </LinearLayout>

 

  기존의 verticla이었던 Button&Edit 예제                          horizontal로 수정한 예제

 

      

 

<TextView>

 

  기존의 했던 TextVeiw도 해보도록 하자.

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" // 마찬가지로 horizontal로 해본다.
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >

<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/insa"
    android:textColor="#ff0000"
    android:textSize="20pt"
    android:textStyle="italic"
    />
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/anyoung"
    android:textSize="20sp"
    android:textColor="#0000ff"
    />
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="Good Morning"
    android:textColor="#8000ff00"
    android:textSize="5mm"
    android:typeface="serif"
    />
</LinearLayout>

 

  그리고 실행해보면 Hello 텍스트 하나만 나오고, 두 문자열을 화면에서 사라져 버렸다.

 

  

 

  왜냐하면 텍스트 뷰의 layout_width 속성이 fill_parent로 되어 있어 부모의 폭 전체를 다 차지하기 때문이다.

 

  셋 모두 보이게 하려면, 텍스트 뷰의 layout_width의 속성을 wrap_content로 바꿔 자신의 폭만큼 차지하도록 수정해야 한다.

 

<?xml version="1.0" encoding="utf-8"?> // xml 문서에서는 주석이 통하지 않으므로 복사 후 제거하고 실행한다.
<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="@string/insa"
    android:textColor="#ff0000"
    android:textSize="20pt"
    android:textStyle="italic"
    />
<TextView  
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="@string/anyoung"
    android:textSize="20sp"
    android:textColor="#0000ff"
    />
<TextView  
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="Good Morning"
    android:textColor="#8000ff00"
    android:textSize="5mm"
    android:typeface="serif"
    />
</LinearLayout>

 

  바꾸고 실행해보면 이렇게 나온다.

 

  

 

  -----------

 

  정렬지정

 

  뷰의 영역이 충분히 크면 상하좌우에 넓은 여백이 생긴다.

 

  하지만 뷰가 커진다고 해서 내용물도 같이 커지는 것은 아니며, 이때 내용물을 뷰의 어느 쪽에 배치할 것인지의 문제가 생긴다.

 

  ...

 

  내용물을 뷰의 어느 쪽에 배치할 것인가는 gravity 속성으로 지정한다. 즉, 내용물의 정렬 방식은 gravity로 통제한다.

 

  수평, 수직 방향에 대해 각각 정렬 방식을 지정할 수 있으며 '|' 연산자로 두 개의 속성을 묶어서 지정할 수도 있다.

 

  두 속성을 같이 지정할 때는 '|' 연산자 양쪽으로 공백이 전혀 없어야 한다.

 

  ...

 

  각 정렬은 비트 필드로 정의 되어 있으며

 

  contet와 fill은 수평, 수직 정렬 상태 플래그의 조합으로 정의되어 있다.

   

 

  이 속성을 테스트하기 위해 예제를 만들어보자.

 

  리니어 레이아웃 안에 폭과 높이를 둘 다 fill_parent로 지정했다.

 

  리니어 전체를 텍스트 뷰로 가득 채웠으므로, 텍스트 뷰의 넓이는 화면 전체와 같으며 여백이 굉장히 많은 상황이다.

 

  Layout/gravity1.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="fill_parent" 
      android:layout_height="fill_parent" 
      android:text="정렬 테스트"
      android:textSize="30px"
      android:textColor="#00ff00"
      />
</LinearLayout>

 

  gravity 속성을 따로 지정하지 않았으므로, 디폴트인 좌상단이 적용되며 문자열이 텍스트 뷰의 왼쪽 위에 출력된다.

 

  

 

  다음과 같이 gravity 속성을 center로 지정해 보자.

 

  Layout/gravity2.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="fill_parent" 
      android:layout_height="fill_parent" 
      android:text="정렬 테스트"
      android:textSize="30px"
      android:textColor="#00ff00"
      android:gravity="center"
      />
  </LinearLayout>

 

  실행해보면 center는 center_horizontal|center_vertical과 같음을 알 수 있다.

 

  수평, 수직, 모두 중앙이므로 문자열이 텍스트 뷰의 정중앙에 나타날 것이다.

 

  

 

  수평, 수직 각각에 대해 다른 정렬 방향을 지정할 수도 있다. 이번에는 다음과 같이 바꿔보자.

 

  Layout/gravity3.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="fill_parent" 
      android:layout_height="fill_parent" 
      android:text="정렬 테스트"
      android:textSize="30px"
      android:textColor="#00ff00"
      android:gravity="center_vertical"
      />
  </LinearLayout>

 

  Layout/gravity4.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="fill_parent" 
      android:layout_height="fill_parent" 
      android:text="정렬 테스트"
      android:textSize="30px"
      android:textColor="#00ff00"
      android:gravity="center_vertical|right"
      />
  </LinearLayout>

 

  center_vertical을 지정하면 수직으로는 중앙이 되고, 수평쪽으로는 디폴트인 왼쪽이 적용되어 중앙 왼쪽에 문자열이 나타난다.

 

  물론 '|' 연산자로 묶어 별도의 정렬 방식을 지정할 수 있다. 수직으로는 중앙, 오른쪽에 정렬하면 오른쪽에 붙는다.

 

      

 

  문자열을 오른쪽 바닥에 정렬하려면 "bottom|right"으로 지정하면 되고, 상단 중앙은 "center_horizontal|top"으로 지정하면 된다.

 

  그렇다고 아무 조합이나 다 되는 것은 아니고, 수평·수직 한 방향에 대해 플래그 하나씩 2개만 조합이 가능하다.

 

  gravity 속성 하나만 놓고 보면 어렵지 않을 것이다.

 

  ...

 

  그런데 gravity와 유사한 속성으로 layout_gravity 라는 속성이 또 있다.

 

  사용할 수 있는 속성 값은 gravity와 동일하나, 적용되는 대상이 다르다.

 

   · gravity : 뷰 안의 내용물을 어디에 배치할 것인가?

 

   · layout_gravity : 뷰를 부모의 어디에 둘 것인가?

 

  방금 전에 했던 gravity2의 예제와 비교를 한다면, 이해가 빠를 것이다.

 

  Layout/gravity2 // 텍스트 뷰를 리니어에 가득 채우고, 내용물을 center에 옮긴다.

 

  <?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="fill_parent" 
      android:layout_height="fill_parent" 
      android:text="정렬 테스트"
      android:textSize="30px"
      android:textColor="#00ff00"
      android:gravity="center"
      />
  </LinearLayout>

 

  

 

  Layout/layout_gravity2 // 텍스트 뷰를 문자열 크기로 설정하고, 텍스트 뷰 자체를 리니어의 중아에 배치한다.

 

  <?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"
      android:gravity="center"
      >
  <TextView  
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:text="정렬 테스트"
      android:textSize="30px"
      android:textColor="#00ff00"
      android:background="#ff0000"
      />
  </LinearLayout>

 

  

 

  분명 같은 그림이지만, 내용은 다르다.

 

  두 방법은 분명한 차이가 있는데, 리니어의 gravity의 속성을 사용하면

 

  두 개 이상의 차일드 뷰를 묶어서 원하는 곳에 배치할 수 있다는 점이다.

 

  Layout/lgravity3.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"
      android:gravity="center"
      >
  <TextView  
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:text="정렬 테스트"
      android:textSize="30px"
      android:textColor="#00ff00"
      android:background="#ff0000"
      />
  <Button
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:text="버튼이다."
      />
  </LinearLayout>

 

  두 차일드 뷰가 중앙에 수직으로 나란히 배치되며 겹치지도 않는다.

 

  두 차일드의 집합이 리니어의 입장에서 볼 때는 내용물이며, 따라서 center 속성이 적용되어 중앙에 온다.

 

  차일드 집합 내부에서는 수직 리니어의 배치 규칙에 따라, 두 위젯이 수직으로 나란히 놓인다.

 

  

 

  gravity는 내용물의 정렬 상태를 지정하며, layout_gravity는 뷰 자체의 정렬 상태를 지정한다.

 

  다음 예를 보면 이 두 속성의 차이점과 적절한 활용처를 분명히 알 수 있을 것이다.

 

  Layout/lgravity4.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="200px" 
      android:layout_height="100px" 
      android:text="정렬 테스트"
      android:textSize="30px"
      android:textColor="#00ff00"
      android:background="#ff0000"
      android:layout_gravity="center_horizontal"
     android:gravity="right|bottom"

      />
  </LinearLayout>

 

  layout_gravity는 차일드 뷰를 정렬하므로, 수평으로 중앙에 놓았다.

 

  gravity는 내용을 정렬하므로, 차일드 뷰의 오른쪽 아래에 놓았다.

  

 

  알고보니 차~암 쉽죠?



안드로이드의 기본적인 액티비티간 화면 전환 효과는 우에서 좌로 새로운 액티비티가 넘어와

 

화면이 전환되는 형태로 굉장히 심플하고 어떻게 보면 볼품 없다고 생각할 수도 있다 -_-ㅋ

 

이에 간단한 방법으로 액티비티 전환 시 Fade효과를 주어 좀 더 부드럽고 고급스러운

 

화면 전환 효과를 이용해 보자.

 

 

 

- 첨부파일 2개 (fade.xmlhold.xml)를 다운 받는다.

 

res 폴더 밑에anim라는 폴더를 생성해준다음 그 밑에 위에서 다운 받은 파일 2개를 넣어준다.

 

- 이동전 액티비티의 소스코드에서 다음 액티비티를 호출하는 부분인 startActivity(intent);

 

 밑에 다음의 코드를 넣어준다.

 

overridePendingTransition(R.anim.fade, R.anim.hold);

 

 

 

이 외에도 다양한 xml파일을 이용하여 액티비티 전환 시 다양한 화면 효과가 연출 가능하다.

출처 : http://lonelyshadow.tistory.com/77

개발을 하다보면 어플리케이션 전체에서 적용되는 전역 변수, 혹은 단 하나의 인스턴스를 사용해야할 때가 있다.

현재 XMPP를 이용하여 개발을 하고 있는데, connection을 단 하나만 유지해야한다.

보통 Java를 사용할 때는 Singleton을 이용하여 유일한 인스턴스를 생성하는데, (나도 처음에 Android Activity, Service와는 별개로 Singleton을 만드는 Class를 만들었었고..)

이 Singleton에서 Activity나 Service로 데이터를 주고 받는 것이 여간 어려운 일이 아니다.

실제로 만들었을 때도, Service에서 Singleton에 있는 인스턴스를 받아오려고 했더니 인스턴스를 새로 생성하고... 하여간 문제가 많다 -_-...

그런데 조금 찾아보니, Android에서 Singleton을 사용할 필요가 없었다.

Android에서는 어플리케이션 전체에 사용되는 변수 및 함수를 넣어놓는 Application Class가 있기 때문이다.

Activity 생성때와 마찬가지로 Application을 상속받아서 Application Class를 만들고,

Menifest.xml에서 현재 존재하는 application 항목에 android:name을 추가하여 만들어진 Application Class의 이름을 넣어주면 된다.

Application Class에는 Context가 존재하기 때문에, SQLiteDatabase도 바로 사용할 수 있으므로 데이터 주고 받는 것이 굉장히 편해진다. 물론 Singleton의 장점을 사용할 수 있는 것은 두말할 필요도 없고.

onCreate()로 Application 실행시의 동작을 정해주고

onTerminate()로 종료시의 동작을 정해줄 수도 있다.

Activity에서 불러올 때는,

ApplicationClassName ACN = (ApplicationClassName)getApplicationContext();
으로 생성하여 사용하면 되겠다
Bundle extras = new Bundle(); 
extras
.putSerializable("aa", Object); 
intent
.putExtras(extras); 
... 
 
Object y = getIntent().getExtras().get("aa"); 

=========================================== 
1 Vector<LOGINDATA> vvv = new Vector<LOGINDATA>();
2 Intent intent = new Intent();
3 intent.putExtra("key", vvv);


백터가 컬랙션이기 때문에 직렬화가 가능해서 위와같이 넣고 받아서 뺄때는 아래와 같이 하면 되요. ^^

 
1 Vector<LOGINDATA> rrr = (Vector<LOGINDATA>)intent.getExtras().getSerializable("key");

출처 : http://pulsebeat.tistory.com/18


<목표> 
[안드로이드] Preference – UI 정보, 간단한 변수 저장하기

   

 

  이번에는 Preference에 대해 다루어 보겠습니다. Preference(이하 프리퍼런스)는 화면 이동 시 간에 발생하는 데이터 손실을 방지하기 위해 사용되는 자료형입니다. 간단한 예로 들자면, 친구에게 메시지를 보내기 위해 열심히 문자를 쓰고 있는 와중에, 전화와 온다고 가정합시다. 전화를 받고 나서 긴 장문의 메시지가 날라간다면 어떤 느낌이 들까요? 이런 일을 방지하기 위해서 프리퍼런스라는 자료형을 사용합니다. 화면 전환 시, UI 정보를 저장하고, 다시 그 화면으로 돌아왔을 때의 UI 정보를 불러와서 다시 화면에 뿌려주는 역할을 합니다. 이 밖에도 화면 전환 시에 저장하고 싶은 데이터를 저장할 수도 있습니다. 즉 간단하게 말하면, 임시 저장소라고 생각하시면 될 것 같습니다.

   

[핵심코드]   Preference

   

엑티비티 변환 데이터를 저장합니다.

SharedPreferences prefs = getSharedPreferences("PrefName", MODE_PRIVATE);

SharedPreferences.Editor editor = prefs.edit();

editor.putString(KEY_MY_PREFERENCE, text);

editor.commit();

엑티비티를 다시 실행하였을 데이터를 불러옵니다.

SharedPreferences prefs = getSharedPreferences("PrefName", MODE_PRIVATE);

String text = prefs.getString(KEY_MY_PREFERENCE, "");

   

   

  아래의 예제에서는 간단하게 두 엑티비티 간의 화면이 바뀌었다가 다시 돌아왔을 때 제대로 프리퍼런스가 동작이 되고 있는지 확인합니다.

     

   

STEP 1  Java Source Code

   

  자바 코드는 두 가지를 다루게 됩니다. 첫 엑티비티는 시작화면으로, 간단히 데이터를 입력하고 화면을 전환시킵니다. 두 번째 엑티비티는 별다른 기능없이 다시 첫번째 엑티비티로 돌아가는 역할만 수행합니다.

   

     

[[Activity]]   Preference 를 이용한 첫 번째 엑티비티

   

package example.preference;

   

import android.app.Activity;

import android.content.Intent;

import android.content.SharedPreferences;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

   

   

public class PreferenceTestActivity extends Activity {

   

public static final String KEY_MY_PREFERENCE = "my_preference";

@Override

   public void onCreate(Bundle savedInstanceState) {

   super.onCreate(savedInstanceState);

   setContentView(R.layout.main);

   

   Button bt = (Button)findViewById(R.id.nextButton);

   bt.setOnClickListener(new View.OnClickListener() {

              

            @Override

            public void onClick(View v) {

                Intent intent =

                new Intent(PreferenceTestActivity.this, NextActivity.class);

                startActivity(intent);

            }

        });

   

   SharedPreferences prefs = getSharedPreferences("PrefName", MODE_PRIVATE);

   String text = prefs.getString(KEY_MY_PREFERENCE, "");

      // 번째인자는 , 두번째 인자는 키에 대한 데이터가 존재하지 않을 경우의 디폴트값

     final EditText edit =(EditText)findViewById(R.id.text);

   edit.setText(text);

}

   

    // onStop() : 이상 엑티비티가 사용저에게 보여지지 않을 불립니다.

    //          기존에 존재하는 데이터를 저장합니다.

   protected void onStop() {

    super.onStop();

   

   EditText editText =(EditText)findViewById(R.id.text);

   String text = editText.getText().toString();

   

       // 데이타를 저장합니다.

   SharedPreferences prefs = getSharedPreferences("PrefName", MODE_PRIVATE);

   SharedPreferences.Editor editor = prefs.edit();

   editor.putString(KEY_MY_PREFERENCE, text);

   editor.commit();

   }

}

     

[Activity ①]   SharedPreferences 이용하여 데이터 저장하기

   

  첫 번째 엑티비티로 화면에서 간단한 UI를 조작하고 있습니다. 버튼을 클릭하면 Intent를 이용하여 다음 엑티비티로 전환합니다. 화면을 전환하게 되면 onStop()이 호출이 되게 되며, 이 상태에서 저희가 사용할 프리퍼런스를 이용해서 데이터를 저장해야합니다. 위의 코드에서 보는 것처럼 SharedPreFerences라는 데이터 유형을 사용해야 합니다. 안드로이드 자체에서 제공해주는 자료형으로써, getSharedPreferences( Key값, Permission값 ) 메서드를 호출하여 자료형의 속성을 지정할 수 있습니다.

"PrefName" 은 말그대로 자신이 정하고 싶은 Key 값이라고 보시면 됩니다. 다음에 데이터를 호출할 때 이 Key 값을 이용하여 그 자료형을 불러올 수 있습니다. 다음 인자는 이 자료형에 대한 권한 설정을 해주는 부분입니다. MODE_PRIVATE, MODE_WORLD_READABLE, MODE_WORLD_WRITEABLE 을 이용하여 이 파일에 대한 접근 권한을 설정합니다.

   

MODE_PRIVATE

-rw-rw----

MODE_WORLD_READABLE

-rw-rw-r--

MODE_WORLD_WRITEABLE

-rw-rw--w-

   

  위에서 보시는 것과 같이 이 데이터에 대한 권한을 설정할 수 있습니다. 리눅스 개발을 한번이라도 접해신분들이라면 곧바로 이해가 되실 겁니다. 이렇게 저장된 프리퍼런스는 xml 형태로 저장이 됩니다. (소스 내에 존재하진 않습니다.) 패키지 파일 내에 있는 shared_prefs 영역에 선언된 SharedPreferences 자료가 저장되어 있는 것을 확인하실 수 있습니다.

  이렇게 저장된 프리퍼런스를 이용하여 Editor라는 자료형으로 선언합니다. 말 그대도 편집하기 위해서 선언을 한 뒤에, 자신이 저장하고 싶은 데이터 이름을 설정해주고, 저장하고 싶은 변수를 넣으면 저장이 됩니다. 그리고 마지막으로 commit()을 호출하면 데이터가 저장됩니다.

   

[[Activity ②]]   SharedPreferences 이용하여 데이터 불러오기

   

  이제 저장한 데이터를 이용하여 불러와야 합니다. 불러오는 부분은 엑티비티가 생성 시, 즉 onCreate( ) 부분에 넣으면 됩니다. SharedPreferences를 설정해주고, 미리 정해둔 Key 값을 넣어 데이터를 불러오면 됩니다. 아주 간단하게 작업이 이루어집니다.

  엑티비티의 라이프 사이클을 고려하여, 해당하는 부분에 알맞도록 프리퍼런스가 동작하도록 설정해주면, 입맛에 맞게 이용할 수 있습니다.

   

   

[[Activity]]   단순히 뒤로갈 수 있게 만드는 두 번째 화면 엑티비티

   

두 번째 화면 엑티비티는 테스트를 위해 필요한 부분입니다. 단순히 다시 화면으로 돌아갔을 때 원래의 값이 남아 있는지 확인 하는 부분입니다. 엑티비티를 생성하시고 간단히 뒤로 가는 버튼을 넣어주면 됩니다. 별다른 어려운 점이 없는 부분이니 넘어가겠습니다.

   

 class NextActivity extends Activity {

   

    public void onCreate(Bundle savedInstanceState) {

   super.onCreate(savedInstanceState);

   setContentView(R.layout.next);

   

   Button bt = (Button)findViewById(R.id.previousButton);

   bt.setOnClickListener(new View.OnClickListener() {

              

            @Override

            public void onClick(View v) {

                Intent intent =

                new Intent(NextActivity.this, PreferenceTestActivity.class);

                startActivity(intent);

            }

        });

  }

}

    

     

STEP 2  Xml Code

     

   첫 번째 엑티비티는 텍스트뷰와 에디트박스, 버튼으로 구성되어 있습니다. 에디트박스에 있는 데이터가 저장되고 다시 돌아왔을 때 저장되었는지를 확인합니다. 제대로 저장이 되어 있다면, 쓰고 있던 메시지가 그대로 저장이 되어야 합니다.

   

<?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="fill_parent"

   android:layout_height="wrap_content"

   android:text=" 엑티비티"

   />

   <EditText

   android:id="@+id/text"

   android:layout_width="fill_parent"

   android:layout_height="wrap_content"

   />

   <Button android:text="다음으로"

   android:id="@+id/nextButton"

   android:layout_width="wrap_content"

   android:layout_height="wrap_content"></Button>

</LinearLayout>

   

  두 번 째 엑티비티는 첫 번째 엑티비티를 이용하여 돌아가기 위해 버튼만 구현해 놓았습니다.

   

<?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="fill_parent"

   android:layout_height="wrap_content"

   android:text="다음 엑티비티"

   />

   <Button android:text="이전으로"

   android:id="@+id/previousButton"

   android:layout_width="wrap_content"

   android:layout_height="wrap_content">

   </>

</LinearLayout>

   

   

STEP 3  AndroidManifest.xml Code

   

  메니페스트에는 다음 엑티비티를 허용하겠다는 퍼미션만 추가하면 됩니다.

     

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="example.preference"

android:versionCode="1"

android:versionName="1.0">

   <application android:icon="@drawable/icon" android:label="@string/app_name">

   <activity android:name=".PreferenceTestActivity"

android:label="@string/app_name">

      <intent-filter>

         <action android:name="android.intent.action.MAIN" />

         <category android:name="android.intent.category.LAUNCHER" />

       </intent-filter>

      </activity>

     <activity android:name=".NextActivity"></activity>

   </application>

</manifest>

   

   

   

 < 마무리 >  프리퍼런스(Preference)를 이용하여 간단한 데이터 저장하기

   

  화면 이동 시 일어날 수 있는 데이터 손실을 미리 방지하기 위해 프리퍼런스를 사용합니다. 이러한 간단한 정보를 데이터베이스를 이용하여 접근하는 것은 속도와 효율적인 면에서 큰 손실이 아닐 수 없습니다. 그렇기 때문에 안드로이드에서 제공해주는 SharedPreferencs라는 데이터 유형으로 프로그래밍 내에서의 변수 선언처럼 간단히 데이터를 저장하고 불러 올 수 있습니다. 엑티비티의 각각의 라이프 사이클에 맞게 데이터를 저장, 불러오기를 한다면 조금 더 사용성에 이점이 있도록 어플리케이션을 개발할 수 있습니다.


  • 게임에서 다양한 효과음을 멀티체널로 호출할 수 있는 SoundManager 클래스입니다.
  • 소스는 구글링중에 예제소스 수정하였습니다.

     

[SoundManager.java]

package kr.co.joynuri.animalorchestra;

import java.util.Collection;
import java.util.HashMap;

import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;
import android.util.Log;

public class SoundManager {
   
    private static final String LOG_TAG = SoundManager.class.getSimpleName();
   
    private Context mContext;
    private SoundPool mSoundPool;
    private HashMap<Integer, Integer> mSoundPoolMap;
    private AudioManager  mAudioManager;
   
    public SoundManager(Context context) {
        mContext = context;
    }
   
    public void create() {
        mSoundPool = new SoundPool(16, AudioManager.STREAM_MUSIC, 0);
        mSoundPoolMap = new HashMap<Integer, Integer>();
        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
    }
   
    public void destroy() {
        if (mSoundPoolMap != null) {
            Collection<Integer> soundIds = mSoundPoolMap.values();
            for (int soundId : soundIds) {
                mSoundPool.unload(soundId);
                Log.d(LOG_TAG, "destroy sound id " + soundId);
            }
            mSoundPoolMap = null;
        }
    }
   
    public void load(int key, int resId) {
        Log.d(LOG_TAG, "load...START");
       
        mSoundPoolMap.put(key, mSoundPool.load(mContext, resId, 1));
       
        Log.d(LOG_TAG, "load...END");
    }
   
    public void play(int key) {
        Log.d(LOG_TAG, "play...START");
       
        int streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        mSoundPool.play(
                mSoundPoolMap.get(key),
                streamVolume, streamVolume,
                1, 0, 1f);
       
        Log.d(LOG_TAG, "play...END");
    }
   
    public void playLoop(int key) {
        Log.d(LOG_TAG, "playLoop...START");
       
        int streamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        mSoundPool.play(
                mSoundPoolMap.get(key),
                streamVolume, streamVolume,
                1, -1, 1f);
       
        Log.d(LOG_TAG, "playLoop...END");
    }
   
    public void stop(int key) {
        mSoundPool.stop(mSoundPoolMap.get(key));
    }
   
    public void pause(int key) {
        mSoundPool.pause(mSoundPoolMap.get(key));
    }
   
    public void resume(int key) {
        mSoundPool.resume(mSoundPoolMap.get(key));
    }
   
}

 

사용예) 사운드 등록

        int coundIndex = 0;
        mSoundManager = new SoundManager(mContext);
        mSoundManager.create();
        mSoundManager.load(coundIndex++, R.raw.snd_animal_0);
        mSoundManager.load(coundIndex++, R.raw.snd_animal_1);
        mSoundManager.load(coundIndex++, R.raw.snd_animal_2);
        mSoundManager.load(coundIndex++, R.raw.snd_animal_3);
        mSoundManager.load(coundIndex++, R.raw.snd_animal_4);
        mSoundManager.load(coundIndex++, R.raw.snd_animal_5);
        mSoundManager.load(coundIndex++, R.raw.snd_animal_6);
        mSoundManager.load(coundIndex++, R.raw.snd_animal_7);
        mSoundManager.load(coundIndex++, R.raw.snd_animal_8);

사용예) 사운드 해제

        mSoundManager.destroy();

사용예) 사운드 플레이

        mSoundManager.play(index);

 


+ Recent posts