출처 : http://cafe.naver.com/ccjmaster.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=1158&

<반복되는 배경 · 패턴 이미지 만들기>

 

  오늘은 반복되는 배경 이미지를 만들어 보겠습니다.

 

  간단히 말해, 이런 간단한 이미지를 가지고 패턴이 반복되는 배경을 만드는 것입니다.

 

 

 

 

  생각보다 간단합니다.

 

  보시다시피 Main Class에는 손 댈 일이 없습니다.

 

  먼저, drawable 폴더에 xml을 하나 만들어주시구요,

 

  pattern에 사용할 이미지도 넣어주셔야겠죠?

 

 



 자, 이제 background.xml을 열고 다음과 같이 작성합니다.

 

  bitmap 속성을 주셔야하는데, 이것을 대문자로 주시면 적용되지 않습니다.

 

  마찬가지로 @drawable/pattern, 이렇게 반복시켜 줄 이미지를 선택하신 다음

 

  android:tileMode="repeat"라고 설정해주시기 바랍니다.

 

 

 

 

  그리고 원래 생성되어 있던 main.xml을 열어주세요.

 

  android:background="@drawable/background"라고 해주시면 됩니다.

 

  당연히 background라는 XML이 drawable에 있어야겠죠?

 

 

 

 

  그런 다음 Application을 실행시켜주면, 이렇게 나오게 될 것입니다.

 

 

 

  참, 간단하죠?

 

출처 : http://jsharkey.org/blog/2008/08/18/separating-lists-with-headers-in-android-09/

Separating Lists with Headers in Android 0.9

Monday August 18, 2008 at 4:42 PM

Earlier today the latest Android 0.9 SDK was released, and it’s packed full of wonderful changes. As you play around, you might see ListViews split into sections using separating headers. (Example shown on the right is the browser settings list.)

There isn’t an easy way of creating these separated lists, so I’ve put together SeparatedListAdapter which does it quickly. To summarize, we’re creating a new BaseAdapter that can contain several other Adapters, each with their own section headers.

First let’s create some simple XML layouts to be used for our lists: first the header view, then two item views that we’ll use later for the individual lists. (Thanks to Romain Guy for helping me find existing styles to keep these XML layouts nice and tidy.)

  1. <!-- list_header.xml -->  
  2. <TextView  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     android:id="@+id/list_header_title"  
  5.     android:layout_width="fill_parent"  
  6.     android:layout_height="wrap_content"  
  7.     android:paddingTop="2dip"  
  8.     android:paddingBottom="2dip"  
  9.     android:paddingLeft="5dip"  
  10.     style="?android:attr/listSeparatorTextViewStyle" />  
  11.   
  12. <!-- list_item.xml -->  
  13. <TextView  
  14.     xmlns:android="http://schemas.android.com/apk/res/android"  
  15.     android:id="@+id/list_item_title"  
  16.     android:layout_width="fill_parent"  
  17.     android:layout_height="fill_parent"  
  18.     android:paddingTop="10dip"  
  19.     android:paddingBottom="10dip"  
  20.     android:paddingLeft="15dip"  
  21.     android:textAppearance="?android:attr/textAppearanceLarge"  
  22.     />  
  23.   
  24. <!-- list_complex.xml -->  
  25. <LinearLayout  
  26.     xmlns:android="http://schemas.android.com/apk/res/android"  
  27.     android:layout_width="fill_parent"  
  28.     android:layout_height="wrap_content"  
  29.     android:orientation="vertical"  
  30.     android:paddingTop="10dip"  
  31.     android:paddingBottom="10dip"  
  32.     android:paddingLeft="15dip"  
  33.     >  
  34.     <TextView  
  35.         android:id="@+id/list_complex_title"  
  36.         android:layout_width="fill_parent"  
  37.         android:layout_height="wrap_content"  
  38.         android:textAppearance="?android:attr/textAppearanceLarge"  
  39.         />  
  40.     <TextView  
  41.         android:id="@+id/list_complex_caption"  
  42.         android:layout_width="fill_parent"  
  43.         android:layout_height="wrap_content"  
  44.         android:textAppearance="?android:attr/textAppearanceSmall"  
  45.         />  
  46. </LinearLayout>  

Now let’s create the actual SeparatedListAdapter class which provides a single interface to multiple sections of other Adapters. After using addSection() to construct the child sections, you can easily use ListView.setAdapter() to present the now-separated list to users.

As for the Adapter internals, to correctly find the selected item among the child Adapters, we walk through subtracting from the original position until we find either a header (position = 0) or item in the current child Adapter (position < size).

