Program/Android Java

[Android] 위젯 : 리소스

너구리V 2012. 7. 6. 10:57

<프로그램의 구성>

 

  프로그램은 코드와 리소스로 구성한다. 여러가지 이유로 코드와 데이터는 가급적 분리하는 것이 좋다.

 

  코드는 일정한 문법에 따라 자바 소스 파일에 작성되며, 프로그램의 흐름과 동작을 정의한다.

 

  리소스는 동작과 직접적인 상관이 없으며, 주로 프로그램의 외형 장식을 정의한다.

 

  리소스와 코드를 분리하면 좋은 이유는 코드는 프로그래머가 작성하고, 리소스는 디자이너가 작성함으로써 병행 작업이 가능하다.

 

  한마디로 개발 속도도 높이고, 관리도 용이하다.

 

 

<리소스>

 

  리소스란 응용 프로그램 실행에 필요한 일체의 데이터를 합쳐서 '리소스'라고 한다.

 

  리소스의 예로는 문자열이나, 이미지, 사운드 같은 것들이 있다.

 

  리소스는 환경이나 조건에 따라, 통째로 교체할 수 있으므로 장비 호환성 확보에 유리하며 언어에 따른 지역화도 간단해진다.

 

  뭐든 분리해 놓으면 관리하기 편해지며, 합치는 과정에서 다양한 기교를 부릴 수 있어 응용의 여지가 생긴다.

 

 

  안드로이드는 프로젝트도 현대적 추세에 따라 res폴더와 assets폴더로 좀 더 세분화하여 관리한다.

 

  이 둘의 차이를 설명하자면 이렇다.

 

  (1) 리소스에 비해 에셋은 동영상 같은 큰 데이터를 의미한다.

  (2) 리소스는 컴파일되지만, 에셋은 원본 그대로 저장된다.

  (3) 리소스는 빈번히 사용되는데 비해, 에셋은 상대적으로 덜 사용된다.

 

  한마디로 assets은 대용량 파일을 넣고, res는 저용랑 파일을 넣는다.

 

 

  리소스의 종류

 

  리소스도 종류가 여러 개 있다. 리소스 폴더 이름은 강제적으로 정해져 있으므로, 타입에 맞는 폴더에 집어넣어야 한다.

 

  각 폴더에 저장되는 리소스 타입은 다음과 같다.

 

  

 

 

  그리고 이 value 폴더에는 문자열, 색상, 배열, 크기, 스타일 등 다양한 자료가 들어가는만큼, 리소스 폴더처럼 또 타입이 필요하다.

 

  

 

  그런데 이는 권장사항이지, 강제는 아니므로 꼭 지키지 않아도 된다.

 

  다만 소스를 여기저기 흩어 놓으면 관리하기 어려우므로, 종류 별로 관리하자는 것이다.

 

 

  다시 정리하면, 앞의 폴더는 꼭 지켜줘야하며, 리소스는 임의대로 생성해도 되나 가급적 권장 사항에 따르자는 내용이었다.

 

 

  aapt

 

  aapt란 res 하위 폴더에 저장된 리소스를 컴파일하여 패키징하는 것인데, 그냥 이런 것이 있다는 것만 알아두고 넘어가면 된다.

 

  aapt가 하는 일은 (1) 폴더의 이름과 리소스 파일의 확장자에 따라 파일 포맷을 판별하여 미리 정의된 방식으로 리소스를 컴파일하기

 

  (2) res 하위 폴더에 있는 모든 리소스 ID를 검색하여, gen 폴더의 R.java 파일을 만들어 내는 것이다.

 

  한마디로 aapt는 res 안에 모든 리소스를 컴파일해서 넘겨준다. 그리고 R.java 파일을 만든다.

 

 

  리소스 사용

 

  증요한 것은 aapt가 어떤 존재인가는 별로 중요하지 않다.

 

  어차피 컴퓨터에서 해주는 것은 컴퓨터가 해주기 때문이다. (당신이 공학 박사가 생각이 아니라면 말이다)

 

 

  리소스 코드는 다방면으로 활용된다. 대표적으로 setContentView인데

 

  이 메서드는 본디 액티비티에 채울 뷰를 요구하는데, 매번 생성하기 번거로우니 ID만 받아 내부적으로 직접 전개하도록 설계되었다.

 

  객체 지향 클래스들의 메서드들은 가능한 모든 타입의 인수를 받아들여, 최종적으로 필요한 정보를 내부적 처리를 하도록 되어 있다.

 

 

  뭐 이모저모 빙빙 돌려 얘기했는데, 간단하다. 이제껏 우리가 했던 것을 체계적으로 얘기하는 것일 뿐.

 

  리소스를 사용하여 전개하는 방법은 크게 2가지다.

 

 

  첫번째는, 각각의 XML 문서를 코드에서 불러와 참조하는 것이고

 

  두번째는, layout에 있는 XML에 모두 등록해놓고 코드에서 한번에 불러내는 것이다.

 

  이것을 코드로 풀어내면 이렇다.

 

 

  (1) res 폴더의 하위 폴더에 각각 저장해두고, 코드에서 각각 호출하기

 

       우리가 앞서 보았던 Strings, colors, dimens 등을 각각 폴더에 저장한 후 부르는 것이다.

 

  public class ReadResource extends Activity {
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.widget_readresource);

      Resources res = getResources();
  
      TextView text = (TextView)findViewById(R.id.text);
  
      String str = res.getString(R.string.textstr);
      text.setText(str);
      //text.setText(R.string.textstr); // (위의 2줄을) 한 줄로 표현하면 이렇다는 뜻이다
  
      int textcolor = res.getColor(R.color.textcolor);
      text.setTextColor(textcolor);
      //text.setTextColor(R.color.textcolor);
  
      float textsize = res.getDimension(R.dimen.textsize);
      text.setTextSize(textsize);
      //text.setTextSize(R.dimen.textsize);
    }
  }

 

 

  (2) 레이아웃 XML 문서에 등록한 후, 한 번에 호출하기

 

  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#e0e0e0"
    >
  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/textstr"
    android:textSize="@dimen/textsize"
    android:textColor="@color/textcolor"
    />
  </LinearLayout>

 

  이렇게 레이아웃 XML 문서에 모두 등록을 해놓고

 

  public class ReadResource2 extends Activity {
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.widget_readresource2);
    }
  }

 

  이처럼 한꺼번에 호출하면 된다.

 

 

  그런데, 이 리소스 ID를 참조하는 방법이 은근히 헷갈린다. 그래서 이 참에 확실히 정리하고 가자.

 

  

 

 

  전혀 새롭지 않은 내용이다. 그동안 했던 아이디 참조를 정리했을 뿐이다.

 

 

  결국 이번 단원에서 배운 내용은 리소스를 참조하는 방법을 배운 것이다. (정해진 참조폴더를 지켜야 한다는 것을 잊지마라)

 

  간단하게 문자열이나 색상만을 나타냈지만, 이외에도 배열, 애니메이션, 사운드, 동영상, 도형 등 아주 많은 리소스를 정의할 수 있다.

 

  나머지 리소스는 다시 소개할 예정이니 걱정하지 말자. 방법은 여기서 소개한 것과 별반 다르지 않다.

 

 

 

