인터페이스나 혹은 추상클래스에 있는 추상메소드의 목적

추상메소드에 대해 제대로 이해하기가 쉽지는 않는듯 하다.

추상메소드와 관련된 일반적인 '지식'에 해당되는 내용들은 얼마든지 있다.

그러나 이것이 어디에 쓰기위해 존재하는지에 대해서 명확한 정립이 되어있지 않으면 남들이 만들어 놓은 추상메소드를 코딩은 하지만 정작 내가 클래스를 만들면서 추상 메소드가 가진 목적을 발휘하는 그런 클래스를 디자인하고 만들어 쓰는데까지는 가지 못하는 것을 보게된다.

오늘은 추상 메소드(abstract method)가 존재하는 목적이 뭔지에 대해 뿌리를 캐보고자 한다.


가령 예를 들어서 우리가 어떤 디바이스를 켜는(power on) 행위(method)를 한번 생각해 보자.

power on에는 다양한 종류가 있을 것이다.

PC를 켜거나(Power On), 스마트폰을 켜거나, TV를 켜거나, ... 이럴 때 켤때마다 첫번째 초기 화면으로 그 제품을 만든 회사의 로고 이미지가 뜨도록 강제하고 싶을 경우가 있을 것이다.

PC를 켤때 삼성에서 만들었으면 삼성 로고가 뜨고 LG에서 만들었으면 LG 로그가 뜨고... 스마트폰도 마찬가지로...

이와같이 Power On 할때 초기 화면에 제조사 로고를 뜨도록 강제하고 싶을 경우 이때 이 목적을 위해서 존재하는 것이 추상 메소드(abstract method)이다.


이것을 클래스로 만들어 PowerOn이라는 클래스로 만들어보자. 

이때 PowerOn을 상속받는 하위 클래스들에서는 power on할 때(켤 때) 반드시 제조사의 로고 이미지를 뜨도록 강제하고자 하면 그에 해당하는 기능을 추상 메소드로 만들어 두면 하위 클래스에서는 반드시 로고 이미지가 뜨는 기능(method)을 만들어야만 되도록 되어 있다. 그렇지 않으면 컴파일 단계에서 에러를 발생시킨다. 따라서 그것을 상속 받는 하위 클래스에서는 무조건 로고 이미지 뜨는 기능을 구현해야만되고 이것은 하위 클래스들은 이 면에서 동일한 공통성을 띄게 강제하게 되는 결과가 된다.

말하자면 사용자들에게 동일한 인터페이스를 제공해 주게 되는 결과를 창출하게 된다.

이런 목적으로 존재하는 것인 추상 메소드이다.

예제 코드를 만들어 보자.


abstract class PowerOn

{

//하위 클래스들은 무조건 로고를 띄우는 기능을 만들도록 하고 싶다.

//그럴 경우 로고를 띄우는 메소드를 추상메소드로 만들면 된다.

abstract void showLogo(); 


public String getName() {

...

return name;

}

}



class SamsungPhone extends PowerOn

{

public void showLogo() {

//여기서 삼성 로그 띄우게

}

}


class LGPC extends PowerOn

{

public void showLogo() {

//여기서 LG 로그 띄우게

}

}


그래서 결론적으로 interface나 abstract class에 있는 abstract method의 목적은 어떤 특정 기능을(메소드를) 하위 클래스에서 반드시 만들도록 강제하고자 할때 사용하는 것이다.


그런 점에서 interface라는 것의 존재 목적이 보이는 것이다.

interface는 interface가 가지고 있는 모든 메소드는 전부 abstract 메소드이다.

추상 메소드(abstract 메소드)의 목적은 하위 클래스에 특정 기능을 반드시 만들도록 강제하고자 하는 것이 그 목적이다.

그로 인해 창출되는 결과(효과)는 하위 클래스들은 모두가 특정 기능(메소드)이 다 공통적으로 존재하게 된다는 점이다. 따라서 사용자는 어떤 하위 클래스를 사용하더라도 동일한 사용자 인터페이스를 경험하게 되는 것이다.


따라서 abstract 클래스와 유사하게 interface는 그 이름 그대로 이 interface를 상속받는(구현하는 implements하는) 하위 클래스들은 모두가 공통된 기능들을 갖추게 된다. 즉 interface에 있는 메소드들을 모든 하위 클래스들은 모두다 그 기능을(그 메소드를) 만들어야 하므로 사용자는 특정 interface를 상속받은(implements한) 모든 하위 클래스들에서는 동일한 사용자 인터페이스를 경험하게될 것이다.

이것이 interface가 노리는 목적이다.





Android strings.xml에 HTML 코드 넣기

의료기 개발을 하면서 측정 결과를 공유하기 위해 