Here’s the source for SeparatedListAdapter:

  1. public class SeparatedListAdapter extends BaseAdapter {   
  2.   
  3.     public final Map<String,Adapter> sections = new LinkedHashMap<String,Adapter>();   
  4.     public final ArrayAdapter<String> headers;   
  5.     public final static int TYPE_SECTION_HEADER = 0;   
  6.   
  7.     public SeparatedListAdapter(Context context) {   
  8.         headers = new ArrayAdapter<String>(context, R.layout.list_header);   
  9.     }   
  10.   
  11.     public void addSection(String section, Adapter adapter) {   
  12.         this.headers.add(section);   
  13.         this.sections.put(section, adapter);   
  14.     }   
  15.   
  16.     public Object getItem(int position) {   
  17.         for(Object section : this.sections.keySet()) {   
  18.             Adapter adapter = sections.get(section);   
  19.             int size = adapter.getCount() + 1;   
  20.   
  21.             // check if position inside this section   
  22.             if(position == 0return section;   
  23.             if(position < size) return adapter.getItem(position - 1);   
  24.   
  25.             // otherwise jump into next section   
  26.             position -= size;   
  27.         }   
  28.         return null;   
  29.     }   
  30.   
  31.     public int getCount() {   
  32.         // total together all sections, plus one for each section header   
  33.         int total = 0;   
  34.         for(Adapter adapter : this.sections.values())   
  35.             total += adapter.getCount() + 1;   
  36.         return total;   
  37.     }   
  38.   
  39.     public int getViewTypeCount() {   
  40.         // assume that headers count as one, then total all sections   
  41.         int total = 1;   
  42.         for(Adapter adapter : this.sections.values())   
  43.             total += adapter.getViewTypeCount();   
  44.         return total;   
  45.     }   
  46.   
  47.     public int getItemViewType(int position) {   
  48.         int type = 1;   
  49.         for(Object section : this.sections.keySet()) {   
  50.             Adapter adapter = sections.get(section);   
  51.             int size = adapter.getCount() + 1;   
  52.   
  53.             // check if position inside this section   
  54.             if(position == 0return TYPE_SECTION_HEADER;   
  55.             if(position < size) return type + adapter.getItemViewType(position - 1);   
  56.   
  57.             // otherwise jump into next section   
  58.             position -= size;   
  59.             type += adapter.getViewTypeCount();   
  60.         }   
  61.         return -1;   
  62.     }   
  63.   
  64.     public boolean areAllItemsSelectable() {   
  65.         return false;   
  66.     }   
  67.   
  68.     public boolean isEnabled(int position) {   
  69.         return (getItemViewType(position) != TYPE_SECTION_HEADER);   
  70.     }   
  71.   
  72.     @Override  
  73.     public View getView(int position, View convertView, ViewGroup parent) {   
  74.         int sectionnum = 0;   
  75.         for(Object section : this.sections.keySet()) {   
  76.             Adapter adapter = sections.get(section);   
  77.             int size = adapter.getCount() + 1;   
  78.   
  79.             // check if position inside this section   
  80.             if(position == 0return headers.getView(sectionnum, convertView, parent);   
  81.             if(position < size) return adapter.getView(position - 1, convertView, parent);   
  82.   
  83.             // otherwise jump into next section   
  84.             position -= size;   
  85.             sectionnum++;   
  86.         }   
  87.         return null;   
  88.     }   
  89.   
  90.     @Override  
  91.     public long getItemId(int position) {   
  92.         return position;   
  93.     }   
  94.   
  95. }  

As expected, it correctly prevents the section headers from being selected, and seamlessly stiches together the various Adapters.

This approach also uses convertView correctly as long as the child Adapters return getItemViewType() and getViewTypeCount() normally. No special changes are needed for an Adapter to become a child.

Now let’s use SeparatedListAdapter in some example code. We use the XML layouts defined earlier to create an ArrayAdapter and an advanced two-row SimpleAdapter, and then add both as sections to our SeparatedListAdapter.

  1. public class ListSample extends Activity {   
  2.   
  3.     public final static String ITEM_TITLE = "title";   
  4.     public final static String ITEM_CAPTION = "caption";   
  5.   
  6.     public Map<String,?> createItem(String title, String caption) {   
  7.         Map<String,String> item = new HashMap<String,String>();   
  8.         item.put(ITEM_TITLE, title);   
  9.         item.put(ITEM_CAPTION, caption);   
  10.         return item;   
  11.     }   
  12.   
  13.     @Override  
  14.     public void onCreate(Bundle icicle) {   
  15.         super.onCreate(icicle);   
  16.   
  17.         List<Map<String,?>> security = new LinkedList<Map<String,?>>();   
  18.         security.add(createItem("Remember passwords""Save usernames and passwords for Web sites"));   
  19.         security.add(createItem("Clear passwords""Save usernames and passwords for Web sites"));   
  20.         security.add(createItem("Show security warnings""Show warning if there is a problem with a site's security"));   
  21.   
  22.         // create our list and custom adapter   
  23.         SeparatedListAdapter adapter = new SeparatedListAdapter(this);   
  24.         adapter.addSection("Array test"new ArrayAdapter<String>(this,   
  25.             R.layout.list_item, new String[] { "First item""Item two" }));   
  26.         adapter.addSection("Security"new SimpleAdapter(this, security, R.layout.list_complex,   
  27.             new String[] { ITEM_TITLE, ITEM_CAPTION }, new int[] { R.id.list_complex_title, R.id.list_complex_caption }));   
  28.   
  29.         ListView list = new ListView(this);   
  30.         list.setAdapter(adapter);   
  31.         this.setContentView(list);   
  32.   
  33.     }   
  34.   
  35. }  

The resulting interface behaves just like the browser preferences list, and you could easily create other custom Adapters to insert into the various sections, such as including icons or checkboxes.

These section headers can really help separate out otherwise-cluttered activities. I used them several places in my CompareEverywhere application which lets you easily compare prices and read reviews for any product with a barcode.

Watch my blog using Google Reader: Add to Google


Anyone that is in need of it .. attached here is the Motorola Atrix4G addon for the Android SDK .. also the instructions for installation and execution.

http://www.mediafire.com/?73hkz09wr0l1kqe

Installing and Using Motorola SDK Add-ons

A Motorola SDK add-on, when added to the Android™ SDK, allows you to run and debug your applications on an emulated Motorola handset. Note that while the device image that the add-on presents does not necessarily reflect the look of the actual device, from a functional standpoint it should be a fairly faithful emulation of the actual handset.

Installing the add-on

To install the Motorola SDK add-on, unzip it, and copy the resulting directory to the directory named add-ons within the Android SDK that you are using (if you are using MOTODEV Studio for Android and are not sure where it placed the Android SDK, check the SDK Location field in the Android preferences dialog).

** NOTE: If MOTODEV Studio for Android or Eclipse™ were running when you copied the SDK add-on, you will need to restart it for the add-on to be recognized by your development environment.

Using the add-on

In order to run or debug your applications on an emulated Motorola handset, you must first create an AVD that has its AVD Target set to reference the SDK add-on. You can then deploy your applications to it. When creating your projects, you can either target the emulated Motorola device specifically (which you would do if your application runs only on that device), or you can select a more general, compatible target such as Android 1.5. The following sections detail the steps involved in each of these tasks.

Creating an AVD



To create an AVD named "Motorola" using the command line, do the following (you may need to be within the Android SDK's "tools" directory):

  1. android list targets
    The details for each possible target are listed, starting with a unique ID number that identifies that target. Make note of the ID number for the Motorola add-on you want to target.
  2. android create avd -n Motorola -t target-ID
    For target-ID supply the ID number for the target you obtained from the previous step.
To create it from within MOTODEV Studio for Android:
  1. Select Android AVD Manager from the Window menu. The Android Virtual Devices Manager dialog appears.
  2. Enter Motorola (or whatever name you want to give the new AVD) in the Name field.
  3. From the Target list, select the target named for the Motorola device you are targeting.
  4. From the Skin list, select the skin named for your target device.
  5. If you want the emulated device to have an SD card, in the SDCard field either specify the path and filename to a file containing an existing SD card image, or specify a size (such as 64M) to create a new, empty SD card image. Leave this field blank if the device isn't to have an SD card.
  6. Click Create AVD.
  7. Click Finish to close the dialog.
When creating Run or Debug configurations, you can now select this new AVD when choosing a target device.

**NOTE: If your newly-created AVD does not appear in the Device Management view, click Refresh, which is located in the top right corner of that view.

Starting the AVD

If you start a Run or Debug configuration that specifies a Motorola SDK add-on AVD as the target device, the AVD will be automatically launched for you. If you want to start the AVD without deploying an application to it, you can do so either from the command line or from within MOTODEV Studio for Android. From the command line, the following command will start the AVD named "Motorola":
emulator -avd Motorola
To start it from within MOTODEV Studio for Android, select the AVD from within the Device Management view and click Start.

Targeting the Motorola device

If your application is designed specifically for a Motorola device, select the corresponding Motorola SDK add-on as the project target when creating the project.



To change an existing project so that it targets the SDK add-on from an Eclipse-based IDE such as MOTODEV Studio for Android:
  1. Right-click the project in the Package Explorer and select Properties.
  2. From the list of properties select Android.
  3. The project build target is shown in the right side of the dialog; select the one named for your target device.
If you are not using an Eclipse-based IDE you can change your project's target with the android update project command; see Google's developer documentation on developing in other IDEs.
제작하는 한 어플리케이션에 버튼은 총 몇개나 등장할까요?

버튼의 이벤트를 등록하려면 또 똑같은 코딩을 몇번을 해야 할 지 모릅니다.

그래서 준비한 강쉪의 특별 소스.

'모든 뷰를 등록하자'입니다.

Android SDK는 java코딩만으로도 충분히 xml의 역할을 대신 할 수 있지만,

바로 눈으로 보이는 구조와 편리함, 연동성을 생각한다면 불가피하게 xml을 사용 할 수 밖에 없습니다.

Android SDK가 1.0부터 발전을 거듭해와 현재 나온 2.2 버전까지

달라진 메소드는 없지만 사라지거나 새로 생긴것들이 많습니다.

이번 주제와도 비슷한 예를 들자면

xml의 버튼에

<Button
    android:onClick = "ClickHandler"        //java에 연동되는 id가 아닌 onClick에 아이디를 줍니다.
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
>

라고 사용하신다면

java에선

public void ClickHandler(View target){
    Intent i = new Intent();
    switch(target.getId()){
    case R.id.$#^%^#%^%:
        break;
    }
}

이런식으로 손쉽게 구현이 가능해 집니다

이는 Android SDK 2.1 이상의 버젼에서만 사용되는 메소드로 그 이전의 SDK를 사용하면 구현되지 않습니다.

이제 이벤트를 처리하는 메소드를 알아봤으니

본격적으로 리스너를 등록해 보도록 하겠습니다.




한 프로젝트에 버튼이 50개 정도 등장한다고 생각해 봅시다.

그중 각 Activity마다 10개씩의 버튼을 사용 해야 한다면??

생각만 해도 어지럽습니다.

그래서 등장한것이


메인 Layout에 ID넣기!

일단은 다른생각 말고 따라해 보세요 ^^

예를들어 자신의 프로젝트에 Main으로 쓰이는 xml이

RelativeLayout이라면?

그 최상위 Layout에

android:id="@+id/mainview_layout"

이라고 등록을 해줍니다.

그리고 자바로 넘어가

onCreate 안에

RelativeLayout layout = (RelativeLayout)findViewById(R.id.mainview_layout);
setOnclickListener(layout);       //최상위 레이아웃 리스너 등록
    

이렇게 등록을 해주시는 겁니다.

이게 끝이 아니죠

    private void setOnclickListener(ViewGroup group){   //모든 뷰 그룹 등록
     View[] childViews = getChildViews(group);
     for (View view : childViews){     
      if(view instanceof Button){
       view.setOnClickListener(this);
      }else if(view instanceof ViewGroup){
       setOnclickListener((ViewGroup)view); //재귀돌림
      }
     }
    }

이렇게 모든 뷰를 등록 해줍니다.

이게 무슨 뜻일까요??

for문을 이용해 View에 생겨나는 자식 View들을 모두 등록해 줍니다.

하지만!

xml의 구조상  한 Layout안에 다른 Layout을 생성할 필요가 있겠죠.

그 때에는 재귀를 돌려 한번더 등록해주는 것입니다.

    private View[] getChildViews(ViewGroup group){
     int childCount = group.getChildCount();
     final View[] childViews = new View[childCount];
     for (int index =0;index < childCount; index++){
      childViews[index] = group.getChildAt(index);
     }
     return childViews;
    }

그 후 자식View에 java에서 쓰일 아이디값을 채워 넣어주면 ok!


xml에서 각 버튼의 아이디 값을 주어

그것이 R.id에 등록이 되고

또 그것을 java에서 사용하는데 보다 많은 도움이 되는 소스라 생각됩니다. ^^

그럼 이만 다음에 뵙겠습니다
width height w/h % 대표기종
320 480 66.67% 아이폰3Gs, Android G1
640 960 66.67% 아이폰4
==========================================================아이폰
480 800 60.00% 갤럭시S, 넥서스원, Desire
480 854 56.21% 모토로이
540 960 56.25% 아트릭스
==========================================================안드로이드 3.0
1280    720       ...   이후 넥서스프리미엄 출시이후(예정)
==========================================================안드로이드 4.0

출처 : http://blog.naver.com/vortex2s?Redirect=Log&logNo=10102806271

안드로이드를 개발하다보면 Dialog같은 메시지 창을 자주 쓰게 되는데 기본으로 주어지는 것은 AlertDialog와 Toast밖에는....ㅠㅠ AlertDialog는 이쁘지 않고 몬가 불만족스럽고 Toast는 간단한 메시지를 띄워서 보여주기 좋은데..이것도 마찬가지로..ㅠㅠ불만이 많다..

 

개발을 하다보면 나만의 메시지창을 만들어 보고 싶다는 생각을 하게 되는데 그럴때를 위해 Popup View로 이쁘게 만들면^^나만의 메시지창을 띄울수 있다.

예제 소스와 간단한 주석으로 나만의 Popup View를 만들어 보자.

 

먼저 PopView를 생성하도록하자.

 

public class PopView {
   protected final View anchor;
   protected final PopupWindow window;
   private View root;
   private Drawable background = null;
   protected final WindowManager windowManager;
   public PopView(View anchor) {
      this.anchor = anchor;
      this.window = new PopupWindow(anchor.getContext());
      window.setTouchInterceptor(new OnTouchListener() {
         @Override
         public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { // popupview영역 외의 바깥부분을 터치할 시
               PopView.this.window.dismiss();
               return true;
            }
            return false;
         }
   });  

      windowManager = (WindowManager) anchor.getContext().getSystemService(Context.WINDOW_SERVICE);
      onCreate();
   }
   protected void onCreate() {}
   protected void onShow() {}
   protected void preShow() {
      if (root == null) {
         throw new IllegalStateException("IllegalStateException preShow.");
      }  
      onShow();
      if (background == null) {
         window.setBackgroundDrawable(new BitmapDrawable());
      } else {
         window.setBackgroundDrawable(background);
      }
      window.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
      window.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
      window.setTouchable(true);
      window.setFocusable(true);
      window.setOutsideTouchable(true);
      window.setContentView(root);
   }
   public void setBackgroundDrawable(Drawable background) {
       this.background = background;
   }
   public void setContentView(View root) {
      this.root = root;
      window.setContentView(root);
   }
   public void setContentView(int layoutResID) {
      LayoutInflater inflator = (LayoutInflater) anchor.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      setContentView(inflator.inflate(layoutResID, null));
   }
   public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
      window.setOnDismissListener(listener);
   }
   public void dismiss() {
      window.dismiss();  // popupview를 닫는다.
   }

}

 

생성된 popupview를 상속받아서 동작할 class를 만든다.

 

public class Pop extends PopView{

   private final Context context;
   private final LayoutInflater inflater;
   private final View root;
   private ViewGroup mTrack;
 
   public Pop(View anchor) {
      super(anchor);  
      context  = anchor.getContext();
      inflater  = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      root  = (ViewGroup) inflater.inflate(R.layout.popview, null);
      setContentView(root);
      mTrack    = (ViewGroup) root.findViewById(R.id.viewRow); //팝업 View의 내용을 추가한 LinearLayout
   }

   public void show () {
      preShow(); //상속받은 PopView의 메서드

      int[] location   = new int[2];
      anchor.getLocationOnScreen(location);

      root.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
      root.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

      window.showAtLocation(this.anchor, Gravity.CENTER, 0, 0); //가운데 정렬 하여 보임
   }

}

 

이제 이렇게 되면 popview의 준비는 끝이네요~^^ 이제 xml의 구성을 보면 되겠죠.

먼저 popview가 되어질 xml입니다. 팝업되는 화면이죠.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="wrap_content" android:layout_height="wrap_content">
   <ScrollView //임의적으로 view를 뿌려주기위해 가로 세로 200dip의 크기 efefef배경색으로 지정
      android:layout_width="200dip" android:layout_height="200dip"
      android:fadingEdgeLength="0dip" android:background="#ffefefef"
      android:scrollbars="none">
      <LinearLayout android:id="@+id/viewRow"
         android:layout_width="wrap_content" android:layout_height="wrap_content"
         android:paddingTop="2dip" android:paddingBottom="2dip"
         android:paddingLeft="10dip" android:paddingRight="10dip"
         android:orientation="vertical" />
   </ScrollView>
</LinearLayout>

 

이제 popview의 준비는 끝났고, 이벤트를 통해서 popview를 띄우도록 하겠습니다.

간단하게 버튼하나를 추가하고버튼을 클릭했을때의 이벤트를 받도록합니다.

@Override
public void onClick(View v) {
   Show(v);
}

Pop pop;
private void Show(View v){
   pop = new Pop(v);  
   pop.show();  //popview를 출력
}

 

이렇게 간단하게 view를 출력하면 간단한 팝업창이 나오게됩니다. 그냥 회색의 네모로 보이지만 xml로 이미지를 넣고 꾸미면 더 좋은 popview가 완성 됩니다.

 

 

다음번에는 이벤트와 텍스트등을 더 넣어 보겠습니다.

 

팝업 뷰 만들기 - 2  텍스트, 이벤트, 이미지 추가하는 방법입니다.


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

모든 뷰에 이벤트 등록하기  (0) 2011.06.29
안드로이드 및 아이폰 해상도 비교  (0) 2011.06.29
android editext textarea처럼 만들기  (0) 2011.06.28
android 퍼미션 종류별 정리  (2) 2011.06.24
루팅폰 확인하기  (0) 2011.06.23

This is way i used and its working fine too. Hope, this would help somebody.

<EditText android:id="@+id/EditText02" 
android:layout_width="wrap_content" android:layout_height="wrap_content" 
android:lines="5" android:gravity="top|left" android:inputType="textMultiLine" 
android:scrollHorizontally="false"  
android:minWidth="10.0dip" 
android:maxWidth="5.0dip"/> 

Thanks.

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

안드로이드 및 아이폰 해상도 비교  (0) 2011.06.29
안드로이드 팝업윈도우 만들기  (1) 2011.06.28
android 퍼미션 종류별 정리  (2) 2011.06.24
루팅폰 확인하기  (0) 2011.06.23
camera intent 관련 flag 팁  (0) 2011.06.21


안드로이드 작업을 하다보면 메니페스트 파일에 넣어야하는 User-permission 을 잊거나 생각이

안날경우가 있따. 그래서 일단 적어 놓는다.

</application> 밑에 추가할것 ㅋ

 

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

//위치정보 확인함
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>

//위치정보 확인함

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

//wifi 연결을 확인함
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>

//wifi 체인지를 확인함

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

//네트웍이 연결된것을 확인할수 있게함

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

//부팅완료를 확인할수있게함

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

// 인터넷을 사용함

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

// 외장메모리 사용

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

//녹음이 가능하게 함

 

ACCESS_CHECKIN_PROPERTIES      체크인데이터베이스의_속성테이블로_액세스
ACCESS_COARSE_LOCATION         코스_로케이션_액세스_(Cell-ID/WiFi)
ACCESS_FINE_LOCATION           파인로케이션_액세스(GPS)         
ACCESS_LOCATION_EXTRA_COMMANDS 로케이션_옵션_커맨드_액세스      
ACCESS_MOCK_LOCATION           목_로케이션_프로바이더_생성_(테스트용)
ACCESS_NETWORK_STATE           네트워크_상태_접근               
ACCESS_SURFACE_FLINGER         서피스_플링거_접근               
ACCESS_WIFI_STATE              WiFi상태_접근                    
ADD_SYSTEM_SERVICE             시스템서비스_추가                
BATTERY_STATS                  배터리_상태                      
BLUETOOTH                      블루투스                         
BLUETOOTH_ADMIN                블루투스_어드민                  
BRICK                          디바이스_실효성_지정             
BROADCAST_PACKAGE_REMOVED      제거된_패키지에_대한_notification_브로드캐스트
BROADCAST_SMS                  SMS에_대한_브로드캐스트          
BROADCAST_STICKY               인텐트_브로드캐스트              
CALL_PHONE                     통화                             
CALL_PRIVILEGED                통화(긴급전화_포함)              
CAMERA                         카메라                           
CHANGE_COMPONENT_ENABLED_STATE 컴포넌트의_실효성_변경           
CHANGE_CONFIGURATION           컨피그_변경                      
CHANGE_NETWORK_STATE           통신상태_변경                    
CHANGE_WIFI_STATE              WiFi상태_변경                    
CLEAR_APP_CACHE                어플리케이션_캐시_클리어         
CLEAR_APP_USER_DATA            어플리케이션의_유저데이터_클리어 
CONTROL_LOCATION_UPDATES       위치정보_갱신                    
DELETE_CACHE_FILES             캐시파일_제거                    
DELETE_PACKAGES                패키지_제거                      
DEVICE_POWER                   전원상태에_대한_로우레벨_접근    
DIAGNOSTIC                     진단리소스_읽고쓰기              
DISABLE_KEYGUARD               키_가드_끄기_DUMP_덤?           
EXPAND_STATUS_BAR              상태표시줄_확장                  
FACTORY_TEST                   팩토리_테스트                    
FLASHLIGHT                     플래시라이트                     
FORCE_BACK                     포스백                           
GET_ACCOUNTS                   어카운트_획득                    
GET_PACKAGE_SIZE               패키지_획득                      
GET_TASKS                      태스크_획득                      
HARDWARE_TEST                  하드웨어테스트                   
INJECT_EVENTS                  유저이벤트_키/트랙볼             
INSTALL_PACKAGES               패키지_인스톨                    
INTERNAL_SYSTEM_WINDOW         내부_시스템윈도_활용             
INTERNET                       인터넷                           
MANAGE_APP_TOKENS              어플리케이션_토큰관리            
MASTER_CLEAR                   마스터_클리어                    
MODIFY_AUDIO_SETTINGS          오디오설정_편집                  
MODIFY_PHONE_STATE             전화상태_편집                    
MOUNT_UNMOUNT_FILESYSTEMS      파일시스템_편집                  
PERSISTENT_ACTIVITY            액티비티_지속                    
PROCESS_OUTGOING_CALLS         전화_발신처리_접근               
READ_CALENDAR                  캘린더_읽어오기                  
READ_CONTACTS                  주소록_읽어오기                  
READ_FRAME_BUFFER              프레임버퍼_읽어오기              
READ_INPUT_STATE               입력상태_읽어오기                
READ_LOGS                      로그_읽어오기                    
READ_OWNER_DATA                owner_data읽어오기               
READ_PHONE_STATE               통화상태_읽어오기_READ_SMS_SMS읽어오기
READ_SYNC_SETTINGS             동기설정_읽어오기                
READ_SYNC_STATS                동기상태_읽어오기                
REBOOT                         reboot                           
RECEIVE_BOOT_COMPLETED         boot완료                         
RECEIVE_MMS                    MMS수신                          
RECEIVE_SMS                    SMS수신                          
RECEIVE_WAP_PUSH               WAP수신                          
RECORD_AUDIO                   오디오_수신                      
REORDER_TASKS                  태스크_Z오더                     
RESTART_PACKAGES               패키지_리스타트                  
SEND_SMS                       SMS송신                          
SET_ACTIVITY_WATCHER           액티비티_왓쳐지정                
SET_ALWAYS_FINISH              액티비티_전체_종료               
SET_ANIMATION_SCALE            스케일_애니메이션_지정           
SET_DEBUG_APP                  디버그어플리케이션_지정          
SET_ORIENTATION                스크린_로테이션지정              
SET_PREFERRED_APPLICATIONS     자주_사용하는_어플리케이션_지정  
SET_PROCESS_FOREGROUND         포어그라운드_처리지정            
SET_PROCESS_LIMIT              제한처리_지정                    
SET_TIME_ZONE                  타임존_지정                      
SET_WALLPAPER                  배경화면_지정                    
SET_WALLPAPER_HINTS            배경화면_힌트_지정               
SIGNAL_PERSISTENT_PROCESSES    지속처리_시그널_지정             
STATUS_BAR                     상태표시줄_지정                  
SUBSCRIBED_FEEDS_READ          서브스트립드_피즈_읽어오기       
SUBSCRIBED_FEEDS_WRITE         서브스트립드_피즈_쓰기           
SYSTEM_ALERT_WINDOW            알림_윈도우                      
VIBRATE                        진동                             
WAKE_LOCK                      알람                             
WRITE_APN_SETTINGS             APN설정_쓰기                     
WRITE_CALENDAR                 캘린더_쓰기                      
WRITE_CONTACTS                 주소록_쓰기                      
WRITE_GSERVICES                G서비스_쓰기                     
WRITE_OWNER_DATA               owner_data쓰기                   
WRITE_SETTINGS                 설정_쓰기
WRITE_SMS                      SMS쓰기
WRITE_SYNC_SETTINGS            동기설정_쓰기

 

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

안드로이드 팝업윈도우 만들기  (1) 2011.06.28
android editext textarea처럼 만들기  (0) 2011.06.28
루팅폰 확인하기  (0) 2011.06.23
camera intent 관련 flag 팁  (0) 2011.06.21
android Badge 참고사항(카운트)  (0) 2011.06.20
 루팅폰에 system/sd 폴더는 퍼미션이 r/w로 되어있다는 것에 착안 거기에 파일을 생성하고 생성이 제대로 되면 루팅폰이라는걸 판단하는 코드

Process p;   
try {   
   // Preform su to get root privledges   
   p = Runtime.getRuntime().exec("su");    
  
   // Attempt to write a file to a root-only   
   DataOutputStream os = new DataOutputStream(p.getOutputStream());   
   os.writeBytes("echo \"Do I have root?\" >/system/sd/temporary.txt\n");   
  
   // Close the terminal   
   os.writeBytes("exit\n");   
   os.flush();   
   try {   
      p.waitFor();   
           if (p.exitValue() != 255) {   
              // TODO Code to run on success   
              toastMessage("root");   
           }   
           else {   
               // TODO Code to run on unsuccessful   
               toastMessage("not root");   
           }   
   } catch (InterruptedException e) {   
      // TODO Code to run in interrupted exception   
       toastMessage("not root");   
   }   
} catch (IOException e) {   
   // TODO Code to run in input/output exception   
    toastMessage("not root");   
} 
private Uri imageUri;

public void takePhoto(View view) {
   
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
   
File photo = new File(Environment.getExternalStorageDirectory(),  "Pic.jpg");
    intent
.putExtra(MediaStore.EXTRA_OUTPUT,
           
Uri.fromFile(photo));
    imageUri
= Uri.fromFile(photo);
    startActivityForResult
(intent, TAKE_PICTURE);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
   
super.onActivityResult(requestCode, resultCode, data);
   
switch (requestCode) {
   
case TAKE_PICTURE:
       
if (resultCode == Activity.RESULT_OK) {
           
Uri selectedImage = imageUri;
            getContentResolver
().notifyChange(selectedImage, null);
           
ImageView imageView = (ImageView) findViewById(R.id.ImageView);
           
ContentResolver cr = getContentResolver();
           
Bitmap bitmap;
           
try {
                 bitmap
= android.provider.MediaStore.Images.Media
                 
.getBitmap(cr, selectedImage);

                imageView
.setImageBitmap(bitmap);
               
Toast.makeText(this, selectedImage.toString(),
                       
Toast.LENGTH_LONG).show();
           
} catch (Exception e) {
               
Toast.makeText(this, "Failed to load", Toast.LENGTH_SHORT)
                       
.show();
               
Log.e("Camera", e.toString());
           
}
       
}
   
}
}

Look at my answer here for widgets/app.

You can use shapecount.xml

res/layout/tabicon.xml

<RelativeLayout
   
android:orientation="vertical"
   
android:background="@null"
   
android:id="@+id/rlayout"
   
android:layout_width="wrap_content"
   
android:layout_height="wrap_content" >
    <ImageView
            android:id="@+id/icon"
            android:src="@android:drawable/ic_menu_mylocation"
<!-- Just for test -->
            android:layout_margin="0dp"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"/>

   
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
       
android:text="50" android:textSize="12dp" android:textStyle="bold"  
       
android:background="@drawable/shapecount"
       
android:textColor="#FFFFFF"
       
android:paddingLeft="3dp" android:paddingRight="3dp"
           
android:layout_margin="0dp"
       
android:layout_alignBottom="@+id/rlayout"
       
android:id="@+id/txtCount" />

</RelativeLayout>

When creating your tab:

LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.tabicon, null);
final TextView txtCount = (TextView) view.findViewById(R.id.txtCount);

spec
= tabHost.newTabSpec("artists").setIndicator(view).setContent(intent);

Now you can use txtCount to update the number in the tab's icon. See the post I pointed out above for an example of TimerTask to test this.

You will have to arrange the layout as you wish also

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

루팅폰 확인하기  (0) 2011.06.23
camera intent 관련 flag 팁  (0) 2011.06.21
[팁] Android에서 SAX를 이용한 Parsing 방법  (0) 2011.06.20
[android]listView 항목 드래그앤드랍 하기  (0) 2011.06.20
stateListDrawable  (0) 2011.06.17


출처 : http://blog.naver.com/kippee?Redirect=Log&logNo=130058382142
일반적으로 Java에서 하는 방법과 동일하게 SAX 에서 제시하는 절차를 적용하면 된다

 



실제 Android에서 적용한 사례를 보면
해당 Site(http://www.google.com/base/feeds) 에 질의할 Query를 조립하고

  public ReviewFetcher(String loc, String description, String rat, int start, int numResults) {

        Log.v(Constants.LOGTAG, " " + ReviewFetcher.CLASSTAG + " location = " + loc + " rating = " + rat + " start = "
            + start + " numResults = " + numResults);

        this.start = start;
        this.numResults = numResults;
        String location = loc;  ⓐ  전의 GUI 에서 입력한 지역 (Location) 값
        String rating = rat;  ⓑ 전의 GUI에서 입력한 음식점 등급

        // urlencode params 물론 질의를 위하여 Encoding 필요
        try {
            if (location != null) {
                location = URLEncoder.encode(location, "UTF-8");
            }
            if (rating != null) {
                rating = URLEncoder.encode(rating, "UTF-8");
            }
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }

        // build query
        this.query = ReviewFetcher.QBASE;
        if ((rating != null) && !rating.equals("ALL")) {
            this.query += (ReviewFetcher.QR_PREFIX + rating + ReviewFetcher.QR_SUFFIX);
        }
        if ((location != null) && !location.equals("")) {
            this.query += (ReviewFetcher.QL_PREFIX + location + ReviewFetcher.QL_SUFFIX);
        }
        if ((description != null) && !description.equals("ANY")) {
            this.query += (ReviewFetcher.QD_PREFIX + description + ReviewFetcher.QD_SUFFIX);
        }
        this.query += (ReviewFetcher.QSTART_INDEX + this.start + ReviewFetcher.QMAX_RESULTS + this.numResults);

        Log.v(Constants.LOGTAG, " " + ReviewFetcher.CLASSTAG + " query - " + this.query);
    }



실제 Site에 질의하여 Parsing 한후 결과를 Array List에 집어 넣는 부분
 

  public ArrayList<Review> getReviews() {
        long startTime = System.currentTimeMillis();
        ArrayList<Review> results = null;

        try {
            URL url = new URL(this.query);
            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser sp = spf.newSAXParser();
            XMLReader xr = sp.getXMLReader();

            ReviewHandler handler = new ReviewHandler(); ⓐ
            xr.setContentHandler(handler);

            xr.parse(new InputSource(url.openStream()));
            // after parsed, get record
            results = handler.getReviews();
        } catch (Exception e) {
            Log.e(Constants.LOGTAG, " " + ReviewFetcher.CLASSTAG, e);
        }
        long duration = System.currentTimeMillis() - startTime;
        Log.v(Constants.LOGTAG, " " + ReviewFetcher.CLASSTAG + " call and parse duration - " + duration);
        return results;
    }

}


그리고 실제 SAX 이벤트를 처리하는 Class

package com.msi.manning.restaurant.data;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;

/**
 * SAX Handler impl for Google Base API and restaurant Review bean.
 *
 * @author charliecollins
 */
public class ReviewHandler extends DefaultHandler {

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");

    private static final String ENTRY = "entry";

    private static final String R_AUTHOR = "review_author";

    private static final String R_CONTENT = "content";

    private static final String R_DATE = "review_date";

    private static final String R_IMAGE_LINK = "image_link";

    private static final String R_LINK = "link";

    private static final String R_LOCATION = "location";

    private static final String R_NAME = "name_of_item_reviewed";

    private static final String R_PHONE = "phone_of_item_reviewed";

    private static final String R_RATING = "rating";

    private boolean authorChars;
    private boolean contentChars;
    private boolean dateChars;
    private boolean imageLinkChars;
    private boolean locationChars;
    private boolean nameChars;
    private int numEntries;
    private boolean phoneChars;
    private boolean ratingChars;

생략


첨부문서는 Unblocking Android에 붙어있는 Code이다

☞ 2009.7.22 추가

첨부파일을 집에 와서 Import하여 새로운 Project를 만들려고 하는 데

Eclipse Console 창에 이런 메시지가 뜬다





[2009-07-22 21:32:03 - uad-RestaurantFinder] no classfiles specified

[2009-07-22 21:32:03 - uad-RestaurantFinder] Conversion to Dalvik format failed with error 1

 





 날보고 어찌 하라고.

하여튼 2시간에 걸쳐 삽질하다가 풀긴 풀었다.

해결하는 방법은 다음 블로그에...

출처 : http://old.wikidocs.net/mybook/6049
listView의 내용을 드래그 앤 드롭으로 순서를 변경하고 싶을 때 사용한다.

 

안드로이드 폰의 "Music" 앱의 playlist를 보면 아이템 드래그 앤 드롭이 가능하다는 것을 확인 할 수 있다. 다만 안드로이드 기본 API로 제공되지 않기 때문에 직접 구현해야만 한다. 드래그 앤 드롭이 적용된 ListView예제는 안드로이드 기본 앱인 Music에서도 확인 가능하며 필자가 만든 "마이투두" 앱에서도 확인이 가능하다.

 

다음은 안드로이드의 Music 앱의 소스를 참고하여 만든 DndListView이다.

아래의 코드를 사용하기 위해서는 안드로이드 버전 1.5 이상부터 가능하다.

 

  1. /*
     * Copyright (C) 2008 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 org.pyframe.tools.view;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.PixelFormat;
    import android.graphics.Rect;
    import android.util.AttributeSet;
    import android.view.GestureDetector;
    import android.view.Gravity;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewConfiguration;
    import android.view.ViewGroup;
    import android.view.WindowManager;
    import android.widget.AdapterView;
    import android.widget.ImageView;
    import android.widget.ListView;
    
    import com.mytodo.andriod.R;
    
    public class DndListView extends ListView {
        
    	private Context mContext;
        private ImageView mDragView;
        private WindowManager mWindowManager;
        private WindowManager.LayoutParams mWindowParams;
        private int mDragPos;      // which item is being dragged
        private int mFirstDragPos; // where was the dragged item originally
        private int mDragPoint;    // at what offset inside the item did the user grab it
        private int mCoordOffset;  // the difference between screen coordinates and coordinates in this view
        private DragListener mDragListener;
        private DropListener mDropListener;
    //    private RemoveListener mRemoveListener;
        private int mUpperBound;
        private int mLowerBound;
        private int mHeight;
        private GestureDetector mGestureDetector;
    //    private static final int FLING = 0;
    //    private static final int SLIDE = 1;
    //    private int mRemoveMode = -1;
        private Rect mTempRect = new Rect();
        private Bitmap mDragBitmap;
        private final int mTouchSlop;
        private int mItemHeightNormal;
        private int mItemHeightExpanded;
    
        public DndListView(Context context, AttributeSet attrs) {
            super(context, attrs);
    //        SharedPreferences pref = context.getSharedPreferences("Music", 3);
    //        mRemoveMode = pref.getInt("deletemode", -1);
            mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
            mContext = context;
    //        Resources res = getResources();
    //        mItemHeightNormal = res.getDimensionPixelSize(R.dimen.normal_height);
    //        mItemHeightExpanded = res.getDimensionPixelSize(R.dimen.expanded_height);
        }
        
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            if (mDragListener != null || mDropListener != null) {
                switch (ev.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        int x = (int) ev.getX();
                        int y = (int) ev.getY();
                        int itemnum = pointToPosition(x, y);
                        if (itemnum == AdapterView.INVALID_POSITION) {
                            break;
                        }
                        ViewGroup item = (ViewGroup) getChildAt(itemnum - getFirstVisiblePosition());
                        mDragPoint = y - item.getTop();
                        mCoordOffset = ((int)ev.getRawY()) - y;
                        View dragger = item.findViewById(R.id.dragicon);
                        
    //                    item.setBackgroundColor(Color.RED);
                        
                        Rect r = mTempRect;
                        dragger.getDrawingRect(r);
                        // The dragger icon itself is quite small, so pretend the touch area is bigger
                        if (x < r.right * 2) {
                            item.setDrawingCacheEnabled(true);
                            // Create a copy of the drawing cache so that it does not get recycled
                            // by the framework when the list tries to clean up memory
                            Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
                            startDragging(bitmap, y);
                            mDragPos = itemnum;
                            mFirstDragPos = mDragPos;
                            mHeight = getHeight();
                            int touchSlop = mTouchSlop;
                            mUpperBound = Math.min(y - touchSlop, mHeight / 3);
                            mLowerBound = Math.max(y + touchSlop, mHeight * 2 /3);
                            return false;
                        }
                        mDragView = null;
                        break;
                }
            }
            return super.onInterceptTouchEvent(ev);
        }
        
        /*
         * pointToPosition() doesn't consider invisible views, but we
         * need to, so implement a slightly different version.
         */
        private int myPointToPosition(int x, int y) {
            Rect frame = mTempRect;
            final int count = getChildCount();
            for (int i = count - 1; i >= 0; i--) {
                final View child = getChildAt(i);
                child.getHitRect(frame);
                if (frame.contains(x, y)) {
                    return getFirstVisiblePosition() + i;
                }
            }
            return INVALID_POSITION;
        }
        
        private int getItemForPosition(int y) {
            int adjustedy = y - mDragPoint - 32;
            int pos = myPointToPosition(0, adjustedy);
            if (pos >= 0) {
                if (pos <= mFirstDragPos) {
                    pos += 1;
                }
            } else if (adjustedy < 0) {
                pos = 0;
            }
            return pos;
        }
        
        private void adjustScrollBounds(int y) {
            if (y >= mHeight / 3) {
                mUpperBound = mHeight / 3;
            }
            if (y <= mHeight * 2 / 3) {
                mLowerBound = mHeight * 2 / 3;
            }
        }
    
        /*
         * Restore size and visibility for all listitems
         */
        private void unExpandViews(boolean deletion) {
            for (int i = 0;; i++) {
                View v = getChildAt(i);
                if (v == null) {
                    if (deletion) {
                        // HACK force update of mItemCount
                        int position = getFirstVisiblePosition();
                        int y = getChildAt(0).getTop();
                        setAdapter(getAdapter());
                        setSelectionFromTop(position, y);
                        // end hack
                    }
                    layoutChildren(); // force children to be recreated where needed
                    v = getChildAt(i);
                    if (v == null) {
                        break;
                    }
                }
                ViewGroup.LayoutParams params = v.getLayoutParams();
                params.height = mItemHeightNormal;
                v.setLayoutParams(params);
                v.setVisibility(View.VISIBLE);
            }
        }
        
        /* Adjust visibility and size to make it appear as though
         * an item is being dragged around and other items are making
         * room for it:
         * If dropping the item would result in it still being in the
         * same place, then make the dragged listitem's size normal,
         * but make the item invisible.
         * Otherwise, if the dragged listitem is still on screen, make
         * it as small as possible and expand the item below the insert
         * point.
         * If the dragged item is not on screen, only expand the item
         * below the current insertpoint.
         */
        private void doExpansion() {
            int childnum = mDragPos - getFirstVisiblePosition();
            if (mDragPos > mFirstDragPos) {
                childnum++;
            }
    
            View first = getChildAt(mFirstDragPos - getFirstVisiblePosition());
    
            for (int i = 0;; i++) {
                View vv = getChildAt(i);
                if (vv == null) {
                    break;
                }
                int height = mItemHeightNormal;
                int visibility = View.VISIBLE;
                if (vv.equals(first)) {
                    // processing the item that is being dragged
                    if (mDragPos == mFirstDragPos) {
                        // hovering over the original location
                        visibility = View.INVISIBLE;
                    } else {
                        // not hovering over it
                        height = 1;
                    }
                } else if (i == childnum) {
                    if (mDragPos < getCount() - 1) {
                        height = mItemHeightExpanded;
                    }
                }
                ViewGroup.LayoutParams params = vv.getLayoutParams();
                params.height = height;
                vv.setLayoutParams(params);
                vv.setVisibility(visibility);
            }
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            if (mGestureDetector != null) {
                mGestureDetector.onTouchEvent(ev);
            }
            if ((mDragListener != null || mDropListener != null) && mDragView != null) {
                int action = ev.getAction(); 
                switch (action) {
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_CANCEL:
                        Rect r = mTempRect;
                        mDragView.getDrawingRect(r);
                        stopDragging();
    //                    if (mRemoveMode == SLIDE && ev.getX() > r.right * 3 / 4) {
    //                        if (mRemoveListener != null) {
    //                            mRemoveListener.remove(mFirstDragPos);
    //                        }
    //                        unExpandViews(true);
    //                    } else {
                        if (mDropListener != null && mDragPos >= 0 && mDragPos < getCount()) {
                            mDropListener.drop(mFirstDragPos, mDragPos);
                        }
                        unExpandViews(false);
    //                    }
                        break;
                        
                    case MotionEvent.ACTION_DOWN:
                    case MotionEvent.ACTION_MOVE:
                        int x = (int) ev.getX();
                        int y = (int) ev.getY();
                        dragView(x, y);
                        int itemnum = getItemForPosition(y);
                        if (itemnum >= 0) {
                            if (action == MotionEvent.ACTION_DOWN || itemnum != mDragPos) {
                                if (mDragListener != null) {
                                    mDragListener.drag(mDragPos, itemnum);
                                }
                                mDragPos = itemnum;
                                doExpansion();
                            }
                            int speed = 0;
                            adjustScrollBounds(y);
                            if (y > mLowerBound) {
                                // scroll the list up a bit
                                speed = y > (mHeight + mLowerBound) / 2 ? 16 : 4;
                            } else if (y < mUpperBound) {
                                // scroll the list down a bit
                                speed = y < mUpperBound / 2 ? -16 : -4;
                            }
                            if (speed != 0) {
                                int ref = pointToPosition(0, mHeight / 2);
                                if (ref == AdapterView.INVALID_POSITION) {
                                    //we hit a divider or an invisible view, check somewhere else
                                    ref = pointToPosition(0, mHeight / 2 + getDividerHeight() + 64);
                                }
                                View v = getChildAt(ref - getFirstVisiblePosition());
                                if (v!= null) {
                                    int pos = v.getTop();
                                    setSelectionFromTop(ref, pos - speed);
                                }
                            }
                        }
                        break;
                }
                return true;
            }
            return super.onTouchEvent(ev);
        }
        
        private void startDragging(Bitmap bm, int y) {
            stopDragging();
    
            mWindowParams = new WindowManager.LayoutParams();
            mWindowParams.gravity = Gravity.TOP;
            mWindowParams.x = 0;
            mWindowParams.y = y - mDragPoint + mCoordOffset;
    //
            mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
            mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
            mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
            mWindowParams.format = PixelFormat.TRANSLUCENT;
            mWindowParams.windowAnimations = 0;
            
            ImageView v = new ImageView(mContext);
            int backGroundColor = mContext.getResources().getColor(R.color.dragndrop_background);
    //        int backGroundColor = Color.parseColor("#e0103010");
            v.setBackgroundColor(backGroundColor);
            
            v.setImageBitmap(bm);
            mDragBitmap = bm;
    
            mWindowManager = (WindowManager)mContext.getSystemService("window");
            mWindowManager.addView(v, mWindowParams);
            mDragView = v;
        }
        
        private void dragView(int x, int y) {
    //        if (mRemoveMode == SLIDE) {
    //            float alpha = 1.0f;
    //            int width = mDragView.getWidth();
    //            if (x > width / 2) {
    //                alpha = ((float)(width - x)) / (width / 2);
    //            }
    //            mWindowParams.alpha = alpha;
    //        }
            mWindowParams.y = y - mDragPoint + mCoordOffset;
            mWindowManager.updateViewLayout(mDragView, mWindowParams);
        }
        
        private void stopDragging() {
            if (mDragView != null) {
                WindowManager wm = (WindowManager)mContext.getSystemService("window");
                wm.removeView(mDragView);
                mDragView.setImageDrawable(null);
                mDragView = null;
            }
            if (mDragBitmap != null) {
                mDragBitmap.recycle();
                mDragBitmap = null;
            }
        }
        
        public void setDragListener(DragListener l) {
            mDragListener = l;
        }
        
        public void setDropListener(DropListener l) {
            mDropListener = l;
        }
        
    //    public void setRemoveListener(RemoveListener l) {
    //        mRemoveListener = l;
    //    }
    
        public interface DragListener {
            void drag(int from, int to);
        }
        public interface DropListener {
            void drop(int from, int to);
        }
        public interface RemoveListener {
            void remove(int which);
        }
    }

 

위 코드중 정상적인 컴파일을 위해서 살펴보아야 할 부분이 두군데 있다.

 

1. 위 코드에 다음과 같은 부분이 있다.

  1. View dragger = item.findViewById(R.id.dragicon);

 

R.id.dragicon 이 바로 드래그를 할 대상이 되는 View가 된다.

 

2. 또 다음과 같은 코드가 있다.

  1. int backGroundColor = mContext.getResources().getColor(R.color.dragndrop_background);

 

이 부분은 드래그 앤 드롭시 백그라운드 색상을 지정해 주는 부분이다.

 

strings.xml파일에 다음과 같은 항목을 추가해 주어야 한다.

  1. <color name="dragndrop_background">#e0103010</color>

 

 

이제 드래그 앤 드롭을 구현하기 위해서 ListActivity는 어떻게 구현해야 하는지 알아보자.

 

아래와 같이 DragListener와 DropListener를 구현하도록 ListActivity를 만든다.

  1. public class MainActivity extends ListActivity implements DragListener, DropListener {

 

onCreate메써드에서 드래그 앤 드롭을 사용한다는 정보를 입력한다.

  1. listView = (DndListView) findViewById(android.R.id.list);
  2. listView.setDragListener(this);
    listView.setDropListener(this);

 

그리고 다음과 같은 메써드를 구현한다.

  1. public void drag(int from, int to) {
  2.   // 드래그 이벤트가 발생시 구현해야 할 것들을 기술한다.
    }
  3.  
  4. public void drop(int fr, int to) {
  5.   // 드롭 이벤트 발생시 구현해야 할 것들을 기술한다.
  6. }

 

Activity의 레이아웃 파일은 다음과 같이 작성해야 한다.

  1.     <org.pyframe.tools.view.DndListView
            android:id="@android:id/list"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:drawSelectorOnTop="false"
            android:fastScrollEnabled="true" 
            android:cacheColorHint="#00000000"
            android:layout_weight="1.0"
             />
 

cacheColorHint값을 주어야 드래그 앤 드롭시 선택된 아이템의 백그라운드 색상이 표시된다.

StateListDrawable drawables = new StateListDrawable();

             drawables.addState(new int[]{-android.R.attr.state_checked, -android.R.attr.state_focused },  backgroundOff);

             drawables.addState(new int[]{android.R.attr.state_checked },     backgroundOn);

             drawables.addState(new int[]{android.R.attr.state_focused },     backgroundOn);

             setBackgroundDrawable(drawables);

 

StateListDrawable을 어떤 뷰까지 쓸 수 있는지는 모르겠는데 일단 버튼류는 다 되는 듯 하니 참고하세요. (ButtonEx도 이걸로 바꿨음)

SELECT * FROM sqlite_master

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

[android]listView 항목 드래그앤드랍 하기  (0) 2011.06.20
stateListDrawable  (0) 2011.06.17
android change titlebar diynamic  (0) 2011.06.14
Fixed header in a TableLayout  (1) 2011.06.14
WORKING WITH JSON,XML AN ANDROID  (3) 2011.06.10

출처 : http://stackoverflow.com/questions/3438276/change-title-bar-text-in-android

you can Change the Title of each screen (i.e. Activity) by setting their Android:label

   <activity android:name=".Hello_World"
                 
android:label="This is the Hello World Application">
   
</activity>

=> Custom - Title - bar


But if you want to Customize title-bar in your way, i.e. Want to put Image icon and custom-text, then following code is running for me:

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>

titlebar.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 
android:layout_width="400px"
 
android:layout_height="fill_parent"
 
android:orientation="horizontal">

<ImageView android:id="@+id/ImageView01"
           
android:layout_width="57px"
           
android:layout_height="wrap_content"
           
android:background="@drawable/icon1">

</ImageView>

<TextView

 
android:id="@+id/myTitle"
 
android:text="This is my new title"
 
android:layout_width="fill_parent"
 
android:layout_height="fill_parent"
 
android:textColor="@color/titletextcolor"
   
/>
</LinearLayout>

TitleBar.java

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

           
final boolean customTitleSupported = requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);

           setContentView
(R.layout.main);


           
if ( customTitleSupported ) {
               getWindow
().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.titlebar);
               
}

           
final TextView myTitleText = (TextView) findViewById(R.id.myTitle);
           