<스타일과 테마>

 

  스타일과 테마는 여러가지 속성 값의 집합을 뜻하는 것이다.

 

  이 둘의 차이는 간단하다. 스타일은 뷰 단위, 테마는 액티비티이다.

 

  왠지 이름만 들어도 감이 오지 않는가? 스타일은 사람들처럼 개개인의 스타일, 테마는 스케일이 큰 극장처럼 말이다.

 

 

  스타일은 아까 말했던대로 속성의 집합이다. 다양한 속성을 지정한 후에 가져다 사용만 하면 일괄적인 모습을 갖게 된다.

 

  무슨 말인가는 직접 해보면 더 빠를 것이다. res/values 폴더에 styles.xml 파일을 생성하고 다음 문서를 작성한다.

 

  styles.xml

 

  <?xml version="1.0" encoding="utf-8"?>
  <resources>
    <style name="red15">
      <item name="android:textColor">#ff0000</item>
      <item name="android:textSize">15pt</item>
    </style>
    <style name="yellow15italic" parent="@style/red15">
      <item name="android:textColor">#ffff00</item>
      <item name="android:textStyle">italic</item>
    </style>
    <style name="mytheme">        
      <item name="android:windowNoTitle">true</item>
    </style>
  </resources>

 

  스타일을 지정하는 속성은 <style name = " "> 이게 끝이다. 간단하지 않은가?

 

  그 속에 item name=" " 해놓고 그 안에 우리가 여태 했던 속성을 집어넣으면 된다.

 

  parent는 말 그대로 상속받았다는 얘기다. 상속 받지 않을 때는 생략하면 된다.

 

  이제 이 스타일이 적용되는지 테스트 해볼 차례다. 코드에는 작성할 것이 없다. 불러오기만 하면 된다.

 

 

  Widget/layout/styletest.xml

 

  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#808080"
    >
  <TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="green"
    android:textColor="#00ff00"
    />
  <TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="red 15 point"
    style="@style/red15"
    />
  <TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="yellow 15 point"
    style="@style/yellow15italic"
    />
  </LinearLayout>

 

 

  

 

 

  리니어 레이아웃 안에 세 개의 텍스트를 배치하고, 스타일 리소스를 적용해 보았다.

 

  위젯에 style 속성에 "@style/스타일명" 형식으로 지정한다.

 

  안드로이드에 미리 정의된 스타일이 아니라, 응용 프로그램이 정의한 스타일이므로 android: 네임스페이스는 붙이지 말아야 한다.

 

 

  물론 스타일을 사용하지 않고, 개별 속성을 일일이 지정해도 똑같다.

 

  그러나 스타일을 적용해 놓으면, 스타일만 수정함으로서 참조하는 모든 위젯의 모양을 한꺼번에 변경할 수 있어 편리하다.

 

  조금 위의 예제인, styles.xml의 textSize를 수정해보자.

 

  styles.xml

 

  <?xml version="1.0" encoding="utf-8"?>
  <resources>
    <style name="red15">
      <item name="android:textColor">#ff0000</item>
      <item name="android:textSize">8pt</item> // 15pt를 8pt로 바꾸어보자.
    </style>
    <style name="yellow15italic" parent="@style/red15">
      <item name="android:textColor">#ffff00</item>
      <item name="android:textStyle">italic</item>
    </style>
    <style name="mytheme">        
      <item name="android:windowNoTitle">true</item>
    </style>
  </resources> 

 

  

 

  아까보다 확실히 줄어든 것을 볼 수 있다. 스타일을 상속 받았기 때문에 그대로 적용된다.

 

 

  이번에는 Manifest 파일을 열어서 다음을 복사해 넣어보자.

 

   <activity android:name="exam.Widget.ThemeTest" 
     android:theme="@style/mytheme" 
     android:label="@string/app_name" />

 

  실행해 보면 테마의 지시대로 액티비티에 타이틀 바가 나타나지 않을 것이다. 액티비티 전체를 이용하고자 할 때 유용하다.

 

  

 

  단순히 manifest.xml에서 지정했다고 되는 것이 아니고, 좀 전에 styles.xml에서 이렇게 지정했기 때문이다.

 

 <style name="mytheme">        
   <item name="android:windowNoTitle">true</item>
 </style>

 

 

  또 다른 한편으론 이런 것도 이용할 수 있다.

 

  SystemTheme 액티비티를 만들고, 또 다음 소스를 Manifest.xml에 추가한다.


  <activity android:name="exam.Widget.SystemTheme" 

    android:theme="@android:style/Theme.Dialog" 
    android:label="@string/app_name" />

 

  대화 상자가 이처럼 화면의 중앙에 나타나고, 뒷부분의 배경은 반투명하게 나타난다.

 

  

 

 

  대체 리소스 

 

  안드로이드의 개방 환경만큼이나 수많은 하드웨어 구성이 가득하다.

 

  다양한 사용자 취향에 맞출 수 있다는 것은 장점이지만, 개발자 입장에서는 호환성 확보에 많은 신경을 써야 한다.

 

  각종 하드웨어에 맞추려면 아무래도 하나의 소스를 가지고는 한계가 있다.

 

  안드로이드는 환경에 따라 적절한 리소르를 선택하는 방식으로 이 문제를 해결한다.

 

 

  개발자가 환경별로 사용할 리소스로 따로 작성해 놓으면 시스템은 실행시 가장 적합한 리소스를 골라 불러온다.

 

  예를 들어, 한글과 영어를 작성해놓으면 장비의 언어 설정에 따라 맞는 리소스를 가져온다.

 

  각 환경에 사용할 대체 리소스를 폴더별로 따로 작성하되, 폴더의 이름에 환경을 의미하는 접미어를 붙여놓는다.

 

  

 

  (keysexposed는 하드웨어 key 지원, keyshidden은 있으나 사용은 못하고, keysofr는 SW키보드를 제공한다)

 

 

  각 환경을 의미하는 접미어는 시스템에 의해 미리 정해져 있으며 접미어를 붙이는 데도 엄격한 규칙이 적용된다.

 

  (1) 여러 개의 접미어를 붙일 때 대시(-)로 구분함, 반드시 도표의 순서대로 작성해야 함

  (2) 모든 폴더는 같은 부모에 , 중첩은 불가

  (3) 접미는 대소문자 구분. 한국어 레이아웃은 layout-kr이어야 하며, layout=KR은 안된다.

  (4) 한 환경에 대해 하나의 접미만 가능. layout-kr-en 따위는 할 수 없다.

  (5) 코드나 리소스에 참조할 때는 접미를 붙이지 않는다. 접미는 어디까지나 운영체제가 리소스를 선택할 경우에만 쓴다.

 

 

  지금까지의 예제는 대체 리소스를 정의하지 않았기 때문에 항상 같은 리소스가 로드되었다.

 

  그래서 이제 예제를 통해 대체 리소스에 대해 알아보려고 한다.

 

  그러나 어떤 리소스를 선택할 것인가는 시스템이 자동으로 결정하며, 사용자가 강제로 선택할 수는 없다.

 

  그래서 한국어 장비에서 영문 리소스를 테스트하기가 쉽지 않다. 언어 설정이나 폴더명을 바꾸는 식으로 변칙을 써야 한다.

 

 

  해상도나 장비 구성도 바꿀 수 있으나 번거로우므로, 가로·세로 방향별로 리소스를 작성해 보자.

 

 

  Widget/layout/landport.xml

 

  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center"
    android:background="#e0e0e0"
    >
  <Button
    android:layout_width="100px" 
    android:layout_height="wrap_content" 
    android:text="버튼이다"
    />
  <Button
    android:layout_width="100px" 
    android:layout_height="wrap_content" 
    android:text="수직이다"
    />
  </LinearLayout>

 

 

  이 리소스를 사용할 소스 파일은 다음과 같이 작성한다.

 

  public class LandPort extends Activity {
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.widget_landport);
    }
  }

 

  이처럼 ID를 읽을 때는 접미를 뺀 폴더명만 적으면 된다. 영문을 읽고 싶다고 R.layout-en.landport이라고 하면 안 된다.

 

  실행해 보면 처음엔 밑의 화면을 얻을 수 있다. Ctrl + 11을 누르면 화면이 세로에서 가로로 바뀐다.

 

   

 

 

  지금 당장 실행해보면 이런 그림이 나오지 않을 것이다. 그것은 당연히 대체 리소스를 작성하지 않았기 때문이다.

 

  

 

 

  가로 방향일 경우를 지정하기 위해, 대체 리소스를 하나 더 만든다.

 

  res 폴더 안에 layout_land라는 이름으로 서브 폴더를 만들고, 이 안에 landport.xml을 하나 더 작성한다.

 

 

  Widget/layout-land/landport.xml

 

  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center"
    android:background="#e0e0e0"
    >
  <Button
    android:layout_width="100px" 
    android:layout_height="wrap_content" 
    android:text="누르세요"
    />
  <Button
    android:layout_width="100px" 
    android:layout_height="wrap_content" 
    android:text="수평이다"
    />
  </LinearLayout>

  이제 실행해보면 결과가 위의 그림처럼 제대로 나올 것이다.

  마지막으로 각 레이아웃은 위젯의 배치나 속성 값이 각각 다를 수 있지만, 위젯의 ID나 위젯 구성은 최소한 같아야 한다.

 

  만약 리소스에 따라 위젯의 구성이 달라지면, 코드도 같이 달라져야 하므로 대체 리소스를 사용하는 의미가 없어진다.

 

 

 