XML 형태로 전송하거나 

HTML 형태로 전송하거나 

혹은 PDF로 전송하는 등 다양한 형태의 파일로 

공유하는 작업을 하는 중에 

HTML형태의 파일로 저장하는 작업을 하면서 소스 코드 상에서 복잡한 HTML 코드를 집어 넣기에는 뭐시기해서 strings.xml을 이용하기 했다.

근데 문제는...


Android의 strings.xml에 HTML 코드를 그대로 넣으면 컴파일 단계에서 에러가 발생한다.


<string name="HtmlCSS">

<!DOCTYPE html>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<title>Meditab Results</title>

<style type="text/css">

body {

font-family: Arial, Verdana, sans-serif;

font-size: 90%;

color: #666;

background-color: #f8f8f8;}

table {

border-spacing: 0px; }

... 이하 생략 ...


이 문제를 해결할려면 다음과 같이 바꿔 주어야 된다.


   &lt;!DOCTYPE html&gt;

&lt;html&gt;

&lt;head&gt;

      &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"&gt;

...이하 생략...


보통 번거로운일이 아니다. ;;;

이런 문제를 한방에 해결할수 있는 방법이 있다. 다음과 같이


<![CDATA[

  이 안에 HTML 코드를 있는 그대로 넣어주면 해결된다.

]]>


이때 string 항목에 속성을 하나 추가해 주어야 하는데 formatted="false"이다.

아래는 전체 예제이다.


<string name="HtmlCSS" formatted="false">

   <![CDATA[

   <!DOCTYPE html>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<title>Meditab Results</title>

<style type="text/css">

body {

font-family: Arial, Verdana, sans-serif;

font-size: 90%;

color: #666;

background-color: #f8f8f8;}

table {

border-spacing: 0px; }

th, td {

padding: 5px 30px 5px 10px;

border-spacing: 0px;

font-size: 90%;

margin: 0px;

}

th, td {

text-align: left;

background-color: #e0e9f0;

border-top: 1px solid #f1f8fe;

border-bottom: 1px solid #cbd2d8;

border-right: 1px solid #cbd2d8;}

tr.head th {

color: #fff;

background-color: #90b4d6;

border-bottom: 2px solid #547ca0;

border-right: 1px solid #749abe;

border-top: 1px solid #90b4d6;

text-align: center;

text-shadow: -1px -1px 1px #666666;

letter-spacing: 0.15em;}

td {

text-shadow: 1px 1px 1px #ffffff;

}

tr.even td, tr.even th {

background-color: #e8eff5;}

tr.head th:first-child {

-webkit-border-top-left-radius: 8px;

-moz-border-radius-topleft: 8px;

border-top-left-radius: 8px;}

tr.head th:last-child {

-webkit-border-top-right-radius: 8px;

-moz-border-radius-topright: 8px;

border-top-right-radius: 8px;}

fieldset {

width: 310px;

margin-top: 20px;

border: 1px solid #d6d6d6;

background-color: #ffffff;

line-height: 1.6em;}

legend {

font-style: italic;

color: #666666;}

.title {

float: left;

width: 160px;

clear: left;}

.submit {

width: 310px;

text-align: right;}

.results {

float: left;

}

</style>

</head>

<body>

<h1>Meditab Test Results</h1>

<h2>&nbsp;</h2>

<h2>개인정보 </h2>

<table>

<tr class="head">

<th>이름</th>

<th>측정일시</th>

<th>생년월일</th>

<th>성별</th>

<th>E-mail</th>

<th>진료담당</th>

</tr>

<tr> 

<th>홍길동</th>

<td>2016. 3. 5</td>

<td>1978. 5. 17</td>

<td>남</td>

<td>aslsd@naver.com</td>

<td>Dr. Park</td>

</tr>

</table>

<p />

<h2>&nbsp;</h2>

<h2>측정결과</h2>

<div >

<table class="results"> 

<tr class="head">

<th>ECG(심전도)</th>

<th>측정결과</th>

</tr>

<tr>

<td>Heart Rate</td>

<td>84</td>

</tr>

<tr>

<td>Respiratory Rate</td>

<td>19</td>

</tr>

<tr>

<td>ST Level(mV)</td>

<td>18</td>

</tr>

<tr>

<td>Arrythmia</td>

<td>Normal</td>

</tr>

</table>

<p />

<table class="results differ" > 

<tr class="head">

<th>NIBP(혈압)</th>

<th>측정결과</th>

</tr>

<tr>

<td>Systolic</td>

<td>120</td>

</tr>

<tr>

<td>Diastolic</td>

<td>84</td>

</tr>

<tr>

<td>Mean</td>

<td>96</td>

</tr>

<tr>

<td>&nbsp;</td>

<td>&nbsp;</td>

</tr>

</table>

<p />

<table class="results"> <!-- SPO2 -->

<tr class="head">

<th>SPO2(혈중 산소포화도)</th>

<th>측정결과</th>

</tr>

<tr>

<td>Saturation Value</td>

<td>100</td>

</tr>

<tr>

<td>Pulse Rate</td>

<td>84</td>

</tr>

<tr>

<td>&nbsp;</td>

<td>&nbsp;</td>

</tr>

<tr>

<td>&nbsp;</td>

<td>&nbsp;</td>

</tr>

</table>

<p />

<table class="results differ"> <!-- Temp -->

<tr class="head">

<th>Temp(체온)</th>

<th>측정결과</th>

</tr>

<tr>

<td>Temperature</td>

<td>36.3</td>


</tr>

<tr>

<td>&nbsp;</td>

<td>&nbsp;</td>

</tr>

<tr>

<td>&nbsp;</td>

<td>&nbsp;</td>

</tr>

<tr>

<td>&nbsp;</td>

<td>&nbsp;</td>

</tr>

</table>

</div>

<p/>

</body>

</html>

   ]]>

</string>





구글이 제공하는 안드로이드의 

Material design incon들이 

나름 산뜻한 느낌을 주는 디자인들이다.

이런 아이콘들을 앱에 사용하고자 한다면 

다음 사이트에서 해당 아이콘들을 각각 다운로드 받을수 있다.


https://material.io/tools/icons/?style=baseline



이 중에서 특정 아이콘을 다운로드 받고자한다면 해당 아이콘을 클릭하면 다음과 같은 다운로드 선택 창이 하단에 보이게 된다.




위의 이미지에서 파란색 띠가 보이고 원하는 것을 선택하면 된다. 

선택할 수 있는 내용은 


 -. 이미지 크기(위의 경우는 23dp. 18dp, 24dp, 36dp, 48dp의 4종류가 있다)

 -. 이미 색상(black, white)

 -. 파일의 종류(svg, png형태)


예를들어서 Dark Action Bar의 경우는 balck 아이콘을 사용하면 아이콘이 보이지 않을 것이다. 따라서 이럴 경우는 white를 체크해서 다운 받으면 된다.

다운 받는 이미지 파일의 형태도 svg와 png 형태를 선택할 수 있다.







현재의 디바이스가 BLE(Bluetooth Low Energy)를 지원하는지 확인하는 법


일단은 Manifest파일에 아래의 permission을 추가해야한다.


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


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


    <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>


BLE가 지원되는 여부를 파악하기 위해서는 PackageManager의 도움을 받으면 된다.

PackageManager의 객체는 Context 클래스에 있는 아래 메소드로부터 객체를 획득할수 있다.


public abstract PackageManager getPackageManager ()


그리고 PackageManager 클래스에 있는 아래 메소드를 통해 BLE 지원 여부를 확인할 수 있다.


public abstract boolean hasSystemFeature (String name)


아래는 코드 조각이다.


        PackageManager pkgMan = getPackageManager();

        if (pkgMan.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {

            Toast.makeText(this, "BLE가 지원되는 디바이스 입니다.", 1).show();

        } else {

            Toast.makeText(this, "BLE가 지원되지 않습니다.", 1).show();

        }






xml의 layout_gravity에 대한 숨겨진 규칙


안드로이드 앱 개발을 하다보면 

xml 레이아웃 파일에서 조정하는 작업들이 

생각처럼 잘 안 먹혀 들어갈 때가 있다.

특히 layout_gravity에 대해 작업할 때

그런 경험을 자주 하게 된다.

그 이유는 이런 개념 때문이다.



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

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

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" >

    <Button

           android:id="@+id/btnPlus"

           android:layout_width="wrap_content"

           android:layout_height="wrap_content"

           android:padding="10dip"

           android:text=" 테스트 "

           android:layout_gravity="center"

           android:textSize="25sp" />

</LinearLayout>


LinearLayout의 orientation이 vertical일 경우 

Button의 layout_gravity는 수평 관련 속성만 적용이 된다.

예를 들어 layout_gravity="left" 혹은 center_horizontal 혹은 right와 같이 수평 관련 속성만 적용이 된다.

수직 관련 속성은 적용이 안된다. 

예를 들어서 layout_gravity="bottom", 혹은 top...은 적용이 안된다.


만일 orientation이 horizontal이라면 

Button의 layout_gravity는 수직 관련 속성만 적용이 된다.

bottom, top, center_vertical


이 이야기의 핵심은 LinearLayout에서 첫 번째 방향을 설정했으면(만일 수평이면) 두 번째 방향 설정(layout_gravity)에서는 수평이 상태에서 그 수평 중 어느 위치로 움직일 것인지를 결정하는 것만 허용이 된다는 것이다.

즉 수평이 첫 번째 설정 값이면(LinearLayout에서) 그 수평 중 top에 놓일지, bottom에 놓일지, center_vertical에 놓일지만 결정한다는 뜻이다.



타이틀바 없애고 전체화면, 화면 가로로(혹은 세로로) 고정하는 법


안드로이드 앱 실행시 전체화면으로 보이게 하고, 타이틀 바 없애고 화면을 가로로 고정할려면

소스 코드 상에서도 처리할 수 있으나 아래와 같이 Manifest 파일에서 설정할 수 있다.

소스 코드 상에서는 실행 시점에 처리한다면 Manifest 파일에서 설정하면 컴파일 단계에서 미리 처리를 한다.


AndroidManifest.xml에 다음을 추가


        <activity

            android:name=".TestActivity"

            android:label="@string/app_name"

            android:configChanges="orientation|keyboardHidden"

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

            android:screenOrientation="landscape" >


화면을 전체 화면과 타이틀 바를 없애는 기능이

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

이고


화면을 가로고 고정하는 방법이 

android:screenOrientation="landscape"

이다.

만일 화면을 세로로 고정하고자 한다면 

android:screenOrientation="portrait"

이다.


혹은 앱만이 아닌 화면 자체를 세로로 뒤집을려면

android:screenOrientation="reversePortrait"

과같이 하면 된다.







안드로이드 xml 문서에(예: strings.xml)에 공백을 넣기 위해서 HTML에서 사용하는

&nbsp;를 사용하면 컴파일 단계에서 에러가 발생한다.


&nbsp; ⇒ HTML에서 공백 넣은 이 기호는 에러


이때 공백을 포함한 특수 문자를 넣기 위해서는 아래의 방식으로 처리하면 된다. 


&#160;    ⇒     공백

&lt;          ⇒     <

&gt;         ⇒     >

\t             ⇒     탭

\n            ⇒     한 줄 띄우기

\'             ⇒     apostrophe(예: Tom\'s)



callback 메소드가 있는 클래스의 객체 선언 법


abstract 클래스도 아니고 interface도 아닌 

일반 클래스임에도 콜백 메소드(시스템에 의해 자동 호출되는 메소드)가 있을 경우 abstract 클래스나 interface와 유사한 방식으로 객체를 생성을 할 수 있다. 

아래 두 경우를 보면 모두 일반 클래스이다.


public class Handler extends Object

public class PhoneStateListener extends Object


그런데 이들 클래스 안에 콜백 메서드를 사용하기 위해 다음과 같이 mHandler라는 객체를 다음과 같이 생성한다.


 Handler mHandler = new Handler(){ 

     @Override

     public void handleMessage(Message msg){ //이게 콜백 메소드

         //여기서 원하는 기능 수행

     }

  };


위의 handleMessage() 메소드는 핸들러로 메시가 들어올때 즉 이 핸들러 호출이 있을 때 시스템에 의해 이 메소드 호출된다.



PhoneStateListener mPhoneState = new PhoneStateListener(){

    public void onCallStateChanged(int state, String incomingNumber){ //이게 콜백 메소드이다.

    switch(state){

    case TelephonyManager.CALL_STATE_IDLE :

    txt.append("\n☆ 전화 상태 : 대기 상태");

    break;

    case TelephonyManager.CALL_STATE_OFFHOOK :

    txt.append("\n☆ 전화 상태 : 통화 중");

    break;

    case TelephonyManager.CALL_STATE_RINGING :

    txt.append("\n☆ 전화가 왔습니다 : " + incomingNumber);

    break;

    }

    }

    };





한 프로그램에 여러 Activity가 있을 때 처음 실행되는 Activity 지정하는 법


보통 한 프로그램 안에 여러 개의 Activity가 돌아간다. 

이때 어느 것을 Main으로 설정하는지 다음과 같이 Menifest 파일에 등록해 주면 된다.


아래 적색 부분을 포함하고 있는 Activity가 프로그램 실행 시 화면에 맨 처음 뜨게 된다.

여기서 AndExam.java가 Main으로 등록되어 있다.



        <activity android:name=".AndExam"

                  android:label="@string/app_name">

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>





프로그램을 개발하다 보면 


글자 색깔이나 배경색 등 


컬러 값을 16진수 값으로 설정해야 할 때가 있다.


컬러 값을 미세하게 조정해야 한다거나 등등...


이때 자주 사용하는 사이트이다.


http://www.w3schools.com/tags/ref_colorpicker.asp





+ Recent posts