if ( myTitleText != null ) {
               myTitleText
.setText("NEW TITLE");

               
// user can also set color using "Color" and then "Color value constant"
             
// myTitleText.setBackgroundColor(Color.GREEN);
           
}
   
}
}

strings.xml

The strings.xml file is defined under the values folder.

<?xml version="1.0" encoding="utf-8"?>
<resources>
   
<string name="hello">Hello World, Set_Text_TitleBar!</string>
   
<string name="app_name">Set_Text_TitleBar</string>

   
<color name="titlebackgroundcolor">#3232CD</color>
   
<color name="titletextcolor">#FFFF00</color>

</resources>

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

stateListDrawable  (0) 2011.06.17
Sqlite3 에서 Table 이 있는지 없는지 검색하기  (0) 2011.06.16
Fixed header in a TableLayout  (1) 2011.06.14
WORKING WITH JSON,XML AN ANDROID  (3) 2011.06.10
android HTTP GET,POST Examples  (0) 2011.06.10

Fixed header in a TableLayout

As the previous post this will also cover TableLayout. As with borders, there are no attribute for defining a TableRow as a header row. With header row I mean a row that will not scroll with the rest of the data in the TableLayout (providing you have declared the table to be inside a ScrollView).

Well, there is actually a way to work around this using a separate TableLayout for the header row. If you know that the header columns will be wider than the rest of the data in the table it's pretty easy. Some key notes to make this work:


  • Set all Views in the table to android:layout_weight="1"
  • You must NOT set android:stretchColumns="*" as this will override the weight attribute.
  • You must know which row is widest, and use a copy of this as a "dummy" row.