<텍스트 뷰>

 

  기본 속성

 

  TextView는 안드로이드 위젯 중에 가장 기본이며, 이 클래스만 잘 연구해도 위젯의 공통적인 기능과 특성들을 대부분 파악할 수 있다.

 

  문자열을 입출력하는 아주 간단한 기능을 수행하지만, 밖으로 보이는 기능보다 숨겨진 고급 기능들이 굉장히 많다.

 

  TextView에는 문자열, 포맷팅, 출력, 입력, 편집에 관련된 대부분의 기능이 모두 구현되어 있다.

 

  TextView 자체는 이들 기능의 대부분을 드러내지 않고 숨긴다.

 

  잠시 후에 연구해 보겠지만, 버트과 에디트는 텍스트 뷰의 숨겨진 기능을 활성화하고 약간의 스타일을 입혔을 뿐이다.

 

  

 

 

  이제 하나하나 알아가보도록 하자.

 

  텍스트 뷰가제공하는 속성 중 일부는 에디트에서 더 잘 확인할 수 있으므로 에디트도 같이 연구해 보자.

 

  레이아웃 소스는 다음과 같다.

 

 

  Widget/textviewattr.xml

 

  그런데 소스가 너무 길어서, 파일로 첨부했다. 그저 res폴더 밑에 layout에 집어넣으면 된다.

 

  (코드에서 불러오가민 하면 된다)

 

  위젯의 갯수가 굉장히 많은데, 스크롤 뷰 안에 각각 조금씩 다른 텍스트 뷰와 에디트 뷰를 배치해 두었다.

 

        

 

  처음과 끝만 캡처한 것인데, 소스를 실행시켜보면 알겠지만 이 가운데에 굉장히 많은 것들이 있다.

 

  직접 실행시키고 눌러보며 어떤 차이가 있는지 느껴보기 바란다. 물론 밑에서 설명도 할 것이다.

 

 

  수평 입력

  
  위젯의 폭보다 더 긴 문자열이 입력된 경우의 동작을 지정한다.

 

  

 

  Normal : 자동개행. 폭을 넘어가면 알아서 개행해준다.

  Scroll Horizontal : 사용자가 개행을 하지 않는 한, 계속해서 한 줄에 수평하게 입력한다. 그러나 분명 개행은 가능하다.

  Single Line : 개행이 불가능하다. 입력을 얼마나 하든 끝까지 한 줄로 끝나게 된다. 즉, 개행불가.

 

 

  입력문자 제한

 

  

 

  digits : 지정한 문자열 외에 다른 문자를 집어넣을 수 없다. 위에는 aeiou 외에는 넣을 수 없을 것이다.

  numeric : 숫자만 입력받는다. 다만 3가지 종류가 있다.

 

  (1) integer : 아라비아 숫자만 입력가능

  (2) signed : 선두에 -부호를 허용, 숫자 중간에는 올 수 없음

  (3) decimal : 소수점을 허용, 소수점은 반드시 하나만 가능

 

  만약 소수점과 음수 부호를 둘 다 허용하려면, decimal|signed라고 하면 된다.

 

  phoneNumber : 전화번호를 구성하는 숫자, -

  password : 입력 중인 문자를 감추고, 마지막 문자만 보여준다.

 

 

  커서 및 포커스

 

  간단하다.  

 

  selectAllOnFocus : 갖다대면 모두 선택된다.

  Cursor Test - true : 커서가 보인다.

  Cursor Test - false : 커서가 안보인다. 물론 문자입력이 가능하다.

 

  

 

 

  자동 링크

 

  autoLink 속성은 말 그대로다. 예를 들어 우리가 사이트 주소를 갖다붙이면 자동으로 링크가 생성되는 것처럼 말이다.

 

  지원 가능한 링크는 다음과 같으며, | 연산자로 묶어 여러가지 링크를 같이 지정할 수도 있다.

  

 

 

  글자의 모양

 

  android:text="Shadow Text"
  android:textSize="20px"
  android:textColor="#ffffff"
  android:shadowColor="#0000ff" // 그림자의 색상
  android:shadowDx="3.0" // 글자와의 거리(x)
  android:shadowDy="3.0" // 글자와의 거리(y)
  android:shadowRadius="3.0" // 그림자의 크기

  android:textScaleX="0.5" // 글자의 폭이다. 높이면 옆으로 늘어나고, 줄이면 날씬해진다.

 

  

 

 

반응형