In the layout xml add the header row:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<TableLayout
  android:id="@+id/header"
  android:layout_width="fill_parent"
  android:layout_marginRight="2dp"
  android:layout_marginLeft="2dp"
  android:layout_height="wrap_content"
  android:background="@android:color/black">
  <TableRow>
   <TextView
    android:textColor="@android:color/black"
    android:layout_margin="1dp"
    android:gravity="center_horizontal"
    android:layout_weight="1"
    android:background="#ffcccccc"
    android:text="Col1" />
   <TextView
    android:textColor="@android:color/black"
    android:gravity="center_horizontal"
    android:layout_weight="1"
    android:layout_margin="1dp"
    android:background="@android:color/white"
    android:text="Col2" />
   <TextView
    android:textColor="@android:color/black"
    android:gravity="center_horizontal"
    android:layout_weight="1"
    android:layout_margin="1dp"
    android:background="@android:color/white"
    android:text="Col3" />
   <TextView
    android:textColor="@android:color/black"
    android:gravity="center_horizontal"
    android:layout_weight="1"
    android:layout_margin="1dp"
    android:background="@android:color/white"
    android:text="Col4" />
   <TextView
    android:textColor="@android:color/black"
    android:gravity="center_horizontal"
    android:layout_weight="1"
    android:layout_margin="1dp"
    android:background="@android:color/white"
    android:text="Col5" />
  </TableRow>
 </TableLayout>
Then add the main table inside a ScrollView containing a copy of the header row as a dummy row with a height set to "0dp" making it invisible:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<ScrollView
  android:id="@+id/table_scroll"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_marginRight="2dp"
  android:layout_marginLeft="2dp">
  <TableLayout
   android:id="@+id/maintable"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:background="@android:color/black">
   <TableRow>
    <TextView
     android:layout_height="0dp"
     android:layout_weight="1"
     android:layout_marginRight="1dp"
     android:layout_marginLeft="1dp"
     android:text="Col1" />
    <TextView
     android:layout_height="0dp"
     android:layout_weight="1"
     android:layout_marginLeft="1dp"
     android:layout_marginRight="1dp"
     android:text="Col2" />
    <TextView
     android:layout_height="0dp"
     android:layout_weight="1"
     android:layout_marginLeft="1dp"
     android:layout_marginRight="1dp"
     android:text="Col3" />
    <TextView
     android:layout_height="0dp"
     android:layout_weight="1"
     android:layout_marginLeft="1dp"
     android:layout_marginRight="1dp"
     android:text="Col4" />
    <TextView
     android:layout_height="0dp"
     android:layout_weight="1"
     android:layout_marginLeft="1dp"
     android:layout_marginRight="1dp"
     android:text="Col5" />
   </TableRow>
  </TableLayout>
 </ScrollView>
This is how it is displayed:


Note that this will only work if you know that the header columns is wider than the rest of the data. If any of the rows in the "main" table is wider than the header row the columns will no longer be aligned. However, there is a way to solve this as well. If you can identify the widest row in the "main" table, then just do the same procedure as above, but this time add this as a dummy row to the header table.

출처 : http://www.ibm.com/developerworks/xml/library/x-andbene1/index.html


Mobile devices and platforms boast more features and functionality with each new release, and often mere months separate significant announcements from the leading mobile vendors. The headlines are mostly about UI features (such as advanced multitouch capabilities and Adobe® Flash® technology) and hardware enhancements (such as processor speed and storage capacity). But the crucial fact remains that content is king. Content—or, more generally, data—is exchanged constantly among applications, servers, mobile devices, and users. Without being able to work with it, smartphones such as Apple's iPhone and Google's Android simply become overpriced and underperforming cell phones.

Frequently used acronyms

  • API: Application Programming Interface
  • DOM: Document Object Model
  • HTML: HyperText Markup Language
  • IDE: Integrated development environment
  • SAX: Simple API for XML
  • SDK: Software Developer Kit
  • UI: User Interface
  • XML: Extensible Markup Language

Consider the phenomenal success of social-networking platforms such as Facebook, LinkedIn, and Twitter. From a pure feature-and-function perspective, these platforms are largely pedestrian. They are popular because members and site visitors derive value from the content published there. And that content is accessed increasingly by mobile devices.

This article demonstrates the use of XML and JSON data-interchange formats on the Android platform. The source of the data for the example application is a status-update feed for a Twitter account. The feed data is available from Twitter in both XML and JSON formats. As you'll see, the programming approach to manipulating the data varies significantly between the two formats.

I suggest you have the Android SDK version 1.5 or later installed along with Eclipse to run the example code that accompanies this article. To learn more about setting up your environment, visit the Android Developers website. It is also helpful, but not required, to have an active Twitter account to follow along with the example. See Resources for relevant links.

I'll begin with a brief look at both data formats, starting with XML. If you're already familiar with XML and JSON, you can safely skip to Application opportunity: Twitter feeds to start working with them on Android.

XML: An old friend

No more hard labor

The value of XML's self-describing nature is clear when you contrast it to the state of the art before XML's general adoption. Then, data-interchange exercises included laborious data-description documents, often written and maintained by hand in a word processor or spreadsheet application. These documents, commonly known as interface specifications, depicted field names, lengths, delimiters, hierarchies, and so on. Users followed practices as they saw fit; the closest thing to a standard was the familiar comma-separated-value (CSV) format. Even CSV files vary a good deal. If you doubt it, try to import one into a spreadsheet program and notice all of the options available.

Virtually anyone who has done programming for the enterprise, web, or mobile markets in recent years has encountered XML. It is just about everywhere you look.

An XML document has a recognizable structure: a series of elements that can optionally contain attributes and child elements. Every valid XML document begins with a declaration on the first line: <?xml version="1.0" encoding="utf-8"?>. What comes after the first line is application-dependent. The beauty of XML is that it is self-describing.

XML schemas

Although XML documents are self-describing, they must follow certain rules and guidelines. This is where the XML schema comes in. It is a document that describes the structure of a particular XML file. Such structures are often verbose and complex. (Arguably, XML's worst contribution to the IT field is the data explosion that took place once the idea of highly descriptive data structures came into fashion, fueled in part by the greatly reduced cost of disk-storage technology in the past decade.)

As these large and complex files became more the norm, the art of working on them manually was often a nonstarter for programmers and analysts. To address this issue, XML editors and validating tools became available to aid in the management of the files and the tasks relating to them, such as documentation and conversion to legacy formats.

In addition to general text data, XML can also be used to store binary data through a special set of tags known as CDATACDATAtags within an XML document can contain any kind of data, including other mark-up text, provided that text does not contain CDATAitself.

It's not uncommon for APIs to leverage this capability by using XML as a structure for performing request/response queries. Often the response data contains an XML structure contained within a CDATA tag. For example, an API call might request a customer record with a last name equaling Mott. When the data is found, it is packaged into an XML structure and placed within the response element, as in Listing 1:


Listing 1. Packaging data into an XML structure and placing it within the response element
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<request>
<query>
<lastname>Mott</lastname>
<maxhits>100</maxhits>
</query>
</request>

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response>
<returncode>200</returncode>
<query>
<lastname>Mott</lastname>
<hits>1</hits>
</query>
<data>
<![CDATA[
<contact>
<firstname>Troy</firstname>
<lastname>Mott</lastname>
<age>not telling</age>
</contact>
]]>
</data>
</response>

XML in the workplace

Today XML is the default, expected data format. Although the same data might be available in other formats too, it is a safe bet to plan on the availability of an XML structure.

Enterprise Resource Planning (ERP) packages use XML heavily for data import and export tasks. Internet news sites often make data available as Really Simple Syndication (RSS) feeds—XML documents with a predefined format that news readers are set up to process. Even word-processing applications such as OpenOffice.org and Microsoft® Office use XML.

Today's Microsoft Office documents are PKZIP-compatible files containing multiple XML documents. Each XML file shares the common declaration in the first line. As you can see in Listing 2, the attributes can be somewhat hard to follow:


Listing 2. Common declaration in the first line of each XML file
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <w:document xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006"
 xmlns:o="urn:schemas-microsoft-com:office:office"
 xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
 xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"
 xmlns:v="urn:schemas-microsoft-com:vml"
 xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
 xmlns:w10="urn:schemas-microsoft-com:office:word"
 xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
 xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml">
 <w:body><w:p w:rsidR="00B6337C" w:rsidRDefault="00663F0E"><w:r>
 <w:t xml:space="preserve">This is a sample </w:t></w:r><w:r
 w:rsidRPr="006906EA"><w:rPr><w:i/></w:rPr><w:t>Microsoft 
 Word document</w:t></w:r><w:r><w:t xml:space="preserve"> used
 to </w:t></w:r><w:r w:rsidRPr="006906EA"><w:rPr><w:b/>
 <w:u w:val="single"/></w:rPr><w:t>demonstrate</w:t></w:r>
 <w:r><w:t xml:space="preserve"> some XML topics.</w:t></w:r>
 </w:p><w:p w:rsidR="00B14B2A" w:rsidRDefault="00B14B2A"/><w:p 
 w:rsidR="00B14B2A"w:rsidRDefault="00B14B2A"><w:r><w:rPr>
 <w:noProof/></w:rPr><w:drawing><wp:inline distT="0" distB="0" 
 distL="0" distR="0"><wp:extent cx="3276600" cy="3838575"/><wp:effectExtent
 l="19050" t="0" r="0" b="0"/><wp:docPr id="1" name="Picture 0"
 descr="frankableson.jpg"/><wp:cNvGraphicFramePr><a:graphicFrameLocks
 xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
 noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic
 xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData
 uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic
 xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
 <pic:nvPicPr><pic:cNvPrid="0"name="frankableson.jpg"/><pic:cNvPicPr/>
 </pic:nvPicPr><pic:blipFill><a:blip r:embed="rId4"
 cstate="print"/><a:stretch><a:fillRect/></a:stretch>
 </pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/>
 <a:ext cx="3276600" cy="3838575"/></a:xfrm><a:prstGeom
 prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic>
 </a:graphicData></a:graphic></wp:inline></w:drawing>
 </w:r></w:p><w:p w:rsidR="00663F0E" w:rsidRDefault="00663F0E"/>
 <w:p w:rsidR="00CC16CE" w:rsidRDefault="00CC16CE"/><w:sectPr 
 w:rsidR="00CC16CE" w:rsidSect="00B6337C"><w:pgSz w:w="12240" w:h="15840"/>
 <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" 
 w:footer="720" w:gutter="0"/><w:cols w:space="720"/><w:docGrid
 w:linePitch="360"/></w:sectPr></w:body></w:document>

XML is self-describing, but that doesn't mean that the tags are necessarily easy to decipher. This cryptic example also demonstrates the use of multiple XML namespaces, which can make XML documents even more challenging to follow without specialized tools.

XML is everywhere, but it can often be a poor choice for an Android programmer, particularly if the data structure has fallen victim to the data explosion often associated with XML structures. A resource-constrained platform such as Android, which is usually working on a cellular data network, cannot store and parse massive amounts of XML data. However, if a particular programming task requires the interchange of both text and binary data, XML can be a solid choice.

Now you'll look at an alternative data-interchange format: JSON.

JSON: New kid on the Net

More and more Internet API providers are offering JSON as a data-format option. JSON made a name for itself in the Ajax (Asynchronous JavaScript and XML) web-programming community. Ajax technology enables web pages to update dynamically by refreshing data in selected pockets rather the entire page. Because less data is transferred—and, more important, because much less data is parsed and drawn to the browser window—an Ajax-enabled application can provide a much better user experience than a traditional web application. In fact, a well-written Ajax application can rival smart- or fat-client applications in user experience.

When an Ajax application exchanges data with a web server, it is often requesting a refresh of some kind of data, but ideally without the formatting. It is generally considered poor practice for a web server to serve up preformatted HTML. Instead, a well-written application should send data content to the browser and apply a Cascading Style Sheets (CSS) file to provide visual effects such as colors and font particulars.

Suppose an application wants to request a contact record for our mythical Mr. Mott. The application has more than one data element to send back to the browser. So how does it get packaged? In the Listing 1 example, you might use a simple request/response structure in XML. This is perfectly adequate; however, it requires that you parse each response from the server, store the data in a structure of some sort (a DOM), and then update the web-page content.

Alternatively, you can simply get some JavaScript back from the server and work directly with it. Here is a sample response from a hypothetical application responding to a query (http://<yourserver/app/searchcontact?Mott) for a man named Mott. The response is a string representation of a JavaScript object—that is, a JSON string (split into two lines here to fit this article's page width):

[{"firstname":"Troy","lastname":"Mott","age":"don't ask!"},{"firstname":"Apple seed",
   "lastname":"Mott's","age":"99"}]

Whereas XML is known for its verbosity, JSON has a reputation for being somewhat hard to read. JSON objects are constructed in a key:value pair format. The object's elements are separated by commas, and each object is contained within curly braces {}. An array of objects is contained within square brackets. This is a common approach to transferring a series of rows from a database to an array of objects in which each array element corresponds to a database row, and each property of the object represents a column of data.

Listing 3 shows an example of using this kind of object within an HTML page. For simplicity, the server communications are not included; instead, the JSON data is provided as a string variable named serverresponse.


Listing 3. Using a JSON object within an HTML page
<html>
<head>
<script language="JavaScript">
var serverresponse = "[{\"firstname\":\"Troy\",\"lastname\":\"Mott\",\"age\":\"don't
ask!\"},{\"firstname\":\"Apple seed\",\"lastname\":\"Mott's\",\"age\":\"99\"}]";
function updatepage()
{
    var contacts = eval(serverresponse );
    var i;
    var s = "Search Results:<br />";
    for (i=0;i<contacts.length;i++)
    {
        s = s + contacts[i].firstname + " " + contacts[i].lastname + "'s age is ... " 
+ contacts[i].age + "<br />";
    }
    document.getElementById("target").innerHTML = s;
}
</script>
</head>
<body>
<button onclick="updatepage();">Search for Mott</button><br />
<span id="target">&nbsp;</span>
</body>
</html>

Note that this example uses the JavaScript function named eval() to turn a string into a JavaScript array. JSON libraries are available to provide faster and more secure methods of performing this step. The approach in Listing 3 is not a best practice. I provide it here to give some context to how a JSON object can be used in an Ajax application: The JSON structure is exchanged, parsed, and manipulated by the client code.

In summary, JSON is:

  • A data-interchange format.
  • A means to encode JavaScript objects as strings.
  • Limited to text and numeric values. Binary values are explicitly not permitted. JSON has no CDATA equivalent.
  • More economical than XML in terms of data size, at the expense of readability.
  • Increasingly available as an option from API providers such as Twitter.

In Listing 3 the client is a web browser running client-side scripting. Returning to this article's focus, next you'll examine the use of XML and JSON in an Android application.

Application opportunity: Twitter feeds

Twitter has become an international force, providing updates on everything from what people are having for breakfast and how their kid's sports team is faring at the ballpark, to serious topics such as street-level updates on political revolts in closed nations or a play-by-play of an organ transplant.

The easiest way to get to get the XML and JSON documents to use with the sample code accompanying this article is through the URL http://twitter.com/statuses/user_timeline/userid.format, where userid is your own Twitter user ID and format is either XML or JSON.

You can also find a link to this page directly on your Twitter page, as in Figure 1. You can see your Twitter user ID there.


Figure 1. Link to feeds page on your Twitter page
Screen capture of fableson Twitter page with a link 'RSS fee of fableson's tweets' in lower right corner 

The complete feed files are very verbose, so the next two listings show only the first entry of the feed (from my own Twitter account). Listing 4 contains the XML snippet:


Listing 4. The XML snippet
<?xml version="1.0" encoding="UTF-8"?>
<statuses type="array">
<status>
  <created_at>Thu Apr 29 05:25:29 +0000 2010</created_at>
  <id>13052369631</id>
  <text>Wrapping up new article on JSON for Android
 programmers...</text>
  <source><a href="http://www.linkedin.com/"rel="nofollow">
   LinkedIn</a></source>
  <truncated>false</truncated>
  <in_reply_to_status_id/>
  <in_reply_to_user_id/>
  <favorited>false</favorited>
  <in_reply_to_screen_name/>
  <user>
    <id>15221439</id>
    <name>fableson</name>
    <screen_name>fableson</screen_name>
    <location>Byram Township, NJ</location>
    <description/>

<profile_image_url>http://a3.twimg.com/profile_images/260492935
/bookcover_normal.jpg</profile_image_url>
    <url>http://msiservices.com</url>
    <protected>false</protected>
    <followers_count>52</followers_count>
    <profile_background_color>9ae4e8
    <profile_text_color>000000</profile_text_color>
    <profile_link_color>0000ff</profile_link_color>
    <profile_sidebar_fill_color>e0ff92
</profile_sidebar_fill_color>
    <profile_sidebar_border_color>87bc44
</profile_sidebar_border_color>
    <friends_count>10</friends_count>
    <created_at>Tue Jun 24 17:04:11 +0000 2008</created_at>
    <favourites_count>0</favourites_count>
    <utc_offset>-18000</utc_offset>
    <time_zone>Eastern Time (US & Canada)</time_zone>

   <profile_background_image_url>http://s.twimg.com/a/1272044617/
images/themes/theme1/bg.png</profile_background_image_url>
   
<profile_background_tile>false</profile_background_tile>
    <notifications>false</notifications>
    <geo_enabled>false</geo_enabled>

    <verified>false</verified>
    <following>false</following>
    <statuses_count>91</statuses_count>
    <lang>en</lang>
    <contributors_enabled>false</contributors_enabled>
  </user>
  <geo/>
  <coordinates/>
  <place/>
  <contributors/>
</status>
</statuses>

Listing 5 shows the same data, this time in JSON format:


Listing 5. Feed data in JSON format
[
{"in_reply_to_status_id":null,
"favorited":false,
"created_at":"Thu Apr 29 05:25:29 +0000 2010",
"in_reply_to_screen_name":null,
"geo":null,
"source":"<a href=\"http://www.linkedin.com/\" rel=\"nofollow\
          ">LinkedIn</a>",
"contributors":null,
"place":null,
"truncated":false,
"coordinates":null,
"user":
{
    "friends_count":10,
    "description":"",
    "lang":"en",
    "statuses_count":91,
    "time_zone":"Eastern Time (US & Canada)",
    "profile_link_color":"0000ff",
    "favourites_count":0,
    "created_at":"Tue Jun 24 17:04:11 +0000 2008",
    "contributors_enabled":false,
    "profile_sidebar_fill_color":"e0ff92",
    "following":null,
    "geo_enabled":false,
    "profile_background_image_url":"http://s.twimg.com/a/1272044617/images/themes
/theme1/bg.png",
    "profile_image_url":"http://a3.twimg.com/profile_images/260492935
/bookcover_normal.jpg",
    "notifications":null,
    "profile_sidebar_border_color":"87bc44",
    "url":"http://msiservices.com",
    "verified":false,
    "profile_background_tile":false,
    "screen_name":"fableson",
    "protected":false,
    "location":"Byram Township, NJ",
    "profile_background_color":"9ae4e8",
    "name":"fableson",
    "followers_count":52,
    "id":15221439,
    "utc_offset":-18000,
    "profile_text_color":"000000"
},
"in_reply_to_user_id":null,
"id":13052369631,
"text":"Wrapping up new article on JSON for Android programmers..."}
]

Notice how much extra data is included in both listings beyond the mere status update. All you need to care about is the date/time when the post was made and the text from the post itself. Next I'll show you the pertinent portions of an Android application that parses this data. The full project is available for download.

The XMLvsJSON application

Real-world real-time updates

Note that the sample application does not pull the data in real time from the web, though that would be the expectation for a real-world application. The data feeds are taken out of the raw resources folder so the application can focus on the parsing aspects. SeeResources for links to information on making network connections with Android.

The Android application is simple. It contains full copies of the XML and JSON data feeds and gives the user the option of parsing either one. Figure 2 shows the structure of the project files within Eclipse. (View a text-only version of Figure 2.)


Figure 2. File structure of the Eclipse project
Screen capture of  the structure of the files within Eclipse 

Figure 3 shows the application's UI prior to the selection of a parsing option:


Figure 3. The application's UI prior to selection of a parsing option
Screen capture of the application's user interface prior to selecting a parsing option 

The application UI shows two buttons, Parse XML and Parse JSON file, followed by default text. Listing 6 contains the layout for this UI, found in the main.xml file in the res/layout folder of the project:


Listing 6. The layout for the UI
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal">

<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
 android:id="@+id/btnXML" android:text="Parse XML"></Button>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
 android:id="@+id/btnJSON" android:text="Parse JSON file"></Button>
</LinearLayout>

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" 
android:id="@+id/ScrollView01" android:layout_width="fill_parent"
android:layout_height="wrap_content">

<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="default text" 
    android:layout_gravity="center_horizontal"
    android:id="@+id/txtData" 
    />


</ScrollView>

</LinearLayout>

The Parse XML and Parse JSON file buttons are defined above a ScrollView, which in turn contains a TextView control. The idea here is that you want the user to be able to scroll through the resulting data.

Note the use of multiple LinearLayout structures. The first is a vertical alignment, and it contains both a LinearLayout with a horizontal structure and a ScrollView. The inner LinearLayout contains the two Button widgets. This layout is inflated and wired up in the onCreate() method, in Listing 7:


Listing 7. The onCreate() method
    Button btnXML;
    Button btnJSON;
    TextView tvData;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);


        tvData = (TextView) findViewById(R.id.txtData);
        btnXML = (Button) findViewById(R.id.btnXML);
        btnXML.setOnClickListener(new Button.OnClickListener()
        {
            public void onClick(View v)
            { 
                examineXMLFile();
            }
        });


        btnJSON = (Button) findViewById(R.id.btnJSON);
        btnJSON.setOnClickListener(new Button.OnClickListener()
        {
            public void onClick(View v)
            {
                examineJSONFile();
            }
        });

    }

The examineXMLFile() method controls the XML parsing.

XML parsing

SAX versus DOM

Android also supports a DOM parser, which requires a larger memory footprint but reduces some of the complexity found in the SAX parser. For an application such as XMLvsJSON, which is only interested in a very small subset of a large data feed, the SAX approach is likely the better tool for the job.

Parsing of XML data is often done with a SAX-style parser. With this style of parser, you set up an InputSource pointing to the source XML data and provide a handler that receives certain events as the document is "walked." Listing 8 shows the examineXMLFile()method, which takes care of the following tasks:

  • Sets up the InputSource with the XML file from the raw resources
  • Creates a SAXParser, associated with a handler namedtwitterFeedHandler ( in Listing 9)
  • Invokes the parser and displays the results in a TextViewwidget identified in the layout file as R.id.txtData and referenced in the code as tvData
  • Shows any errors, also in the TextView

Listing 8. The examineXMLFIle() method
void examineXMLFile()
    {
        try {
            InputSource is = new InputSource(getResources()
.openRawResource(R.raw.xmltwitter));
            // create the factory
            SAXParserFactory factory = SAXParserFactory.newInstance();
            // create a parser
            SAXParser parser = factory.newSAXParser();
            // create the reader (scanner)
            XMLReader xmlreader = parser.getXMLReader();
            // instantiate our handler
            twitterFeedHandler tfh = new twitterFeedHandler();

            // assign our handler
            xmlreader.setContentHandler(tfh);
            // perform the synchronous parse
            xmlreader.parse(is);
            // should be done... let's display our results
            tvData.setText(tfh.getResults());
        }
        catch (Exception e) {
            tvData.setText(e.getMessage());
        }
    }

Whereas examineXMLFile() sets things up, the real parsing work from the perspective of the application takes place in the handler that's implemented in the twitterFeedHandler.java file. This class, which implements the DefaultHandler interface, is inListing 9:


Listing 9. The twitterFeedHandler class
public class twitterFeedHandler extends DefaultHandler {

    StringBuilder sb = null;
    String ret = "";
    boolean bStore = false;
    int howMany = 0;

    twitterFeedHandler() {
    }

    String getResults()
    {
        return "XML parsed data.\nThere are [" + howMany + "] status updates\n\n" + ret;
    }
    @Override

    public void startDocument() throws SAXException {
        // initialize "list"
    }

    @Override
    public void endDocument() throws SAXException {

    }

    @Override
    public void startElement(String namespaceURI, String localName, String qName, 
Attributes atts) throws SAXException {

        try {
            if (localName.equals("status")) {
                this.sb = new StringBuilder("");
                bStore = true;
            }
            if (localName.equals("user")) {
                bStore = false;
            }
            if (localName.equals("text")) {
                this.sb = new StringBuilder("");
            }
            if (localName.equals("created_at")) {
                this.sb = new StringBuilder("");
            }
        } catch (Exception ee) {

            Log.d("error in startElement", ee.getStackTrace().toString());
        }
    }

    @Override

    public void endElement(String namespaceURI, String localName, String qName)
throws SAXException {

        if (bStore) {
            if (localName.equals("created_at")) {

                ret += "Date: " + sb.toString() + "\n"; 
                sb = new StringBuilder("");
                return;

            }

            if (localName.equals("user")) {
                bStore = true;
            }

            if (localName.equals("text")) {

                ret += "Post: " + sb.toString() + "\n\n";
                sb = new StringBuilder("");
                return;

            }


        }
        if (localName.equals("status")) {
            howMany++;
            bStore = false;
        }
    }

    @Override

    public void characters(char ch[], int start, int length) {

        if (bStore) {
            String theString = new String(ch, start, length);

            this.sb.append(theString);
        }
    }

}

Listing 9 contains some noteworthy items. The first thing to consider is that the SAX parser is an event-based parser, which means that you build the actual document as it is parsed. Events are fired whenever the document starts, the document ends, a tag starts, a tag ends, and data is found. This implies that you must define a data structure to keep the data of interest and discard the rest.

Note the StringBuilder and the appending of data, used because a particular data element can be processed across multiple reads on the InputSource. Never assume that all of the data is provided in any given call to the characters() method.

This application collects the data into a simply formatted string. An alternative example might include putting these entries into a collection class or database instead, particularly if a great deal of manipulation were to take place after the parse.

The getResults() method is custom to this class. It's used to gather the assembled representation of this data and provide it to the application. It is not part of the DefaultHandler's interface.

Figure 4 depicts the parsed XML data. (View a text-only version of Figure 4.)


Figure 4. The parsed XML data
Screen capture of the parsed XML data on a mobile phone screen 

Although parsing XML with the SAX parser is nontrivial in terms of building up, managing, and navigating the resulting structure, its main advantages are speed and the opportunity to reduce significantly the amount of RAM required both during and after the parsing step.

Now you'll take a look at Android's approach to parsing JSON data.

JSON parsing

Parsing JSON data in the application starts when the user selects the JSON button. This invokes the examineJSONFile() method, in Listing 10. No additional handler class is required, because all parsing and document management take place within the Android-supplied libraries, and all JSON-related code is contained in this method.


Listing 10. Invoking the examineJSONfile() method
void examineJSONFile()
    {
        try
        {
            String x = "";
            InputStream is = this.getResources().openRawResource(R.raw.jsontwitter);
            byte [] buffer = new byte[is.available()];
            while (is.read(buffer) != -1);
            String jsontext = new String(buffer);
            JSONArray entries = new JSONArray(jsontext);

            x = "JSON parsed.\nThere are [" + entries.length() + "]\n\n";

            int i;
            for (i=0;i<entries.length();i++)
            {
                JSONObject post = entries.getJSONObject(i);
                x += "------------\n";
                x += "Date:" + post.getString("created_at") + "\n";
                x += "Post:" + post.getString("text") + "\n\n";
            }
            tvData.setText(x);
        }
        catch (Exception je)
        {
            tvData.setText("Error w/file: " + je.getMessage());
        }
    }

Like the XML routine shown previously, this code reads in a file from the raw resources. The data is read into memory in its entirety, converted to a java.lang.String, and then parsed into a JSONArray. Note that a particular string can be parsed directly into an array, as in this example, or the string can be parsed into a JSONObject. Because the Twitter data is an array of objects, it makes sense to parse the entire string into an array and then access the object individually by ordinal position.

This method's flow is straightforward; once the data is parsed, the code builds up a string representation that's similar to the approach of the XML parser handler. Of interest here is that the data is managed for you; you don't need to build up additional memory structures to contain the data. Likewise, the application knows up front just how many entries are in the JSONArray (20 in this example).

Although JSON parsing is much more simple programmatically, it is not cost-free. It adds the burden of memory consumption for reading in the entire data stream before it can be worked on, and for storing all of the data. In contrast, the SAX XML approach uses only the data of interest. That caveat aside, if memory for parsing a particular JSON object is ample, this approach can be favorable for many applications, particularly if working with the DOM is of little interest.

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

android change titlebar diynamic  (0) 2011.06.14
Fixed header in a TableLayout  (1) 2011.06.14
android HTTP GET,POST Examples  (0) 2011.06.10
[펌]ViewFlipper 관한 설명(추천)  (0) 2011.06.09
progressbar 배경 및 색깔 바꾸기  (0) 2011.06.09

출처 : http://adsgear.tistory.com/49

HTTP GET
 

try {

        HttpClient client = new DefaultHttpClient();  

        String getURL = "http://www.google.com";

        HttpGet get = new HttpGet(getURL);

        HttpResponse responseGet = client.execute(get);  

        HttpEntity resEntityGet = responseGet.getEntity();  

        if (resEntityGet != null) {  

                    //do something with the response

                    Log.w("RESPONSE",EntityUtils.toString(resEntityGet));

                }

} catch (Exception e) {

    e.printStackTrace();

}


HTTP POST 

try {

        HttpClient client = new DefaultHttpClient();  

        String postURL = "http://somepostaddress.com";

        HttpPost post = new HttpPost(postURL); 

            List params = new ArrayList();

            params.add(new BasicNameValuePair("user", "kris"));

            params.add(new BasicNameValuePair("pass", "xyz"));

            UrlEncodedFormEntity ent = new UrlEncodedFormEntity(params,HTTP.UTF_8);

            post.setEntity(ent);

            HttpResponse responsePOST = client.execute(post);  

            HttpEntity resEntity = responsePOST.getEntity();  

            if (resEntity != null) {    

                Log.w("RESPONSE",EntityUtils.toString(resEntity));

            }

} catch (Exception e) {

        e.printStackTrace();

}


HTTP POST with File attachment 

File file = new File("path/to/your/file.txt");

try {

         HttpClient client = new DefaultHttpClient();  

         String postURL = "http://someposturl.com";

         HttpPost post = new HttpPost(postURL); 

     FileBody bin = new FileBody(file);

     MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);  

     reqEntity.addPart("myFile", bin);

     post.setEntity(reqEntity);  

     HttpResponse response = client.execute(post);  

     HttpEntity resEntity = response.getEntity();  

     if (resEntity != null) {    

               Log.w("RESPONSE",EntityUtils.toString(resEntity));

         }

} catch (Exception e) {

    e.printStackTrace();

}

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

Fixed header in a TableLayout  (1) 2011.06.14
WORKING WITH JSON,XML AN ANDROID  (3) 2011.06.10
[펌]ViewFlipper 관한 설명(추천)  (0) 2011.06.09
progressbar 배경 및 색깔 바꾸기  (0) 2011.06.09
color등 기타팁등  (0) 2011.06.09

 
출처 :  http://tigerwoods.tistory.com/23

 

 


 

 

1. ViewFlipper의 용도

ViewFlipper는 Tab과 마찬가지로 화면에 표시 가능한 여러 view가 준비되어 있을 때 화면에 현재 표시된 view를 다른 view로 전환하기 위해 사용된다.

Tab은 일반적으로 표현하려는 view간의 구분(TabWidget의 이름으로) 이 필요할 때 사용되며, ViewFlipper는 view간의 구분이 필요 없을 경우 많이 사용된다.

 

ViewFlipper의 사용 예로는 안드로이드 폰의 초기화면과 같은 경우가 있다.

예를 들면, 많은 어플리케이션을 인스톨한 유저는 사용하는 모든 어플리케이션에 대한 icon을 하나의 view에 표시할 수 없고, 여러 개의 view를 사용해야만 모든 icon을 표시 할 수 있다. Tab을 사용해서 여러 view를 하나씩 보여줄 수도 있지만, 모든 view는 단순한 icon의 나열임으로 굳이 view 와 view를 이름으로 구분할 필요가 없는 상황에서 Tab이름을 위한 Label을 배치하는 것은 공간의 효율적 사용이 아니다.

 

이럴 때 ViewFlipper를 사용하면 화면 전체를 원하는 view로 채워 넣을 수 있어 공간의 효율적인 사용이 가능하다.

 

하지만, 여러 view가 있는데 그 중 특정 view로 비 순차적으로 건너뛰어야 할 경우 Tab은 해당 Tab을 클릭하면 되지만, ViewFlipper는 원하는 view까지 순차적으로 flip해야 한다는 단점이 있기는 하다. (ViewFlipper도 구현에 따라 특정 view로 건너뛰게 만들 수 있으나, 그렇게 사용하려면 굳이 ViewFlipper를 사용할 이유가 적어지는 것 같다)

 

결국, view간의 전환을 구현하는데 Tab을 사용할지 아니면 ViewFlipper를 사용할 지는 전적으로 개발자의 몫임으로, 사용자의 입장에서 판단해서 사용자에게 최대한 편리한 UI를 제공하는 것이 중요할 것이다.

 

 

 

 

2. ViewFlipper의 구현

다음은 ViewFlipper의 클래스 상속 구조이다.

 

ViewFlipper를 사용했을때 앞/뒤 view로 전환을 위해서는 버튼, trackball, 방향키, 화면 터치, 흔들기 등이 사용되는데, 여기서는 일반 스마트폰의 초기 화면 같은 터치 방식을 이용해 구현해 보려고 한다.

 

먼저 예제를 보기 전에 몇가지 살펴볼 것이 있다.

 

 

 

FrameLayout의 특징

ViewFlipper가 상속하는 FrameLayout은 다른 layout과는 조금 다른 점이 있다.

바로 FrameLayout에서는 child view들이 모두 왼쪽 상단을 기준으로 겹쳐 쌓이도록 디자인 되었다는 것이다.

즉, FrameLayout에 어떤 layout을 적용 하던 child view는 항상 FrameLayout의 좌측 상단 코너를 기준으로 쌓여버린다. (layout_gravity등의 layout 관련 속성이 FrameLayout에서는 작동 안 함)

만약 하나 이상의 child view가 FrameLayout에 할당될 경우 모든 child view는 FreameLayout의 좌측 상단에 쌓이게 되고, 가장 마지막에 쌓인 child view의 크기만큼 뒤에 쌓여있는 다른 child view의 영역을 가려버린다.

 

그림으로 표현하면 다음과 같다. 


위의 그림은 Child View 1, 2, 3 순으로 FrameLayout에 더했을 때 일어 나는 일이다.

FrameLayout에 더해지는 모든 view는 외쪽 상단 코너를 기준점으로 정렬되고 가장 나중에 더해진 Child View 3이 전에 더해진 child view들을 모두 가려 버리는 것을 볼 수 있다.

 

Tab과 ViewFlipper에서는 이렇게 child view를 한곳에 포개어 놓고 보여줄 child view를 가장 상단으로 올리는 방법으로 여러 개의 view를 한 화면에 번갈아 가면서 표시한다.

 

그렇기 때문에 FrameLayout 내부에서 복잡한 구성의 child view를 표현하려면 다음과 같은 구조를 사용해야 한다.

(Tab, ViewFlipper 모두 FrameLayout에서 상속함으로 참고)

FrameLayout은 한번에 한 view만 표현 가능하기 때문에 child view의 layout을 "fill_parent"로

설정하면 FrameLayout을 꽉 채운 child view로 표현 할 수 있다.

 

 

 

OnTouchListener 인터페이스의 구현

화면에 일어나는 touch를 감지하기 위해서는 android.view.View.OnTouchListener interface를 구현하여야 한다.

OnTouchListener 인터페이스는 다음과 같은 abstract callback method를 포함 함으로 구현해 주어야 한다.

abstract boolean onTouch(View v, MotionEvent event)

parameter:

    v - touch가 일어나고 있는 view object

    event - 현재 일어나고 있는 event

return:

     callback이 event를 처리 했으면 true, 그렇지 못했으면 false.

 

 

MotionEvent객체에는 여러 모션을 나타내는 constant가 미리 define되어 있다.

예제에서는 MotionEvent:ACTION_DOWN과 MotionEvent:ACTION_UP을 사용하는데 이는 각각 터치 시작과 터치 끝을 나타낸다.

 

또, MotionEvent객체는 현재 일어나는 이벤트에 대한 좌표 정보를 리턴하는 여러 메소드도 제공하는데

예제에서는 float MotionEvent:GetX()라는 이벤트가 일어나는 x좌표를 float형으로 리턴하는 메소드를 이용해 발생 이벤트를 인식한다.

 

 

 

Animation 리소스의 사용

안드로이드에서는 animation도 resource로 등록이 된다.

 

프로젝트 폴더\res\anim\ 폴더 밑에 xml형식의 문서로 저장되며, java code 에서는 AnimationUtil:loadAnimation(context, int)을 이용해 컴파일된 animation 리소스에 접근한다. ('R.anim.animation_xml_file_이름' 이용)

이 번 예제를 위해서는 다음과 같이 4개의 animation xml 문서가 사용되었다.

  • push_left_in.xml: 새로운 view가 왼쪽 방향으로 밀려 들어옴.
  • push_left_out.xml: 기존 view가 왼쪽 방향으로 밀려 나감.
  • push_right_in.xml: 새로운 view가 오른쪽 방향으로 밀려 들어옴.
  • push_right_out.xml: 기존 view가 오른쪽 방향으로 밀려 나감.

 

 

위의 xml 문서에서 사용된 주요 xml element와 attribute는 다음과 같다. 


<translate>

상하좌우 이동 animation을 지정하며. TranslateAnimation 클래스에 대응한다.


다음과 같은 4가지의 xml attribute에

  • android:fromXDelta: animation시작하는 X 좌표
  • android:toXDelta: 끝나는 X 좌표
  • android:fromYDelta: Starting Y location.
  • android:toYDelta: Ending Y location.


다음과 같은 3가지 종류의 값(value) 중 하나를 사용한다.

  • -100% ~ 100%: 자기자신을 기준으로 한 위치에 대한 비율. 0%가 현재 자기 위치.
  • -100%p ~ 100%p: 부모 컨테이너를 기준으로 한 위치에 대한 비율. 0%p가 현재 자기 위치.
  • float값: 절대 위치를 나타냄.

  


<alpha>

투명함을 조정하며, AlphaAnimation 클래스에 대응한다.


다음과 같은 xml attribute를

  • android:fromAlpha: animation 시작 시 투명도
  • android:toAlpha: animation 완료 시 투명도


다음과 같은 값으로 설정한다.

  • 0.0 ~ 1.0 (0.0이 투명, 1.0이 완전 불 투명)

  


추가로 위의 두 element(translate, alpha)에서 공통으로 사용되는 animation이 지속되는 시간을 나타내는 attribute는 다음과 같다.

  • android:duration: Animnation이 시작돼서 끝나는 시간을 밀리 초(1/1000초) 단위로 설정함.

  




Java code에서 ViewFlipper 객체의 사용

Java code에서 ViewFlipper 객체 생성은 findViewById() 메소드를 사용하던가 새로운 view를 inflate 시킬 수 있다.

예제에서는 layout xml 파일 내부에 선언된 ViewFlipper element와 findViewById() 메소드를 이용해 객체를 생성한다.

예제 코드 내부에서 ViewFlipper객체가 사용할 메소드는 다음과 같다.


  • View:addView(View): layout xml 문서 내부의 ViewFlipper element에 nest된 view 이외에 새로운 view를 동적으로 추가한다.
  • ViewAnimator:setInAnimation(Context, int): 새로운 view가 화면에 진입시의 animation 설정
  • ViewAnimator:setOutAnimation(Context, int): 기존 view가 화면에서 퇴장시의 animation 설정
  • ViewAnimator:showNext(): ViewFlipper에 등록된 view중 현재 view의 다음 view를 화면에 보이게 함
  • ViewAnimator:showPrevious(): ViewFlipper에 등록된 view중 현재 view의 전 view를 화면에 보이게 함
  • ViewFlipper:setFlipInterval(int): 자동 flipping시 적용할 시간간격 (1/1000초 단위 사용)
  • ViewFlipper:startFlipping(): 설정된 시간간격을 적용해 자동 flipping 시작 함 
  • ViewFlipper:stopFlipping(): 자동 flipping을 정지 함

 

  



위에서 설명한 것들을 바탕으로 예제 소스를 분석해 보자.

push_left_in.xml

소스 펼치기

 

push_left_out.xml

소스 펼치기

 

push_right_in.xml

소스 펼치기

 

push_right_out.xml

소스 펼치기

 

main.xml

소스 펼치기

 

MyViewFlipper.java

소스 펼치기

 


다음은 실행 결과이다. 에뮬레이터에서는 마우스 드레그를 통해 터치 이벤트를 test가능하며, 에니메이션이 적용되어 view간의 전환이 부드럽게 이뤄짐을 볼 수 있다. 또, CheckBox를 첵크 하면 2초 간격으로 자동으로 view 가 flip 되는 것을 볼 수 있다.




  


                                                       프로젝트 소스 다운로드


  



 

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

WORKING WITH JSON,XML AN ANDROID  (3) 2011.06.10
android HTTP GET,POST Examples  (0) 2011.06.10
progressbar 배경 및 색깔 바꾸기  (0) 2011.06.09
color등 기타팁등  (0) 2011.06.09
테이블 결합 함수 설명  (0) 2011.06.07
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

<item android:id="@android:id/background">
   
<shape>
       
<corners android:radius="5dip" />
       
<gradient
               
android:startColor="#ff9d9e9d"
               
android:centerColor="#ff5a5d5a"
               
android:centerY="0.75"
               
android:endColor="#ff747674"
               
android:angle="270"
       
/>
   
</shape>
</item>

<item android:id="@android:id/secondaryProgress">
   
<clip>
       
<shape>
           
<corners android:radius="5dip" />
           
<gradient
                   
android:startColor="#80ffd300"
                   
android:centerColor="#80ffb600"
                   
android:centerY="0.75"
                   
android:endColor="#a0ffcb00"
                   
android:angle="270"
           
/>
       
</shape>
   
</clip>
</item>
<item
   
android:id="@android:id/progress"
>
   
<clip>
       
<shape>
           
<corners
               
android:radius="5dip" />
           
<gradient
               
android:startColor="@color/progress_start"
               
android:endColor="@color/progress_end"
               
android:angle="270" />
       
</shape>
   
</clip>
</item>

</layer-list>

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

android HTTP GET,POST Examples  (0) 2011.06.10
[펌]ViewFlipper 관한 설명(추천)  (0) 2011.06.09
color등 기타팁등  (0) 2011.06.09
테이블 결합 함수 설명  (0) 2011.06.07
TableLayout 개념잡기  (0) 2011.06.03

출처 : http://fanpro.springnote.com/pages/5306953

xml에 color설정하는 법

http://developer.android.com/intl/fr/guide/topics/resources/available-resources.html

res/values/colors.xml이라는 파일을 만들고 아래와 같이 지정하면 된다.

 

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <resources>         
  3. <color name="dim">#77000000</color>
  4. <color name="transparent">#00000000</color> 
  5. </resources> 

 

이렇게 지정한 color는 android:textColor="@color/dim" 과 같은 식으로 사용이 가능하다.

 

xml에 color state list 설정하는 법

http://androidappdocs-staging.appspot.com/reference/android/content/res/ColorStateList.html

color를 xml에 지정하기 위해서는 res/color라는 폴더를 만든 후 xml파일을 생성하면 된다.

ex) res/color/font_set_a.xml 아래의 예는 

 

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <selector xmlns:android="http://schemas.android.com/apk/res/android">
  3. <item android:state_focused="true" android:color="#cccccc" />
  4. <item android:state_pressed="true" android:color="#cccccc" />
  5. <item android:state_active="true" android:color="#333333" />
  6. <item android:color="#333333" />
  7. </selector>

 

이렇게 선언한 셀렉터는 "@color/font_set_a"와 같이 사용이 가능하다.

 

스타일 설정

위와 같이 선언한 색상들과 폰트크기등을 합쳐서 스타일을 만들 수가 있는데

res/values/styles.xml파일을 생성 후

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <resources>
  3. <style name="font_set_a">
  4. <item name="android:textColor">@color/font_set_a</item>
  5. <item name="android:textSize">36px</item>
  6. </style>
  7. <style name="font_set_b">
  8. <item name="android:textColor">@color/font_set_a</item>
  9. <item name="android:textSize">24px</item>
  10. </style>
  11. </resources>

위와 같이 묶어서 스타일을 지정할 수 있다. 이렇게 만든 스타일은 style="@style/font_set_a"와 같은식으로 사용가능하다.

 

액티비티의 테마지정(1)

액티비티는 기본적으로 title과 body로 구분되는 불투명한 스타일이다.

이를 변경할 수 있는데 해당 스타일은 아래와 같으며 manifest.xml의 <activity .../> 내에 지정하면 된다.

android:theme="@android:style/Theme.NoTitleBar" 타이틀바 제거

android:theme="@android:style/Theme.NoTitleBar.Fullscreen" 타이틀바 + 인디케이터 제거

android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen" 타이틀바 + 인디케이터 제거 + 투명하게 설정

 

액티비티의 테마를 xml로 지정하기(2)

values/styles.xml에 아래와 같이 속성을 지정

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <resources>
  3.  <style name="WebTheme" parent="android:Theme">
  4.              <item name="android:windowNoTitle">true</item>
  5.              <item name="android:windowFullscreen">true</item>
  6.              <item name="android:windowBackground">@android:color/white</item>
  7. </style>  

 

activity의 속성에 theme를 지정

  1. android:theme="@style/WebTheme"

 


테이블 결합 함수 데이터 베이스

2009/12/03 13:24

복사 http://blog.naver.com/findaday/94643786


1. 내부 조인(INNER JOIN)

 

내부조인 (Inneer Join) 은 단순히 연결만 한 개념이 아니라 결과값이 일치하는 것만 join을 한 것이다. 기준이 되는 테이블과 일치하는 값만 join 하여 return 한다고 생각을 하면 된다.

2. 전체외부조인 (FULL JOIN)

 

오른쪽 외부조인과 왼쪽 외부조인을 합친 개념이다. 왼쪽/오른쪽 테이블의 값이 일치하든 일치 하지 않든 모두 가져오는 조인이다. 다만 Cross join 과 다른 점은 Full join 은 빈 값을  null 로 리턴한다는 것이고 Crosss join 은 양쪽 테이블을 그냥 단순히 연결시켜 준다는 점이다.

3. 왼쪽 외부 조인 (LEFT JOIN)

 

왼쪽 테이블을 기준으로 외부 조인 한다. 오른쪽 테이블에 인치하는 값이 없어도 왼쪽 테이블의 데이터는 모두 가져 온다. 이때 오른쪽에 없는 데이터는 null 로 대치 된다. 외부조인은 여집합(Complement) 의 개념이다.

 

4. 오른쪽 외부 조인(RIGHT JOIN)

 

오른쪽 테이블을 기준으로 외부 조인 한다. 왼쪽 테이블에 일치하는 값이 없어도 오른쪽 테이블의 데이터는 모두 가져 온다. 이때 왼쪽에 없는 데이터는 null 로 대치 된다.


5. 교차조인(CROSS JOIN) 

 

교차 조인 (Cross Join) 은 말 그대로 두 개의 테이블을 그냥 붙였다는 개념이다. 아무런 조건이나 제한 사항 없이 양쪽은 있는 그대로 연결만 했다는 개념으로 생각을 하면 되겠다. 합집합(Union) 이라는 개념으로 생각해도 큰 무리가 없다.

 

 


6. 합집합 세로결합(UNION)


UNION              두 집합에 대해 중복되는 행을 제외한 합집합
UNION ALL        두 집합에 대해 중복되는 행을 포함한 합집합
MINUS             두 집합간의 차집합
INTERSECT       두 집합간의 교집합

 

- UNION은 두 테이블의 결합을 나타내며, 결합시키는 두 테이블의 중복되지 않은 값들을 반환 합니다.

- UNION 연산은 두개의 테이블에 속하면서 중복되는 행을 제외한 행 집합을 결과 집합으로 반환한다. 한가지 주의할 점은 UNION 연산에서는 두개의 테이블에서 중복되는 행을 제외시키기 위하여 내부적으로 정렬 작업이 발생하여 UNION ALL 연산보다 처리 속도가 느려진다는 점이다. UNION 연산의 결과 집합은 오름차순으로 출력되며 테이블의 순서를 바꾸어도 결과는 동일하다. UNION ALL연산은 중복되는 행을 제외하지 않고 모두 출력한다. 따라서 UNION ALL 연산은 정렬 작업이 필요없는 관계로 UNION 연산보다 처리 속도가 빠르다.

- CORR(CORRESPONDING) 옵션은 두 개의 테이블에서 공통 변수를 사용.

- ALL 옵션은 중복 제거 없이 세로 결합.

 


7. 교집합 세로결합(INTERSECT)

 

- INTERSECT는 두 행의 집합중 공통된 행을 반환

- 두개의 테이블에 모두 속하는 행집합을 결과 집합으로 반환한다. INTERSECT연산에서 테이블의 순서를 바꾸어도 결과 집합은 동일.

- CORR(CORRESPONDING) 옵션은 두 개의 테이블에서 공통 변수를 사용.

- ALL 옵션은 중복 제거 없이 세로 결합.


8. 차집합 세로결합(MINUS)

 

- MINUS는 첫번째 SELECT문에 의해 반환되는 행중에서 두번째 SELECT문에 의해 반환되는 행에 존재하지 않는 행들을 반환.

- UNION이나 INTERSCET 연산과 다르게 테이블의 순서를 바꾸면 출력 결과가 달라짐.


9. 외부합집합(OUTER UNION)  

 

세로 결합을 수행시 모든 변수를 출력한다.

OUTER UNION CORR은 DATA STEP의 SET와 같다.


10. 배반 합집합 세로결합(EXCLUSIVE UNION)

 

2개의 테이블 결합시 공통된

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

progressbar 배경 및 색깔 바꾸기  (0) 2011.06.09
color등 기타팁등  (0) 2011.06.09
TableLayout 개념잡기  (0) 2011.06.03
Android GridView의 테두리 제거하는 방법  (0) 2011.06.03
android pixel,dip(dp) 개념잡기  (1) 2011.06.02
http://tigerwoods.tistory.com/12

 



이번에는 위젯들을 화면에 배치 하는데 필요한 container에 관한 내용입니다.

다음과 같이 3부분으로 나누어서 포스팅 하려고 합니다. 


Part 1. LinearLayout 

Part 2. RelativeLayout 

Part 3. TableLayout  + ScrollView 


그중 Part 3. TableLayout에 대한 정리 입니다.

  

 

3. TableLayout (Grid Model)

TableLayout 컨테이너는 HTML과 같이 방식으로 쓰인다.

즉, 위젯을 테이블에 배치하고, 테이블의 행과 열의 간격을 조정하여 위젯의 크기를 결정하는 방식이다.

 

Table 구성하기

TableLayout은 TableRow와 같이 사용된다.

TableLayout은 테이블의 전반적인 사항을 관리하고, TableRow로 테이블에 행을 하나씩 추가하고, 추가한 행에 위젯(들)을 배치하는 한다.

 

TableLayout 예제 (main.xml) *속성 적용 전

01 <?xml version="1.0" encoding="utf-8"?>
02 <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
03     android:layout_width="fill_parent"
04     android:layout_height="fill_parent" >  
05      
06     <!-- Row 0 -->
07     <TableRow>
08         <TextView
09             android:id="@+id/textview1"
10             android:text="Row0 : Column0"
11             android:textColor="#000000"
12             android:background="#FFFFFF" />
13         <TextView
14             android:id="@+id/textview2"
15             android:text="Row0 : Column1"
16             android:textColor="#000000"
17             android:background="#FF0000" />
18         <TextView
19             android:id="@+id/textview3"
20             android:text="Row0 : Column2"
21             android:textColor="#000000"
22             android:background="#FFFFFF" />
23         <TextView
24             android:id="@+id/textview4"
25             android:text="Row0 : Column3"
26             android:textColor="#000000"
27             android:background="#FF0000" />
28     </TableRow>
29      
30     <!-- Row 1 -->
31     <TableRow>
32         <TextView
33             android:id="@+id/textview5"
34             android:text="Row1 : Column0"
35             android:textColor="#000000"
36             android:background="#FFFF00" />
37         <TextView
38             android:id="@+id/textview6"
39             android:text="Row1 : Column1"
40             android:textColor="#000000"
41             android:background="#00FF00" />
42         <TextView
43             android:id="@+id/textview7"
44             android:text="Row1 : Column2"
45             android:textColor="#000000"
46             android:background="#FFFF00" />
47         <TextView
48             android:id="@+id/textview8"
49             android:text="Row1 : Column4"
50             android:textColor="#000000"
51             android:layout_column="4"
52             android:background="#00FF00" />    
53     </TableRow>
54 </TableLayout>

 

 

위의 예제의 경우, 테이블은 2개의 행으로 이루어져 있으며

첫 번째 행과 두 번째 행 모두 4개의 TextView로 이루어져 있다.

위의 XML Layout을 일반적으로 사용하는 테이블로 시각화 시킨다면 다음과 같다.

 

Row1 : Column4 처럼 위젯을 특정 column에 배치하는 TableRow.LayoutParams 의 속성은 다음과 같다.

android:layout_column="배치하려는 column 번호(0-based)"

 

또, 테이블의 전체 column 수는 column수가 가장 많은 row에 따른다.

위의 경우 Row0은 순차적으로 4개의 cell을 소모하여 4개의 column이면 충분히 표현 가능하지만,

Row1은 마지막 위젯이 cell 하나를 건너뛰어 배치되어서 총 5개의cell이 사용되었다.

그럼으로 테이블 전체 column수는 row1의 column수에 따라 5 column 테이블이 된다.

다음은 예제를 실행한 화면이다. cell들의 너비가 넓어 테이블이 화면을 벋어났지만 다음에서 해결한다.

 

  


셀(Cell)의 너비 줄이기(shrink) *참고: 테이블 좌우 폭이 컨테이너 폭보다 넓은 경우에만 효과 있음.

위 실행 화면에서 보면, 테이블의 전체 너비가 컨테이너 너비보다 넓어 컨테이너 밖으로 삐쳐 나간 것을 볼 수 있다.

경우에 따라서는 테이블의 column(들)의 너비를 인위적으로 줄여 테이블 전체가 보이도록 할 필요가 있다.

column(들)의 너비가 화면에 적당하도록 줄이려면 TableLayout의 다음 속성을 사용한다.

android:shrinkColumns="줄이고자 하는 column 번호(0-based) 또는 *"

1~n 개의 column에 적용 가능하며 복수의 column은 쉼표 (,) 로 구분하며 전체 column을 줄이려면 아스테리스크(*)를 사용한다.

또, column의 너비가 줄어들면서 위젯이 표현하는 컨텐트(예. string object)의 너비가 위젯의 view 너비 보다 커서 하나의 라인에 정상적으로 표현할 수 없게 되면 자동으로 줄 바꿈이 일어난다.

Java 코드에서는 setColumnShrinkable 또는 setShrinkAllColumns 메소드를 사용.

다음은 예제에 android:shrinkColumns="*"를 적용한 화면. 모든 cell들이 정상적으로 보인 반면 줄 바꿈이 일어났다.

 

  


셀(Cell) 숨기기(collapse)

필요에 따라서 테이블의 특정 셀이 존재는 하지만 보이지 않게 숨겨야 할 경우도 있다.

android:collapseColumns=" 숨기고자 하는 column 번호(0-based) 또는 *"

역시 TableLayout의 속성이며 1~n 개의 column에 적용가능 하다. 복수의 column은 쉼표 (,) 로 구분하며 전체 column을 숨기려면 아스테리스크(*)를 사용한다.

자바 코드에서는 setColumnCollapsed 메소드를 사용.

다음은 예제에 android:collapseColumns="0, 1"를 적용한 화면이다. column 0, 1이 보이지 않는 것을 확인할 수 있다.

 

  


셀(Cell) 합치기

위젯이 2개이상의 column에 걸쳐 나타나게 할 수 있는데 이를 결정하는 TableRow.LayoutParams 의 속성은 다음과 같다.

android:layout_span="차지하려는 Column 수"

예제 중 Row1 : Column 2 에 위치한 EditText 위젯에 android:layout_span="2" 속성을 적용하면 테이블이 다음과 같아진다.

 

  


셀(Cell) 늘리기(stretch) *참고: 테이블 좌우 폭이 컨테이너 폭보다 좁을 경우에만 효과 있음.

TableLayout 컨테이너에 배치된 위젯의 넓이는 그 위젯이 속한 컬럼에서 가장 넓은 위젯의 크기를 따른다.

하지만 필요에 따라 특정 위젯이 속한 column의 너비를 인위적으로 늘릴 필요가 있다.

이럴경우 TableLayout의 다음 속성을 사용한다. 

android:stretchColumns="늘리려는 column 번호(0-based) 또는 *"

속성에 지정된 column외의 column들이 소비하고 남은 모든 여유공간을 지정된 column(들)이 차지하게 된다.

Java 코드에서는 TableLayout의 setColumnStretchable 이나 setStretchAllColumns 메소드를 이용.

예제에서 column4를 늘리면 다음과 같이 테이블 형태가 변한다. 이전 그림과 비교해 보면 Row1 : Column4 가 화면 끝까지 확장 된 것을 볼 수 있다.

 

  


TableLayout 중간에 TableRow가 아닌 위젯 삽입하기

TableLayout의 중간에 TableRow가 아닌 위젯이 올 수도 있다.

단, 그런 요소의의 경우 다음과 같은 특징을 갖는다.

  • TableLayout은 orientation이 vertical로 지정된LinearLayout 처럼 작동함.
  • android:layout_width속성이 무조건 "fill_parent"로 고정됨.

 

예제의 두 TableRow 사이에 다음과 같은 코드를 삽입 한다면,

TableLayout 내부의 TableRow가 아닌 요소 예제 (main.xml 의 일부)

1 <View
2         android:layout_height="4px"
3         android:background="#FF00FF" />

 

실행 화면은 다음과 같이 된다. Row 0과 Row 1사이의 핑크색 경계선을 볼 수 있다.

 

  다음은 위 실행화면에 대한 소스코드이다. 지금까지 정리한 여러 속성들이 사용된 것을 확인할 수 있다.

TableLayout 예제 코드 (main.xml) *여러 속성 적용 후

01 <?xml version="1.0" encoding="utf-8"?>
02 <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
03     android:layout_width="fill_parent"
04     android:layout_height="fill_parent"
05     android:shrinkColumns="*"
06     android:collapseColumns="0, 1"
07     android:stretchColumns="4" >   
08      
09     <!-- Row 0 -->
10     <TableRow>
11         <TextView
12             android:id="@+id/textview1"
13             android:text="Row0 : Column0"
14             android:textColor="#000000"
15             android:background="#FFFFFF" />
16         <TextView
17             android:id="@+id/textview2"
18             android:text="Row0 : Column1"
19             android:textColor="#000000"
20             android:background="#FF0000" />
21         <TextView
22             android:id="@+id/textview3"
23             android:text="Row0 : Column2"
24             android:textColor="#000000"
25             android:background="#FFFFFF" />
26         <TextView
27             android:id="@+id/textview4"
28             android:text="Row0 : Column3"
29             android:textColor="#000000"
30             android:background="#FF0000" />
31     </TableRow>
32      
33     <!-- TableLayout 내부의 TableRow가 아닌 요소 -->
34     <View
35         android:layout_height="4px"
36         android:background="#FF00FF" />
37      
38     <!-- Row 1 -->
39     <TableRow>
40         <TextView
41             android:id="@+id/textview5"
42             android:text="Row1 : Column0"
43             android:textColor="#000000"
44             android:background="#FFFF00" />
45         <TextView
46             android:id="@+id/textview6"
47             android:text="Row1 : Column1"
48             android:textColor="#000000"
49             android:background="#00FF00" />
50         <TextView
51             android:id="@+id/textview7"
52             android:text="Row1 : Column2"
53             android:textColor="#000000"
54             android:background="#FFFF00"
55             android:layout_span="2" />
56         <TextView
57             android:id="@+id/textview8"
58             android:text="Row1 : Column4"
59             android:textColor="#000000"
60             android:layout_column="4"
61             android:background="#00FF00" />    
62     </TableRow>
63 </TableLayout>

 

 

  



4. Container Scroll 하기

스마트폰의 화면은 크기가 제한적 임으로 스크롤의 사용이 중요하다.

스크롤 구현 방법은 다음과 같이 스크롤 시키고 싶은 container를 ScrollView elememt내부에 nest 시키면 된다.


ScrollView 예제 (main.xml)

01 <?xml version="1.0" encoding="utf-8"?>
02 <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
03     android:layout_height="fill_parent"
04     android:layout_width="fill_parent" >
05      
06     <!-- 어떤 Layout을 사용해도 상관없음 -->
07     <!-- 스크롤 바를 위한 공간 확보용 paddingRight="5px" 주목 -->
08     <LinearLayout
09         android:orientation="vertical"
10         android:layout_width="fill_parent"
11         android:layout_height="fill_parent"
12         android:paddingRight="5px" >
13         <TextView
14         android:layout_width="fill_parent"
15         android:layout_height="300px"
16         android:background="#FF0000"
17         android:text="TextView 1" />
18         <TextView
19         android:layout_width="fill_parent"
20         android:layout_height="300px"
21         android:background="#00FF00"
22         android:text="TextView 2" />
23         <TextView
24         android:layout_width="fill_parent"
25         android:layout_height="300px"
26         android:background="#0000FF"
27         android:text="TextView 3" />
28     </LinearLayout>
29 </ScrollView>

 

다음은 실행 화면이다.


 

완전한 TableLayout API Reference는 다음을 참조.

TableLayout API Reference 링크

TableRow API Reference 링크



 

GridView의 background에 white color를 설정 하면 흰색의 테두리가 나타난다.

이것은 list selector에 padding이 지정되어 있어서 나타나는 문제인데 단순히 Padding 같은 layout조정으로는 해결이 되지 않는다.


해결을 하기위해서는 GridView의 속성 중에서 listSelector를 수정해야 한다.

GridView는 AbsListVIew를 상속받게 되어 있는데 AbsListView에 기본으로 지정되어 있는 listSeletor에 padding이 들어가 있어서 GridView에 여백이 생기게 된다.


GridView에 여백을 없에기 위해서는 GridView에 listSelector속성에 padding을 제거한 selector를 지정하면 됩니다.


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

 android:variablePadding="false">   

</selector>


위의 소스를 list_selector.xml로 저장하여 drawable 폴더에 넣은 후, GridVIew의 listSelector 속성에 위에 XML을 지정하면 테두리 문제는  해결이 된다.


[출처] GrideView 테두리 없애기|작성자 palfuni

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

테이블 결합 함수 설명  (0) 2011.06.07
TableLayout 개념잡기  (0) 2011.06.03
android pixel,dip(dp) 개념잡기  (1) 2011.06.02
키보드 및 화면 변경 이벤트 관련 팁  (0) 2011.06.02
android animation관련 팁  (0) 2011.06.01
안드로이드 디바이스의 경우 크게 3가지의 해상도 범주를 가지고 있는데,
LDPI(저해상도), MDPI(중해상도), HDPI(고해상도)입니다.

Density값은 LDPI가 120, MDPI가 160, HDPI가 240입니다. (각각 인치당 픽셀수를 이야기합니다.)
아래 표 (Table.1)를 참고하시면 결국 현재 디자인 하신 그림 파일의 기준은 HDPI를 기준으로 되어있는 것이고,
LDPI를 위해서는 당연히 120/240 = 1/2, 
MDPI를 위해서는 당연히 160/240 = 3/4로 길이당 픽셀수가 감소하는 셈입니다.

모든 해상도를 지원하시려면 하나의 그림 파일을 HDPI, MDPI, LDPI에 맞춰서 3개로 만들어서 개발자에게 넘기시면 됩니다.

Table 1. Screen sizes and densities of emulator skins included in the Android SDK.


Low density (120), ldpi Medium density (160), mdpi High density (240), hdpi
Small screen QVGA (240x320)

Normal screen WQVGA400 (240x400)
WQVGA432 (240x432)
HVGA (320x480) WVGA800 (480x800)
WVGA854 (480x854)
Large screen
WVGA800* (480x800)
WVGA854* (480x854)

* To emulate this configuration, specify a custom density of 160 when creating an AVD that uses a WVGA800 or WVGA854 skin.

레이아웃용 xml 파일에 기술되어야 할 dip값은 다음과 같이 계산해서 넣으시면 됩니다.

dip = px * (160/density)

density값은 HDPI, MDPI, LDPI의 값들입니다. 
결국 MDPI일때는 dip값은 px값과 같다는 것이지요.

모바일에서 px은 멀티 스크린을 지원할때 문제가 되기 때문에 지양한다.

그래서 여러 해상도의 화면에 맞추기 위해 모바일에서는 dip를 사용하고 있다.

안드로이드 치수
     px: pixel 10px는 정확히 10픽셀. 스크린 픽셀의 실제 크기와 밀도와 상관없다
           QVGA (320x240)에서 10px는 높이의 1/24를 차지. VGA(640x480)은 10px는 1/64를 차지.
     dip, dp: device-independent pixel. 실제 픽셀 크기와 밀도에 따른 변활르 막기 위한 단위.
          이는 dip dot independent pixel이라고도 부른다. 안드로이드는 160dpi 화면에 나타나는 것과
          똑같은 크기로 그 개체의 크기를 확대/축소 할 수 있다.
          예로 해상도가 640x480, 크기 4x3인치라면 픽셀 밀도는 160dpi므로 10dp 객체는 10px 그대로 보인다.
          그러나 640x480이고 8x6화면은 밀도가 80dpi므로 10dp 개체가 10px짜리 객체보다 두배크다.
          안드로이드가 dp를 확대하거나 축소하는 스케일은 정확친 않고 대략 맞출 뿐이다.
      sp: sclaed pixcel. dp와 비슷, 문자열과 같이 밀도 스케이이 정확해야 하는 개체의 크기를 정할 때 사용
      pts: points.텍스트 에디터 처럼 문자의 크기를 포인트로 정할 때 사용 포인트는 1포인트 1/72인치.
      in: inches
      mm: millimeters

240 Density 기준으로 디자인을 할때 다음과 같이 간단히 dp값을 알 수 있습니다.

dp = px / 3 * 2
dp = px * 0.66

1. 우리는 240 Density의 480x800 해상도를 가지고 있는 갤럭시S를 대상으로 개발/디자인 한다.
2. 모든 개발에서 사용하는 단위는 dp와 sp로만 이루어진다. (단, 이미지는 px사용)
3. 디자이너는 모든 px사이즈의 표기에 대하여 나누기 3 곱하기 2를 한 후 dp라는 단위를 사용한다.
4. 디자이너는 사용 폰트 크기에 대하여 위에 나와있는 sp 테이블 이미지를 참고하여 사용한다.
5. 디자이너는 모든 제작된 이미지들의 크기를 480x800 대상으로 제작하되 특별한 계산은 하지 않는다.
6. 개발자는 Resource Identifier중 drawable-hdpi만을 사용한다. (개인적으로 아이콘은 mdpi도 만들길 추천함)

참고URL: http://hest.tistory.com/111

관련블로그: http://gtko.springnote.com/pages/5725475

관련블로그: http://blog.naver.com/monster7575?Redirect=Log&logNo=100113840411

참고사이트: http://developer.android.com/guide/practices/screens_support.html

출처 : http://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange

Application Resources
 >

Handling Runtime Changes

Some device configurations can change during runtime (such as screen orientation, keyboard availability, and language). When such a change occurs, Android restarts the running Activity (onDestroy() is called, followed by onCreate()). The restart behavior is designed to help your application adapt to new configurations by automatically reloading your application with alternative resources.

To properly handle a restart, it is important that your Activity restores its previous state through the normal Activity lifecycle, in which Android callsonSaveInstanceState() before it destroys your Activity so that you can save data about the application state. You can then restore the state duringonCreate() or onRestoreInstanceState(). To test that your application restarts itself with the application state intact, you should invoke configuration changes (such as changing the screen orientation) while performing various tasks in your application.

Your application should be able to restart at any time without loss of user data or state in order to handle events such as when the user receives an incoming phone call and then returns to your application (read about the Activity lifecycle).

However, you might encounter a situation in which restarting your application and restoring significant amounts of data can be costly and create a poor user experience. In such a situation, you have two options:

  1. Retain an object during a configuration change

    Allow your Activity to restart when a configuration changes, but carry a stateful Object to the new instance of your Activity.

  2. Handle the configuration change yourself

    Prevent the system from restarting your Activity during certain configuration changes and receive a callback when the configurations do change, so that you can manually update your Activity as necessary.

Retaining an Object During a Configuration Change

If restarting your Activity requires that you recover large sets of data, re-establish a network connection, or perform other intensive operations, then a full restart due to a configuration change might be an unpleasant user experience. Also, it may not be possible for you to completely maintain your Activity state with the Bundle that the system saves for you during the Activity lifecycle—it is not designed to carry large objects (such as bitmaps) and the data within it must be serialized then deserialized, which can consume a lot of memory and make the configuration change slow. In such a situation, you can alleviate the burden of reinitializing your Activity by retaining a stateful Object when your Activity is restarted due to a configuration change.

To retain an Object during a runtime configuration change:

  1. Override the onRetainNonConfigurationInstance() method to return the Object you would like to retain.
  2. When your Activity is created again, call getLastNonConfigurationInstance() to recover your Object.

Android calls onRetainNonConfigurationInstance() between onStop() and onDestroy() when it shuts down your Activity due to a configuration change. In your implementation ofonRetainNonConfigurationInstance(), you can return any Object that you need in order to efficiently restore your state after the configuration change.

A scenario in which this can be valuable is if your application loads a lot of data from the web. If the user changes the orientation of the device and the Activity restarts, your application must re-fetch the data, which could be slow. What you can do instead is implement onRetainNonConfigurationInstance() to return an object carrying your data and then retrieve the data when your Activity starts again withgetLastNonConfigurationInstance(). For example:

@Override
public Object onRetainNonConfigurationInstance() {
   
final MyDataObject data = collectMyLoadedData();
   
return data;
}

Caution: While you can return any object, you should never pass an object that is tied to the Activity, such as a Drawable, an Adapter, a View or any other object that's associated with a Context. If you do, it will leak all the Views and resources of the original Activity instance. (To leak the resources means that your application maintains a hold on them and they cannot be garbage-collected, so lots of memory can be lost.)

Then retrieve the data when your Activity starts again:

@Override
public void onCreate(Bundle savedInstanceState) {
   
super.onCreate(savedInstanceState);
    setContentView
(R.layout.main);

   
final MyDataObject data = (MyDataObject) getLastNonConfigurationInstance();
   
if (data == null) {
        data
= loadMyData();
   
}
   
...
}

In this case, getLastNonConfigurationInstance() retrieves the data saved by onRetainNonConfigurationInstance(). If data is null (which happens when the Activity starts due to any reason other than a configuration change) then the data object is loaded from the original source.

Handling the Configuration Change Yourself

If your application doesn't need to update resources during a specific configuration change and you have a performance limitation that requires you to avoid the Activity restart, then you can declare that your Activity handles the configuration change itself, which prevents the system from restarting your Activity.

Note: Handling the configuration change yourself can make it much more difficult to use alternative resources, because the system does not automatically apply them for you. This technique should be considered a last resort and is not recommended for most applications.

To declare that your Activity handles a configuration change, edit the appropriate <activity> element in your manifest file to include the android:configChanges attribute with a string value that represents the configuration that you want to handle. Possible values are listed in the documentation for the android:configChanges attribute (the most commonly used values are orientation to handle when the screen orientation changes and keyboardHidden to handle when the keyboard availability changes). You can declare multiple configuration values in the attribute by separating them with a pipe character ("|").

For example, the following manifest snippet declares an Activity that handles both the screen orientation change and keyboard availability change:

<activity android:name=".MyActivity"
         
android:configChanges="orientation|keyboardHidden"
         
android:label="@string/app_name">

Now when one of these configurations change, MyActivity is not restarted. Instead, the Activity receives a call to onConfigurationChanged(). This method is passed a Configuration object that specifies the new device configuration. By reading fields in the Configuration, you can determine the new configuration and make appropriate changes by updating the resources used in your interface. At the time this method is called, your Activity's Resources object is updated to return resources based on the new configuration, so you can easily reset elements of your UI without the system restarting your Activity.

For example, the following onConfigurationChanged() implementation checks the availability of a hardware keyboard and the current device orientation:

@Override
public void onConfigurationChanged(Configuration newConfig) {
   
super.onConfigurationChanged(newConfig);

   
// Checks the orientation of the screen
   
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
       
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
   
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
       
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
   
}
   
// Checks whether a hardware keyboard is available
   
if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
       
Toast.makeText(this, "keyboard visible", Toast.LENGTH_SHORT).show();
   
} else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
       
Toast.makeText(this, "keyboard hidden", Toast.LENGTH_SHORT).show();
   
}
}

The Configuration object represents all of the current configurations, not just the ones that have changed. Most of the time, you won't care exactly how the configuration has changed and can simply re-assign all your resources that provide alternatives to the configuration that you're handling. For example, because the Resources object is now updated, you can reset any ImageViews with setImageResource(int) and the appropriate resource for the new configuration is used (as described in Providing Resources).

Notice that the values from the Configuration fields are integers that are matched to specific constants from the Configuration class. For documentation about which constants to use with each field, refer to the appropriate field in the Configuration reference.

Remember: When you declare your Activity to handle a configuration change, you are responsible for resetting any elements for which you provide alternatives. If you declare your Activity to handle the orientation change and have images that should change between landscape and portrait, you must re-assign each resource to each element during onConfigurationChanged().

If you don't need to update your application based on these configuration changes, you can instead not implement onConfigurationChanged(). In which case, all of the resources used before the configuration change are still used and you've only avoided the restart of your Activity. However, your application should always be able to shutdown and restart with its previous state intact. Not only because there are other configuration changes that you cannot prevent from restarting your application but also in order to handle events such as when the user receives an incoming phone call and then returns to your application.

For more about which configuration changes you can handle in your Activity, see the android:configChanges documentation and the Configuration class.

↑ Go to top

← Back to Application Resources

http://blog.daum.net/hopefullife/98

Android... 멈추지 않는 변화

 

 

[Intro]

SDK 1.5 R1 버전이 발표된지 꽤 오래전 일입니다만,

무슨 바람이 들었는지 몰라도 재빨리 바뀐 부분을 캐치했어야 하는데 그러지 못했습니다.

이제서야 부랴부랴 Android Development Tool도 뭔가 바뀌어 버린것을 알았고,

SDK들도 뭔가 바뀌어 버린 것을 알았습니다.

 

이전에 만들어 두었던 프로젝트들도 1.5버전에 맞추어서 실행 하려던 찰나...

아차! 이게 뭡니까...

 

AbsoluteLayout is deprecated

 

아... 이렇게 허망하게 없애 버리다니...

없애는게 이해가 안가는것은 아닙니다.

디바이스마다 화면이 다르니 절대 좌표로 박으면 호환성 유지가 안되는건...

뭐 충분히 생각 할 수 있는 부분입니다. 그냥 이번에 1.5버전에선 과감하게 빼버린 것이지요

 

 

[Why?]

일단 AbsoluteLayout은 안좋은 것이니까 왠만하면 쓰지맙시다!

라고 구글에서 강력하게 걸고 넘어지는 것인데...

그렇다면 정말 절대 좌표를 사용해야 하는 경우에는 어떻게 해야 하는 것인가요...

일단 열심히 찾아 봤습니다.

 

맨 먼저 Reference를 뒤져봤습니다.

This class is deprecated. Use FrameLayout, RelativeLayout or a custom layout instead.

라는군요...

FrameLayoutRelativeLayoutCustomLayout을 사용 하라는 말 밖에 없네요.

(CustomLayout이라는 Class가 있는게 아닙니다.)

뭐 살짝 무책임 한게 없지 않아 있습니다. 데체 왜 없애 버렸는지,

그리고 어떻게 대체 하라는지 급 궁금해져서 또 다시 뒤져봤습니다.

 

검색 키워드를 잘 선택 했는지 어렵지 않게 아래 글을 발견 할 수 있었습니다.

Everything you can do with AbsoluteLayout can be done with FrameLayout 
and RelativeLayout. AbsoluteLayout was deprecated because its use 
encourage applications that will NOT work with devices with different 
screen dimensions/resolutions. As such we decided to deprecate 
AbsoluteLayout to encourage developers to do the right thing and use 
layouts that will work much better with other screen 
dimensions/resolutions.

 

-- 
Romain Guy 
Android framework engineer 
romain...@android.com

각 디바이스들은 각각 다른 크기와 해상도를 가지기 때문에 없애버렸습니다만,

FrameLayout과 RelativeLayout으로 모두 표현 가능 하다고

Romain Guy님께서 설명해 주신 글을 발견 했습니다. (원문보기)

 

누군지는 몰라도 Android framework engineer의 말이니 믿어야죠.

믿을 수 밖에 없습니다. 그럼 어떻게 해야 하는 것일까요?

 

 

[How?]

정렬이 잘 되어 있고 깔끔하게 보이는 왠만한 레이아웃은 AbsoluteLayout을 사용할 필요가 없습니다.

하지만 정말로 원하는 위치에 View들을 배치하고 싶을때는 어떻게 해야 좋을까요?

 

 

위와 같이 주어진 공간 안에서 View들을

원하는 임의의 위치에 표시 하고 싶을때는 어떻게 하면 좋을지 생각해 봅시다.

일단 FrameLayout은 모든 Child들을 좌측 상단(Left-Top)에 붙여버리는 Layout입니다.

Child가 여러개 붙어 있다면 Child View들은 좌측 상단에 붙어 차곡차곡 쌓이게 되는 것이죠.

FrameLayout안에 있는 View들은 margin같은 속성이 전혀 먹혀 들어가지 않습니다.

 

결국 FrameLayout은 사용 불가...

(하지만 FrameLayout이 전혀 사용 할 수 없는건 아닙니다. 조금만 생각해 보시면 알 수 있습니다.)

 

그렇다면 RelativeLayout을 선택 해야 하는데...

RelativeLayout은 B라는 View는 A라는 View의 아래, 혹은 좌측, 혹은 우측

이런식으로 상대적인 위치를 지정하여 View들을 배치 하는 방법입니다.

 

그렇다면 원하는 위치만큼 위에서, 옆에서 떨어 뜨리고 싶으면 이렇게 하면 되지 않을까...

하고 생각해 봤습니다.

 

 

원하는 위치만큼 밀어낼 투명한 View들을 만들어서 공간을 차지하게 만드는 겁니다....

그런데 그림으로 봐도 불가능 할 것 같지 않나요...?

그냥 손으로 때려 박는 것이라면야 가능 하겠지만, 만약 코딩으로 원하는 위치에 표시하고 싶다거나

View의 위치를 옮긴다고 한다면 이런 방법은 전혀 말이 되지 않습니다.

 

그래서 좀더 생각을 해본 결과 RelativeLayout안에 있는 

Child View의 android:layout_margin 값을 이용 하면 된다는 결론이 나오게 되었습니다.

그림 한번 보시죠.

 

 

각 View마다 android:layout_margin 속성을 주는 겁니다.

이때 상대적인 위치는 지정할 필요가 없습니다.

따로 지정하지 않으면 기준위치는 왼쪽 상단이니까 딱 좋은게죠.

빨간색 View를 만약 (10, 10) 위치에 표시하고 싶다면,

android:layout_marginTop과 android:layout_marginLeft값을 각각 10으로 설정 하면 땡입니다.

물론 View를 여러개 추가해도 상관 없습니다.

 

아래는 위의 레이아웃을 구성한 실제 코드 입니다.

<RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#FFFFFF" >
    <View
        android:layout_width="100dip"
        android:layout_height="100dip"
        android:background="#FF0000"
        android:layout_marginTop="50dip"
        android:layout_marginLeft="50dip" />
    <View
        android:layout_width="100dip"
        android:layout_height="100dip"
        android:background="#00FF00"
        android:layout_marginTop="100dip"
        android:layout_marginLeft="190dip" />
    <View
        android:layout_width="100dip"
        android:layout_height="100dip"
        android:background="#0000FF"
        android:layout_marginTop="30dip"
        android:layout_marginLeft="320dip" />
 </RelativeLayout>

생각보다 간단합니다.

이제 원하는 위치에 View를 옮길 수 있게 되었습니다.

 

 

[More]

RelativeLayout의 margin속성을 이용한 트릭으로 잘 구성을 하긴 했습니다.

그런데 조금 욕심이 나는 부분이 바로 Custom Layout인거죠.

좀더 잘 해 보겠다! 라고 하시는 분들은 Layout따로 만드셔도 됩니다.

 

그런데 한가지 알아야할 사실은 1.5버전 소스를 보면

AbsoluteLayout.java 소스가 버젓이 살아있다는 것입니다.

@Deprecated로 이 Class가 더 이상 사용되지 않기를 바라지만,

사실 소스가 그대로 살아 있으니 얼마든지 쓸 수 있습니다.

그냥 소스 통채로 가져다 쓰면 되긴 하지만 그건 그냥 Deprecated 경고를 무시하고

AbsoluteLayout을 사용하는것과 다른게 없죠.

 

그래서 어떻게 Customize를 하는지 간단하게 살펴 보기로 하겠습니다.

방법만 알면 나중에 어떤 Layout을 구성하든지 다 만들어 낼 수 있는 법!

전체소스는 올릴 수 없으니 AbsoluteLayout.java 도 참고하며 공부해 보도록 합시다.

 

 

[More and More]

일단 Layout은 모두 ViewGroup을 상속 받습니다.

ViewGroup은 View의 특수한 형태... 라고 설명해 두었는데,

그냥 다른 View를 담을 수 있는 View인거죠.

그렇다면 아래와 같이 Class를 만들 수 있겠죠?

public class CustomLayout extends ViewGroup

네... 간단합니다.

Eclipse에서 필요한 생성자와 Method를 만들라고 할터이니 그대로 만들면 됩니다.

헌데, 생성자를 만들 때 한가지 중요한 것이 있습니다.

XML에서 사용하기 위해서는 반드시 아래 두개의 생성자가 필요 합니다.

public CustomLayout(Context context)
public CustomLayout(Context context, AttributeSet attrs)

위 두 생성자가 없으면 XML에서 사용 할 수 없습니다. 엄밀하게 말하면

public CustomLayout(Context context, AttributeSet attrs) 생성자가 XML에서 사용하는

생성자 입니다. 하지만 코딩을 할 때는 public CustomLayout(Context context)를 사용하는게

편하기 때문에 두개 다 쓰는 것이죠.

AttributeSet 만들려면 좀 귀찮습니다. 코딩할때는 필요도 없고요.

 

그리고 onLayout은 반드시 Override해야 합니다.

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)

onLayout이 실제로 Child View들을 배치 하는 부분입니다. 있다가 살펴 보기로 하죠.

 

Layout은 LayoutParams와 같이 사용해야 합니다.

굳이 LayoutParams를 사용하지 않겠다고 하시는 분들이 있을 수 도 있습니다.

그럴 경우에는 Child View에 레이아웃 정보를 담은 뒤에 

onLayout에서 불러 와서 배치 하면 됩니다....만 Custom View가 아닌 이상

정보를 어떻게 담을 수 있겠습니까? 결국 LayoutParams를 써야 합니다. 음허허!

 

LayoutParams는 보통 Inner Class로 만들어 둡니다.

(다른 LayoutParams들도 다 Inner Class로 만들어져 있습니다.)

CustomLayout Class안에 하나더 만들어 봅시다.

public static class LayoutParams extends ViewGroup.LayoutParams

ViewGroup.LayoutParams를 상속 받아 만듭니다.

한가지 주의할 것은 static으로 만들어야 한다는 것입니다.

ViewGroup.LayoutParams Class가 static이기 때문에,

생성자를 사용하기 위해서는 CustomLayout.LayoutParams도 static이어야 하죠.

x, y 좌표 값을 가질 수 있게 CustomLayout.LayoutParams Class을 조금 수정해 봅시다.

public int x
public int y

일단 x, y 두 필드를 추가 하고,

public LayoutParams(int width, int height)

요런 위와 같이 생긴 생성자를,

public LayoutParams(int width, int height, int x, int y)

요렇게 바꾸어 줍시다.

생성자에서 x, y 저장하는건 굳이 설명할 필요도 없을 정도로 간단합니다.

ViewGroup.LayoutParams가 기본적으로 public int width, public int height를 가지고 있기 때문에,

super(width, height)는 반드시 필요하고, 거기에 x, y값을 저장하는 코드만 더넣으면 땡입니다.

 

그럼 실제로 LayoutParams를 어떻게 쓰는지 코드를 살펴 봅시다.

아직 전부 설명하지 않았지만 먼저 LayoutParams부분을 보도록 하겠습니다.

CustomLayout layout = new CustomLayout(this);
View v = new View(this);
v.setLayoutParams(new CustomLayout.LayoutParams(100, 100, 10, 10));
layout.addView(v);

이게 전부 입니다. 전혀 어렵지 않습니다.

 

예전에도 포스팅을 했었는데, XML Inflating은 Depth-First 방식이기 때문에,

onLayout()이 호출 될 때는 Child View들이 이미 추가가 되어 있는 상태입니다.

 

그래서 onLayout()안에서는 getChildCount()로 Child의 수를 알아내고, 

getChildAt(i)으로 View를 얻어낸 뒤에, Child View의 getLayoutParams()을 사용해서

위치 정보를 얻어오면 됩니다.

마지막으로 Child View의 layout() 메소드를 호출하면 Child View가 자리를 잡게 되는 것이죠.

final void layout(int l, int t, int r, int b)

Left, Top, Right, Bottom값을 넣어 주어야 합니다.

저희가 알고 있는건 x, y, width, height 4가지 정보를 알고 있으니 간단하게 LTRB로 변환 가능 하겠죠?

 

 

[Further Works]

사실 저기까지 만들어 놓으면 코딩할때 밖에 쓸 수 없습니다.

XML에서 사용하기 위해서는

LayoutParams에 public LayoutParams(Context c, AttributeSet attrs)생성자를 다시 작성 해야 합니다.

obtainStyledAttributes()를 사용 해야 하는데,

요것에 대한 내용은 좀 길어서 지금은 빼놓도록 하겠습니다.

+ Recent posts