<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>so__ok.log</title>
        <link>https://velog.io/</link>
        <description>찌끄레기 코딩</description>
        <lastBuildDate>Thu, 16 Feb 2023 16:57:54 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>so__ok.log</title>
            <url>https://velog.velcdn.com/images/so__ok/profile/a6b7ae63-fcd6-477e-bac0-c833bf39644c/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. so__ok.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/so__ok" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[안드로이드 리소스]]></title>
            <link>https://velog.io/@so__ok/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EB%A6%AC%EC%86%8C%EC%8A%A4</link>
            <guid>https://velog.io/@so__ok/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EB%A6%AC%EC%86%8C%EC%8A%A4</guid>
            <pubDate>Thu, 16 Feb 2023 16:57:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>안드로이드에서 제공하는 다양한 리소스의 종류와 활용하는 방법에 대하여 다뤄 보려한다. 안드로이드 앱의 리소스들은 모두 res 폴더 하위에 있어야 하며, 개발자가 임의로 지정한 것이 아니라 리소스별 폴더명이 지정되어 있다. 또한 리소스 폴더 하위에 서브 폴더를 작성할 수도 없다.
※ 각 폴더에 리소스 파일을 추가하는 순간, 추가한 리소스를 식별하기 위한 int형 변수가 R.java 파일에 추가된다. 이때 파일명을 변수명으로 사용하므로 리소스 파일명은 자바 명명규칙을 위배할 수 없다. </p>
</blockquote>
<h2 id="◼︎-리소스-종류">◼︎ 리소스 종류</h2>
<pre><code>▪︎ drawable: 이미지, 이미지와 관련된 XML, 그림을 표현한 XML
▪︎ layout: 화면 UI를 정의한 레이아웃 XML
▪︎ values: 문자열, 색상, 크기 등 여러 가지 값
▪︎ menu: 액티비티의 메뉴를 구성하기 위한 XML
▪︎ xml: 특정 폴더가 지정되어 있지 않은 기타 XML
▪︎ anim: 애니메이션을 위한 XML
▪︎ raw: 바이트 단위로 직접 이용되는 이진 파일
▪︎ mipmap: 앱 아이콘 이미지</code></pre><h2 id="◼︎-리소스-활용">◼︎ 리소스 활용</h2>
<h3 id="애니메이션-리소스">애니메이션 리소스</h3>
<p>안드로이드 앱에서는 화면전환 애니메이션이 가장 대표적으로 쓰인다. </p>
<p>애니메이션 리소스 XML 파일이 위치하는 폴더는 res 하위에 anim이라는 폴더이다. </p>
<pre><code>&lt;set xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
    &lt;translate/&gt;
    &lt;rotate/&gt;
    &lt;alpha/&gt;
    &lt;scale/&gt;
&lt;/set&gt;</code></pre><p>애니메이션 리소스 XML 파일이 위치하는 폴더는 res 하위에 anim폴더이다.
XML 파일 내에서 애니메이션을 위와 같은 태그를 이용하여 지정한다. </p>
<p>루트는 set이며, 하위에 scale, rotate, alpha, translate 태그를 이용하여 애니메이션 효과를 지정할 수 있다. </p>
<pre><code>∙ scale: 크기 변경 애니메이션. 크기 확대/축소
∙ rotate: 회전 애니메이션
∙ alpha: 투명도 조정 애니메이션
∙ translate: 이동 애니메이션</code></pre><p>set 태그 하위 중 필요한 것만 적용하면 된다. </p>
<p>► 예시</p>
<pre><code>&lt;set
    xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:shareinterpolator=&quot;true&quot;&gt;
    &lt;scale
        android:fromXScale=&quot;0.0&quot;
        android:toXScale=&quot;1.0&quot;
        android:fromYScale=&quot;0.0&quot;
        android:toYScale=&quot;1.0&quot;
        android:pivotY=&quot;50%&quot;
        android:pivotX=&quot;50%&quot;
        android:startOffset=&quot;0&quot;
        android:duration=&quot;2000&quot; /&gt;
    &lt;alpha
        android:fromAlpha=&quot;0.0&quot;
        android:toAlpha=&quot;1.0&quot;
        android:startOffset=&quot;0&quot;
        android:duration=&quot;2000&quot; /&gt;
&lt;/set&gt;</code></pre><p>위의 XML 코드는 크기와 투명도가 바뀌는 애니메이션 효과이다. 
애니메이션을 XML로 정의할 때 공통 속성은 duration과 startOffset 이다.</p>
<pre><code>∙ duration: 지정한 애니메이션을 지속 시간 설정. &quot;2000&quot;=2초
∙ startOffset: 애니메이션 시작 후 얼마 후에 적용할 것인지. 
        예시) &quot;0&quot;=시작하자 마자, &quot;500&quot;=시작 후 0.5초 후부터
∙ repeatCount: 애니메이션 반복 횟수.
        예시) &quot;infinite&quot;=무한반복, &quot;2&quot;=2번 더 반복된 후 멈춤.
∙ repeatMode: 애니메이션 반복 방향.
        예시) &quot;restart&quot;=지정한 애니메이션 효과가 그대로 다시 적용.
             &quot;reverse&quot;=반대 방향으로 반복.
</code></pre><p>각 애니메이션 태그에 들어가는 중요 속성들은 android developers에서 확인하면 될 것 같다. </p>
<p>위 처럼 XML로 지정한 애니메이션을 자바 코드에 적용하면 된다. </p>
<pre><code>Animation anim = AnimationUtils.loadAnimation(this, R.anim.in);
imageView.startAcimation(anim);</code></pre><p>Animation 객체로 in이라는 XML 파일을 지칭하여 뷰의 startAnimation() 함수는 이용해 적용한다. 위의 코드만으로 XML에 정의된 애니메이션 효과는 나타난다. </p>
<p>때에 따라서 애니메이션이 적용된 순간이나 끝난 순간의 이벤트를 처리해야 하는 경우도 있는데 예시를 들어보며 알아보자.</p>
<p>► 예시
<img src="https://velog.velcdn.com/images/so__ok/post/afbf2ce2-7631-4fec-97d6-c76794d3fa85/image.png" alt=""></p>
<blockquote>
<p>첨부) 깡쌤의 안드로이드 프로그래밍 책</p>
</blockquote>
<p>위 사진의 첫 번째 애니메이션은 화면에 보이지 않던 ImageView 가 Alpha 값과 Scale이 바뀌면서 원래의 크기대로 화면에 나타나고, 이 애니메이션이 끝나자 마자 두 번째 애니메이션이 적용되어 ImageView가 하단으로 슬라이드되면서 사라지는 예이다. 이를 구현하려면 애니메이션 XML 파일을 두 개 만들어서 첫 번째 애니메이션이 끝나는 순간에 두 번째 애니메이션을 적용해야 하는데, 이럴 때 애니메이션과 관련된 이벤트가 필요하다. </p>
<pre><code>anim.setAdroidlistener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {
    }
    @Override
    public void onAnimationEnd(Animation animation) {
    }
    @Override
    public void onAnimationRepeat(Animation animation) {
    }
});</code></pre><p>Animation 객체에 setAnimationListener로 이벤트를 등록하면 애니메이션 시작(start)되는 순간과 끝나는(End) 순간, 그리고 반복(repeat)되는 순간 각 함수가 실행된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[터치, 키 이벤트]]></title>
            <link>https://velog.io/@so__ok/%ED%84%B0%EC%B9%98-%ED%82%A4-%EC%9D%B4%EB%B2%A4%ED%8A%B8</link>
            <guid>https://velog.io/@so__ok/%ED%84%B0%EC%B9%98-%ED%82%A4-%EC%9D%B4%EB%B2%A4%ED%8A%B8</guid>
            <pubDate>Thu, 09 Feb 2023 12:47:44 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><img src="https://velog.velcdn.com/images/so__ok/post/78789e72-db0d-42e9-b404-3214426d1667/image.png" alt="">
델리게이션 이벤트 모델로 구현한 동일한 화면을 하이어라키 이벤트 모델로 구현하 보자.</p>
</blockquote>
<p>자바 코드</p>
<pre><code>public class HierarchyEventModel extends AppCompatActivity implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {

    TextView bellTextView;
    TextView labelTextView;
    CheckBox repeatCheckView;
    CheckBox vibrateCheckView;
    Switch switchView;

    float initX;
    long initTime;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hierarchy_event_model);

        bellTextView = findViewById(R.id.bell_name);
        labelTextView = findViewById(R.id.label);
        repeatCheckView = findViewById(R.id.repeatCheck);
        vibrateCheckView = findViewById(R.id.vibrate);
        switchView = findViewById(R.id.onOff);

        bellTextView.setOnClickListener(this);
        labelTextView.setOnClickListener(this);

        repeatCheckView.setOnCheckedChangeListener(this);
        vibrateCheckView.setOnCheckedChangeListener(this);
        switchView.setOnCheckedChangeListener(this);
    }
    //    OnTouchEvent() 함수 추가, 왼쪽, 오른쪽으로 밀었는지 확인

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            initX = event.getRawX();
        }else if (event.getAction() == MotionEvent.ACTION_UP) {
            float diffX = initX-event.getRawX();
            if (diffX&gt;30){
                showToast(&quot;왼쪽으로 화면을 밀었습니다.&quot;);
            }else if (diffX&lt;-30){
                showToast(&quot;오른쪽으로 화면을 밀었습니다.&quot;);
            }
        }return  true;
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (System.currentTimeMillis() - initTime&gt;3000) {

                showToast(&quot;종료할려면 한번 더 누르세요.&quot;);
                // 현재 시간 저장
                initTime = System.currentTimeMillis();
            }else {
                // 3초 이내에 Back button이 두 번 눌린 경우. Activity 종료
                finish();
            }
            return  true;
        }
        return  super.onKeyDown(keyCode, event);
    }



    private void showToast(String message) {
        Toast toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
        toast.show();
    }


    @Override
    public void onClick(View v) {
        if (v == bellTextView) {
            showToast(&quot;bell text click event...&quot;);
        }else if (v == labelTextView) {
            showToast(&quot;label text click event&quot;);
        }
    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if (buttonView == repeatCheckView) {
            showToast(&quot;repeat checkbox is&quot;+ isChecked);
        }else if (buttonView == vibrateCheckView){
            showToast(&quot;vibrate checked is&quot;+ isChecked);
        }else if (buttonView == switchView) {
            showToast(&quot;switch is&quot;+ isChecked);
        }
    }

}
</code></pre><p>onTouchEvent() 함수를 추가해서 화면을 왼쪽, 오른쪽으로 밀었는지 확인하는 코드를 작성하였다. 또한 onKeyDown() 함수를 추가해서 뒤로가기 버튼을 두 번 누르면 종료하는 로직을 작성하였다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[하이어라키 이벤트 모델]]></title>
            <link>https://velog.io/@so__ok/%ED%95%98%EC%9D%B4%EC%96%B4%EB%9D%BC%ED%82%A4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%AA%A8%EB%8D%B8</link>
            <guid>https://velog.io/@so__ok/%ED%95%98%EC%9D%B4%EC%96%B4%EB%9D%BC%ED%82%A4-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%AA%A8%EB%8D%B8</guid>
            <pubDate>Thu, 09 Feb 2023 12:23:04 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>델리게이션 이벤트 모델에서 다뤘다 싶이 안드로이드의 이벤트는 델리게이션과 하이어라키 크게 두 가지로 나뉜다.  이번에는 하이어라키에 대해 알아보려 한다. </p>
</blockquote>
<p>하이어라키 이벤크 모델(Hierarchy Event Model)은 액티비티가 화면에 출력되었을 때 발생하는 사용자의 키 이벤트와 화면 터치 이벤트를 처리하기 위한 모델이다.</p>
<p>액티비티에서 터치 이벤트와 키 이벤트를 직접 처리하고 싶다면 이벤트 발생 시 자동 호출되는 함수만 액티비티 내에 재정의하면 된다. </p>
<h2 id="◼︎-터치-이벤트">◼︎ 터치 이벤트</h2>
<p>액티비티에 보이는 내용을 사용자가 손가락으로 조작하는 일, 화면을 상하좌우 어떤 방향으로 밀어냈는지를 알아 낼 때 터치 이벤트로 처리한다. </p>
<p>터치 이벤트가 발생할 때 콜백 함수를 액티비티 내에 정의하여 이벤트를 처리한다. </p>
<pre><code>@Override
public boolean onTouchEvent(MotionEvent event) {
    return super.onTouchEvent(event);
</code></pre><p>onTouchEvent 메서드가 호출되는 터치 이벤트는 3가지 타입이 있으며, 이 메서드의 매개변수로 식별해서 사용할 수 있다. </p>
<pre><code>∙ ACTION_DOWN: 화면에 터치된 순간의 이벤트
∙ ACTION_UP: 터치를 떼는 순간의 이벤트
∙ ACTION_MOVE: 터치한 후 이동하는 순간의 이벤트</code></pre><p>밑의 함수 모두 터치 이벤트가 발생한 지점의 x, y 좌푯값을 얻는 데 사용한다. getX(), getY 함수는 이벤트가 발생한 뷰 내에서의 좌푯값을 반환하며, getRawX(), getRawY() 함수는 화면에서 좌푯값을 반환한다. </p>
<pre><code>∙ getX()
∙ getY()
∙ getRawX()
∙ getRawY()</code></pre><p>▶︎ 예시</p>
<pre><code>@Override
public boolean onTouchEvent(MotionEvent event) {
    if(event.getAction()==MotionEvent.ACTION_DOWN){
        initX=event.getRawX();
    }
    return true;
}    </code></pre><h2 id="◼︎-키-이벤트">◼︎ 키 이벤트</h2>
<p>앱에서 키 이벤트 처리는 뒤로가기가 대부분이지만 기종에 따라 메뉴, 검색, 오버뷰 버튼 등이 제공될 수 있으며 홈과 전원, 오버뷰 버튼은 일반 애플리케이션에서 이벤트로 제어할 수 없다. </p>
<p>예를 들어, 뒤로가기 버튼을 눌렀을 때, &quot;정말 종료하시겠습니까?&quot; 또는 &quot;종료하려면 한 번 더 누르세요.&quot; 라는 메세지를 띄우는 작업은 키 이벤트를 처리해서 작성한다. </p>
<p>액티비티 내에서 키 이벤트를 처리하려면 키 이벤트가 발생할 때 호출되는 이벤트 함수를 액티비티 내에 정의만 하면 된다. </p>
<pre><code>∙ onKeyDown: 키가 눌린 순간의 이벤트
∙ onKeyUp: 키를 떼는 순간의 이벤트
∙ onKeyLongPress: 키를 오래 누르는 순간의 이벤트
</code></pre><pre><code>@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    return super.onKeyDown(keyCode, event);
    }</code></pre><p>매개변수로 keyDown 값이 전달되어 어느 버튼을 누른 건지 식별할 수 있다. </p>
<p>▶︎ 예시</p>
<pre><code>@Override
public boolean onKeyDown(int keycode, KeyEvent event) {
    if(keyCode==KeyEvent.KEYCODE_BACK){
    }
    return super.onKeyDown(ketCode, event);
 }</code></pre><p>onKeyDown() 메서드 이외에 뒤로가기 버튼 이벤트를 처리할 수 있는 메서드가 하나 더 있다. onBackPressed() 메서드를 이용해서 처리할 수도 있다. onBackPressed() 하무는 뒤로가기 버튼 제어만을 목적으로 만들어졌기 때문에 다른 키 이벤트는 처리할 수 없다. </p>
<pre><code>@Override
public void onBackPressed() {
    super.onBackPressed();
    }</code></pre><p>🟥 키 이벤트는 키보드에서 키를 누른 순간의 이벤트 처리라고 생각하는 경우가 있는데, 안드로이드 소프트 키보드는 키 이벤트로 처리할 수 없다. </p>
<blockquote>
<p>✏️ 이게 뭐라고 정리하는데 사십분이 걸려..후..
이렇게 다 작성해야 캐나다 갈 때 짐이 하나라도 줄지..ㅎ </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[델리게이션 이벤트 모델<Delegation Event Model>]]></title>
            <link>https://velog.io/@so__ok/%EB%8D%B8%EB%A6%AC%EA%B2%8C%EC%9D%B4%EC%85%98-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%AA%A8%EB%8D%B8Delegation-Event-Model</link>
            <guid>https://velog.io/@so__ok/%EB%8D%B8%EB%A6%AC%EA%B2%8C%EC%9D%B4%EC%85%98-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%AA%A8%EB%8D%B8Delegation-Event-Model</guid>
            <pubDate>Thu, 09 Feb 2023 11:32:01 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><img src="https://velog.velcdn.com/images/so__ok/post/77a0c236-8283-4d78-a958-5a41b0deaa85/image.png" alt="">
오늘 구현해 볼 알람 화면이다. 이벤트 처리 로직은 실제 앱에서 제공하는 기능을 이용하지 않고, 단순 토스트 문자열로 이벤트를 확인하는 정도로 처리하려 한다. </p>
</blockquote>
<p>액티비티 클래스 자체를 이밴트 핸들러로 만들기 위해 두 개의 인터페이스를 클래스 선언 부분에 상속받고 인터페이스의 추상 함수를 재정의 했다. </p>
<p>클릭 이벤트의 콜백 함수인 onClick() 함수 그리고, CheckBox와 Switch의 상태 변경 이벤트에서 호출되는 onCheckedChange() 함수를 구현하였다. </p>
<p>자바 코드</p>
<pre><code>public class DelegationEventModel extends AppCompatActivity implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {

    TextView bellTextView;
    TextView labelTextView;
    CheckBox repeatCheckView;
    CheckBox vibrateCheckView;
    Switch switchView;

    float initX;
    long initTime;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_delegation_event_model);

        bellTextView = findViewById(R.id.bell_name);
        labelTextView = findViewById(R.id.label);
        repeatCheckView = findViewById(R.id.repeatCheck);
        vibrateCheckView = findViewById(R.id.vibrate);
        switchView = findViewById(R.id.onOff);

        bellTextView.setOnClickListener(this);
        labelTextView.setOnClickListener(this);

        repeatCheckView.setOnCheckedChangeListener(this);
        vibrateCheckView.setOnCheckedChangeListener(this);
        switchView.setOnCheckedChangeListener(this);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            initX = event.getRawX();
        }else if (event.getAction() == MotionEvent.ACTION_UP) {
            float diffX = initX-event.getRawX();
            if (diffX&gt;30){
                showToast(&quot;왼쪽으로 화면을 밀었습니다.&quot;);
            }else if (diffX&lt;-30){
                showToast(&quot;오른쪽으로 화면을 밀었습니다.&quot;);
            }
        }return  true;
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (System.currentTimeMillis() - initTime&gt;3000) {
                showToast(&quot;종료할려면 한번 더 누르세요.&quot;);
                initTime = System.currentTimeMillis();
            }else {
                finish();
            }
            return  true;
        }
        return  super.onKeyDown(keyCode, event);
    }



    private void showToast(String message) {
        Toast toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
        toast.show();
    }


    @Override
    public void onClick(View v) {
        if (v == bellTextView) {
            showToast(&quot;bell text click event...&quot;);
        }else if (v == labelTextView) {
            showToast(&quot;label text click event&quot;);
        }
    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if (buttonView == repeatCheckView) {
            showToast(&quot;repeat checkbox is&quot;+ isChecked);
        }else if (buttonView == vibrateCheckView){
            showToast(&quot;vibrate checked is&quot;+ isChecked);
        }else if (buttonView == switchView) {
            showToast(&quot;switch is&quot;+ isChecked);
        }
    }
}</code></pre><p>레이아웃 XML</p>
<pre><code>&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.DelegationEventModel&quot;
    android:background=&quot;#283593&quot;&gt;

    &lt;RelativeLayout
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:background=&quot;#3b479d&quot;
        android:paddingBottom=&quot;16dp&quot;&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/ampm&quot;
        android:text=&quot;오전&quot;
        android:textColor=&quot;#FFFFFF&quot;
        android:layout_marginTop=&quot;56dp&quot;
        android:layout_marginLeft=&quot;16dp&quot;/&gt;

        &lt;TextView
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:id=&quot;@+id/time&quot;
            android:text=&quot;5:40&quot;
            android:textSize=&quot;40dp&quot;
            android:textColor=&quot;#FFFFFF&quot;
            android:layout_marginLeft=&quot;16dp&quot;
            android:layout_toRightOf=&quot;@+id/ampm&quot;
            android:layout_alignBaseline=&quot;@+id/ampm&quot;/&gt;

        &lt;Switch
            android:id=&quot;@+id/onOff&quot;
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:layout_alignParentRight=&quot;true&quot;
            android:layout_alignBottom=&quot;@+id/time&quot;
            android:buttonTint=&quot;#FF0000&quot;/&gt;

        &lt;CheckBox
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:id=&quot;@+id/repeatCheck&quot;
            android:text=&quot;반복&quot;
            android:textColor=&quot;#FFFFFF&quot;
            android:layout_below=&quot;@+id/ampm&quot;
            android:layout_alignLeft=&quot;@+id/ampm&quot;
            android:layout_marginTop=&quot;16dp&quot;
            android:buttonTint=&quot;#FFFFFF&quot;/&gt;

        &lt;ImageView
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:id=&quot;@+id/bell&quot;
            android:src=&quot;@drawable/bell&quot;
            android:layout_below=&quot;@+id/repeatCheck&quot;
            android:layout_alignLeft=&quot;@+id/ampm&quot;
            android:layout_marginTop=&quot;16dp&quot;/&gt;
        &lt;TextView
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:id=&quot;@+id/bell_name&quot;
            android:text=&quot;Helium&quot;
            android:textColor=&quot;#FFFFFF&quot;
            android:layout_toRightOf=&quot;@+id/bell&quot;
            android:layout_alignBottom=&quot;@+id/bell&quot;/&gt;

        &lt;TextView
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:id=&quot;@+id/label&quot;
            android:text=&quot;라벨&quot;
            android:textColor=&quot;#FFFFFF&quot;
            android:layout_below=&quot;@+id/bell&quot;
            android:layout_marginTop=&quot;16dp&quot;
            android:layout_alignLeft=&quot;@+id/ampm&quot;/&gt;

        &lt;CheckBox
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:id=&quot;@+id/vibrate&quot;
            android:text=&quot;진동&quot;
            android:textColor=&quot;#FFFFFF&quot;
            android:layout_alignParentRight=&quot;true&quot;
            android:layout_alignBaseline=&quot;@+id/bell_name&quot;
            android:layout_marginBottom=&quot;16dp&quot;
            android:layout_marginRight=&quot;16dp&quot;
            android:buttonTint=&quot;#FFFFFF&quot;/&gt;

&lt;/RelativeLayout&gt;

&lt;/LinearLayout&gt;</code></pre><blockquote>
<p>✏️  공부하면서 항상 느끼는 건데,  코드 다 치고 런 돌리니까 에뮬레이터에  에러떠서 뭔가 했는데.. 토스트 잘 못 적어서 ㅎ,, 진짜 한시간 동안 머리 싸매고 뭐가 문젠데..또.. 이러고 있었는데 너무 허접한 문제라서 실소만 나온다 ㅋㅋ</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[델리게이션 이벤트 모델]]></title>
            <link>https://velog.io/@so__ok/%EB%8D%B8%EB%A6%AC%EA%B2%8C%EC%9D%B4%EC%85%98-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%AA%A8%EB%8D%B8</link>
            <guid>https://velog.io/@so__ok/%EB%8D%B8%EB%A6%AC%EA%B2%8C%EC%9D%B4%EC%85%98-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%AA%A8%EB%8D%B8</guid>
            <pubDate>Mon, 06 Feb 2023 12:54:57 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>스마트폰 화면에서 발생하는 이벤트는 델리게이션 이벤트 모델(Delegation Event Model)과 하이어라키 이벤트 모델(Hierarchy Event Model) 크게 두 가지이다. 
델리게이션 이벤트 모델은 뷰에서 발생하는 이벤트를 처리하기 위한 모델이며, 하이어라키 이벤트 모델은 액티비티에서 발생하는 사용자의 터치나 키 이벤트를 직접 처리하기 위한 모델이다. </p>
</blockquote>
<h2 id="◼︎-이벤트-프로그램-구조">◼︎ 이벤트 프로그램 구조</h2>
<p>델리게이션 이벤트 모델은 이벤트 소스와 이벤트 핸들러를 리스너(Listener)로 연결하여 처리하는 구조이다. </p>
<pre><code>∙ 이벤트 소스: 이벤트가 발생한 뷰 객체
∙ 이벤트 핸들러: 이벤트 처리 내용을 가지는 객체
∙ 리스너: 이벤트 소스와 이벤트 핸들러를 연결하는 작업</code></pre><p>객체지향프로그래밍에서 명료성은 중요한 목적중 하나이다. </p>
<p>만약 구성한 화면에 여러가지 뷰가 있다고 가정해 보자. 터치를 하여 이벤트가 발생하는데, 이때 여러 개의 뷰중 어떤 것을 터치하였는지, 터치한 뷰의 목적은 무엇인지 명료하게 알 수 없기 때문에 터치 이벤트로 처리하는 것이 아닌 이벤트가 발생하는 뷰를 직접 지칭하여 각 이벤트의 성격별로 이벤트 이름을 다르게 처리하여 명료성을 높이는 것이다. </p>
<p>이처럼 델리게이션 이벤트 모뎅은 이벤트가 발생한 객체를 명료하게 지칭하고자 이벤트 소스를 사용하고, 이벤트 성격을 명료하게 지칭하고자 리스너를 사용한다. </p>
<p><img src="https://velog.velcdn.com/images/so__ok/post/061e73ad-c4da-434f-a57a-41bc97c2e8cd/image.png" alt=""></p>
<p>위의 그림처럼 vibrateCheckBox 객체에서 CheckedChangeEvent가 발생하면 어느 객체에서 이벤트가 처리된다는 것을 명료하게 연결하여 처리할 수 있다. </p>
<p>코드로 살펴보면</p>
<pre><code>vibrateCheckView.setOnCheckedChangeListener(new MyEventHandler());</code></pre><p>안드로이드 프로그램을 작성하면서 &quot;setOnXXXListener()&quot; 와 같은 구문을 많이 보게되는데, 이 부분이 이벤트 소스와 이벤트 핸들러를 리스너로 연결하는 부분이다.</p>
<p>코드를 해석하자면 vibrateCheckView 객체에서 CheckedChangeEvent가 발생하면 MyEventHandler라는 클래스 객체를 실행하여 이벤트를 처리하라는 의미이다. 
이 때 이벤트 핸들러로 지정한 MyEventHandler는 개발자가 만든 클래스이며, 이벤트 핸들러 클래스는 꼭 지정된 인터페이스를 구현해야 한다. </p>
<pre><code>class MyEventHandler implements CompoundButton.OnCheckedChangeListener{
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    }
}</code></pre><p>OnCheckedChangeListener 인터페이스를 구현하고, 추상 함수를 재정의(Override)하여 이벤트처리 로직을 작성해 놓으면 실제 이벤트가 발생할 때 해당 함수가 실행된다. </p>
<h2 id="◼︎-다양한-이벤트-처리">◼︎ 다양한 이벤트 처리</h2>
<p>안드로이드에서 사용하는 주요 이벤트를 정리했다. </p>
<pre><code>∙ OnClickListener: 뷰 클릭 시 발생하는 이벤트
∙ OnLongClickListener: 뷰를 오래 클릭했을 때 발생하는 이벤트
∙ OnCheckedChangeListener: CheckBox의 상태 변경 이벤트
∙ OnItemClickListener: ListView의 항목 선택 이벤트
∙ OnDateSetListener: DatePicker의 날짜 선택 이벤트
∙ OnTimeSetListener: TimePicker의 시간 선택 이벤트 </code></pre><p>안드로이드 버전이 올라가면서 신규로 추가되는 뷰에 맞는 이벤트가 더 추가 될 수 있는데, 많고 많은 뷰와 이벤트가 있지만 델리게이트 이벤트 모델에 대한 이해가 있다면 이벤트를 처리하는 구조는 모두 같다. </p>
<p>이벤트 소스와 이벤트 핸들러를 setOnXXXListener() 함수로 연결하고, 이벤트 핸들러는 OnXXXListener를 구현하여 작성하면 되는 것이다. </p>
<p>OnClickListener는 모든 뷰에 적용할 수 있는 이벤트로 Button, TextView, ImageView 등 모든 뷰에 등록이 가능하며 OnLongClickListener 이벤트 역시 클릭 이벤트와 더불어 모든 뷰에 등록할 수 있다. </p>
<blockquote>
<p>✏️ 오늘  총 세개의 벨로그를 올렸다~! 
간단한 구조만 알고있었는데 오늘 글을 쓰면서 확실하게 정리가 된 것 같다.
내일하고 모래는 일하니까 아마 목요일에 다시 쓸 수 있을 듯하다.
그때를 위해 오늘은 여기까지....엽떡 먹엉지</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[다이얼로그 ]]></title>
            <link>https://velog.io/@so__ok/%EB%8B%A4%EC%9D%B4%EC%96%BC%EB%A1%9C%EA%B7%B8</link>
            <guid>https://velog.io/@so__ok/%EB%8B%A4%EC%9D%B4%EC%96%BC%EB%A1%9C%EA%B7%B8</guid>
            <pubDate>Mon, 06 Feb 2023 12:01:42 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>전에 올렸던 내용을 기반으로 다이얼로그를 구성해 보고자 한다. 
전에 다이얼로그는 앱을 이용하고 있을 때 다양한 상황을 알리기 위해 사용한다고 했는데, 토스트, 알림 창, 목록, 날짜 선택, 시간 선택, 커스텀 다이얼로그 모두 구현해 보고자 한다. <img src="https://velog.velcdn.com/images/so__ok/post/ce8e4120-13bf-472a-ac3b-4a3b87ebfc20/image.png" alt="">
우선 기본 화면이며 각 버튼을 누르면 그에 맞는 화면이 제공된다. </p>
</blockquote>
<h3 id="자바-코드">자바 코드</h3>
<pre><code>public class dialoglayout extends AppCompatActivity implements View.OnClickListener {

    Button alertBtn;
    Button listBtn;
    Button dateBtn;
    Button timeBtn;
    Button customDialogBtn;

//    이벤트 처리를 위해 dialog 객체를 멤버변수로 선언
    AlertDialog customDialog;
    AlertDialog listDialog;
    AlertDialog alertDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialoglayout);

//        View 객체 획득
        alertBtn  = findViewById(R.id.btn_alert);
        listBtn = findViewById(R.id.btn_list);
        dateBtn = findViewById(R.id.btn_date);
        timeBtn = findViewById(R.id.btn_time);
        customDialogBtn = findViewById(R.id.btn_custom);

//        버튼 이벤트 등록
        alertBtn.setOnClickListener(this);
        listBtn.setOnClickListener(this);
        dateBtn.setOnClickListener(this);
        timeBtn.setOnClickListener(this);
        customDialogBtn.setOnClickListener(this);
    }
//     매개변수의 문자열을 Toast로 띄우는 개발자 함수
        private void showToast(String message){
            Toast toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
            toast.show();
        }

//        Dialog Button 이벤트 처리
        DialogInterface.OnClickListener dialogListener = new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        if (dialog == customDialog &amp;&amp; which == Dialog.BUTTON_POSITIVE){
            showToast(&quot;custom dialog 확인 click.....&quot;);
        }else if (dialog == listDialog){
//            목록 dialog의 항목이 선택되었을 때 항목 문자열 획득
            String[] datas =  getResources().getStringArray(R.array.dialog_arrays);
            showToast(datas[which]+ &quot;선택 하셨습니다.&quot;);
        }else if (dialog == alertDialog &amp;&amp; which == DialogInterface.BUTTON_POSITIVE){
            showToast(&quot;alert dialog ok click....&quot;);
        }

    }
};

    @Override
    public void onClick(View v) {
        if ( v == alertBtn){
//            알림창을 만들어 주는 Builder 클레스의 객체 생성
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setIcon(android.R.drawable.ic_dialog_alert);
            builder.setTitle(&quot;알림&quot;);
            builder.setMessage(&quot;정말 종료 하시겠습니다.&quot;);
            builder.setPositiveButton(&quot;OK&quot;, dialogListener);
            builder.setNegativeButton(&quot;NO&quot;, null);
//          알림창 생성, 생성된 다이얼로그 화면에 출력
            alertDialog = builder.create();
            alertDialog.show();
        }else if (v == listBtn){
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle(&quot;알람 벨소리&quot;);
            builder.setSingleChoiceItems(R.array.dialog_arrays, 0, dialogListener);
            builder.setPositiveButton(&quot;확인&quot;, null);
            builder.setNegativeButton(&quot;취소&quot;, null);
            listDialog = builder.create();
            listDialog.show();
        }
        else if (v == dateBtn){
//            현재 날짜로 dialog를 띄우기 위해 날짜를 구함성

            Calendar c = Calendar.getInstance();
            int year = c.get(Calendar.YEAR);
            int month = c.get(Calendar.MONTH);
            int day = c.get(Calendar.DAY_OF_MONTH);

            DatePickerDialog dateDialog = new DatePickerDialog(this,
                    new DatePickerDialog.OnDateSetListener(){
                @Override //onDateSet() 함수가 호출되어 매개변수로 선택한 연, 월, 일을 받을 수 있다.
                        public void onDateSet(DatePicker view, int year, int monthOfYear,
                                              int datOfMonth){
                    showToast(year + &quot;:&quot; + (monthOfYear + 1) + &quot;:&quot; + datOfMonth);
                }
                    }, year, month, day);
            dateDialog.show();
        }else if (v == timeBtn){
//            현재 시간으로 Dialog를 띄우기 위해 시간을 구함
            Calendar c = Calendar.getInstance();
            int hour = c.get(Calendar.HOUR_OF_DAY);
            int minute = c.get(Calendar.MINUTE);

            TimePickerDialog timeDialog = new TimePickerDialog(this, new TimePickerDialog.OnTimeSetListener() {

                @Override
                public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
                 showToast(hourOfDay + &quot;:&quot; + minute);
                }
            }, hour, minute, false);
            timeDialog.show();
        }else if (v == customDialogBtn){
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
            View view = inflater.inflate(R.layout.dialog_layout, null);
            builder.setView(view);
            builder.setPositiveButton(&quot;확인&quot;, dialogListener);
            builder.setNegativeButton(&quot;취소&quot;, null);

            customDialog = builder.create();
            customDialog.show();
        }
    }
}</code></pre><h3 id="📂resvaluesarrays">📂res&gt;values&gt;arrays</h3>
<p>목록을 구성하기 위한 문자열을 배열 리소스로 만들기.</p>
<pre><code>&lt;resources&gt;
    &lt;string-array name=&quot;dialog_arrays&quot;&gt;
        &lt;item&gt; 기본 알람 소리&lt;/item&gt;
        &lt;item&gt;Argon&lt;/item&gt;
        &lt;item&gt;Awaken&lt;/item&gt;
        &lt;item&gt;Bounce&lt;/item&gt;
        &lt;item&gt;Carbon&lt;/item&gt;
    &lt;/string-array&gt;
&lt;/resources&gt;</code></pre><h3 id="레이아웃-xml">레이아웃 XML</h3>
<pre><code>&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.dialoglayout&quot;
    android:orientation=&quot;vertical&quot;&gt;

    &lt;Button
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/btn_alert&quot;
        android:text=&quot;alert dialog&quot;/&gt;

    &lt;Button
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/btn_list&quot;
        android:text=&quot;list dialog&quot;/&gt;
    &lt;Button
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/btn_date&quot;
        android:text=&quot;date dialog&quot;/&gt;

    &lt;Button
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/btn_time&quot;
        android:text=&quot;time dialog&quot;/&gt;

    &lt;Button
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/btn_custom&quot;
        android:text=&quot;custom dialog&quot;/&gt;

&lt;/LinearLayout&gt;</code></pre><p>커스텀 다이얼로그의 화면을 구성하는 새 레이아웃 XML 파일을 res/layout 폴더를 오른쪽 마우스로 선택하여 NEW→Layout Resource file 메뉴를 이용하여 생성하고 화면 구성을 한다.</p>
<pre><code>&lt;RelativeLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/text1&quot;
        android:text=&quot;USB 디버깅을 허용하시겠습니까?&quot;
        android:textStyle=&quot;bold&quot;
        android:textSize=&quot;15dp&quot;
        android:layout_marginLeft=&quot;32dp&quot;
        android:layout_marginTop=&quot;32dp&quot;/&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/text2&quot;
        android:text=&quot;컴퓨터 RSA키 지문&quot;
        android:layout_below=&quot;@+id/text1&quot;
        android:layout_alignLeft=&quot;@+id/text1&quot;/&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/text3&quot;
        android:text=&quot;17:AA:BB:77:DD:97:7E&quot;
        android:layout_below=&quot;@+id/text2&quot;
        android:layout_alignLeft=&quot;@id/text2&quot;/&gt;

    &lt;CheckBox
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;이 컴퓨터에서 항상 허용&quot;
        android:layout_below=&quot;@id/text3&quot;
        android:layout_alignLeft=&quot;@+id/text3&quot;/&gt;

&lt;/RelativeLayout&gt;</code></pre><p>위의 코드를 입력하면 밑의 화면이 구성된다. 
<img src="https://velog.velcdn.com/images/so__ok/post/9d7f8cd9-eb21-4511-9918-1459449f9732/image.png" alt=""></p>
<p>이렇게 구현된 화면은
<img src="https://velog.velcdn.com/images/so__ok/post/5d1a49b2-4a82-4a9b-8d7d-9ab293473f96/image.png" alt=""></p>
<p>이러한 결과물이 나온다. </p>
<blockquote>
<p>✏️ 오늘의 두 번쨰 벨로그도 끝.. 이제 캐나다 어학원을 알아봐야 한다. 
캐나다 까지 이 무거운 노트북을 들고 갈 생각하니 좀 막막..하다..
막상 코드로 짜보면 그렇게 어려운 부분도 아닌데 처음에는 왜 그렇게 오류가 났는지..  그럼에도 구구절절한 설명을 적어내는 건 그냥 독서하는 느낌으로 한번 훑고 적어 내리는 식으로 해서 나름 재미도 있고 도중에 궁금한걸 찾을 수 있어서 계속 올리게 되는거 같다. 오늘도 벨로그 끝 내일은 또 일.. 
근데 만들고 있는 앱하고 동행해서 올리는 건 역시 조금 어렵다.. </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[다이얼로그<Dialog>]]></title>
            <link>https://velog.io/@so__ok/%EB%8B%A4%EC%9D%B4%EC%96%BC%EB%A1%9C%EA%B7%B8Dialog</link>
            <guid>https://velog.io/@so__ok/%EB%8B%A4%EC%9D%B4%EC%96%BC%EB%A1%9C%EA%B7%B8Dialog</guid>
            <pubDate>Mon, 06 Feb 2023 11:01:52 GMT</pubDate>
            <description><![CDATA[<h1 id="다이얼로그">다이얼로그</h1>
<p>사용자가 스마트폰에서 앱을 이용하고 있을 때 다양한 상황을 알리기 위해서 다이얼로그(Dialog)를 이용한다. 애플리케이션에서 다이얼로그는 크게 모달(Modal)과 모달리스(Modaless)로 구분하는데, 모달은 다이얼로그를 닫기 전까지 원래의 창을 사용자가 이용할 수 없으며, 모달리스는 다이얼로그가 화면에 떠 있더라도 사용자가 원래의 창을 계속 이용할 수 있다. </p>
<h2 id="◼︎-토스트toast">◼︎ 토스트(Toast)</h2>
<p>안드로이드에서 이용 빈도가 가장 높은 다이얼로그로 화면 하단에 검정 바탕의 흰색 글이 잠깐 보이다가 사라지는 것을 말한다. 모달리스 형식으로 실행되며 사용자를 방행하지 않는다. </p>
<p>다이얼로그와 같은 UI는 제공되지 않지만, 액티비티 화면 위에 띄울 수 있다는 측면에서 다이얼로그의 일종으로 취급한다. </p>
<pre><code>∙ makeText(Context context, int resId, int duration)
∙ makeText(Context context, CharSequence text, int duration)</code></pre><p>토스트는 위의 makeText() 함수를 이용하여 생성한다. 
두 번째 매개변수가 토스토로 띄울 메세지로 문자열이나 문자열 리소스를 지정한다. 
세 번째 매개변수는 토스토로 보이는 메세지의 유지 시간으로  LENGTH_SHORT 이나 LENGTH_LONG으로 지정한다. </p>
<p>LENGTH_SHORT의 실제시간은 3초이며 LENGTH_LONG은 5초이다. 
단, 이 값을 임의의 숫자값으로 지정할 수는 없다. </p>
<p>▶︎ 예시</p>
<pre><code>Toast t = Toast.makeText(this, &quot;종료하려면 한번 더 누르세요&quot;, 
Toast.LENGTH_SHORT); t.show();</code></pre><p>토스트는 문자열만 잠깐 보였다가 사라지게 할 때 사용하므로 위의 코드처럼 간단하게 구현하는게 일반적이지만, 원한다면 다음의 함수를 이용하여 추가로 설정할 수도 있다. </p>
<pre><code>∙ setDuration(int duration)
∙ setText(int resId)
∙ setView(View view)
∙ setGravity(int gravity, int xOffset, int yOffset)
∙ setMargin(float horizontalMargin, float verticalMargin)</code></pre><p>setDuration(), setText() 함수는 문자열과 화면에 보이는 시간을 설정할 수 있으며, setView() 함수는 임의의 뷰를 토스트로 띄울 수 있다. 또한, setGravity(), setMargin() 함수는 토스트가 뜨는 위치를 조정할 수 있다. </p>
<h2 id="◼︎-알림-창">◼︎ 알림 창</h2>
<p>알림 창은 안드로이드 다이얼로그의 가장 기본이며, 사용자에게 메세지 확인을 강제할 수 있다. 간단한 메세지 출력뿐 아니라 다양한 화면 구성이 가능해서 복잡한 화면도 띄울 수 있다.  안드로이드 라이브러리에서 몇몇 다이얼로그를 제공하는데 이들 모두가 AlertDialog의 서브 클래스이다. </p>
<p><img src="https://velog.velcdn.com/images/so__ok/post/dcb8391c-e7d1-407e-9354-95df099031b3/image.png" alt=""></p>
<p>알림 창에는 세 가지 영역이 있다. 
맨 윗줄은 타이틀(Tiltle) 영역으로 아이콘 이미지와 문자열이 위치한다.
타이틀 영역 아랫부분은 다이얼로그의 본문에 해당하는 영역으로, 문자열을 비롯하여 다양하게 구성할 수 있다. 창 아래는 버튼 영역이다. 알림 창을 구성할 때 타이틀과 버튼에 대한 정보를 지정하지 않으면 두 영역은 출력되지 않는다.</p>
<p>알림 창은 코드에서 직접 new  연산자로 생성하지 못하고 Builder 클래스로 생성해야 한다. </p>
<pre><code>AlertDialog.Builder builder = new AlertDialog.Builder(this);</code></pre><p>위의 코드는 알림 창을 생성하는 코드가 아니라, 알림 창을 만들어 주는 Builder 클래스의 객체를 생성한 것이다. Builder 객체의 여러 setter 메서드를 이용하여 다이얼로그 구성을 설정하면 Builder 객체가 알림 창을 생성해 주는 구조이다. </p>
<pre><code>∙ setIcon(int iconId): 타이틀 영역의 아이콘 지정
∙ setMessage(CharSequence message): 본문을 단순 문자열로 구성
∙ setTitle(CharSequence title): 타이틀 문자열 지정</code></pre><p>알림 창에 버튼을 추가하려면 다음 함수를 이용하면 된다. </p>
<pre><code>∙ setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener)
∙ setNegativeButton(CharSequence text, DialogInterface.OnClickListener listener)
∙ setNeutralButton(CharSequence text, DialogInterface.OnClickListener listener)</code></pre><p>다이얼로그 화면 하단에 들어가는 버튼은 최대 3개까지 지정할 수 있다. 
버튼은 각각 positive button, negative button, neutral button으로 구분한다. 
몇 개를 추가할 것인지는 개발자의 마음이지만, 같은 성격의 버튼을 두 개 추가할 수는 없다. 즉, setPositiveButton() 함수를 두번 호출하면 같은 성격의 버튼이 중복되어 하나밖에 안 보이게 된다. </p>
<p>이처럼 버튼을 추가하는 함수를 구분하는 이유는 버튼의 UI 측면이 아니라, 어떤 성격의 버튼인지를 구분하여 버튼 클릭 이벤트 처리를 다르게 하는 데 목적이 있다. </p>
<p>▶︎ 예시</p>
<pre><code>builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setTitle(&quot;알림&quot;);
builder.setMessage(&quot;정말 종료 하시겠습니까?&quot;);
builder.setPositiveButton(&quot;OK&quot;, null);
builder.setNegativeButton(&quot;NO&quot;, null);</code></pre><p>Builder에 설정한 내용대로 알림 창을 생성하려면 create() 함수를 이용하고, 생성된 다이얼로그를 show() 함수를 이용해 화면에 출력한다. </p>
<pre><code>alertDialog=builder.create();
alertDialog.show();</code></pre><p>알림 창에서 하단의 버튼이 눌리거나 스마트폰 뒤로가기 버튼, 알림 창 밖의 화면을 터치했을 때, 이렇게 세 가지 경우에 알림 창이 닫힌다. 그런데 때에 따라 뒤로가기 버튼을 눌렀을 때 다이얼로그 창이 닫히지 않게 하려면 setCancelable() 함수를 이용한다. </p>
<pre><code>setCancelable(boolean cancelable) </code></pre><p>Builder 클래스의 함수로 이 함수의 값을 false로 지정하면 뒤로가기 버튼에 의해 다이얼로그 창이 닫히는 것을 막을 수 있다. 또한 창 밖을 터치해도 닫히지 않는다. </p>
<pre><code>setCanceledOnTouchOutside(boolean cancel)</code></pre><p>위의 함수는 다이얼로그 창밖을 터치했을 때 다이얼로그가 닫히는지에 대한 설정이다. </p>
<p>사용자가 다이얼로그 창 하단의 버튼을 누르면 기본적으로 창이 닫힌다. 
하지만 닫히는 순간 추가 이벤트 처리가 필요한 경우도 있다. 
다이얼로그 이벤트 처리는 setPositiveButton()과 setNegativeButton() 함수의 두 번째 매개변수에 이벤트 핸들러 클래스를 등록해주면 된다.  </p>
<pre><code>builder.setPositiveButton(&quot;OK&quot;, dialogListener);</code></pre><p>setPositiveButton() 함수의 두 번째 매개변수로 dialogListener를 지정하였다. 
이 객체는 개발자가 이벤트 처리를 위해 DialogInterface.OnClickListener를 구현한 클래스이다. 이 객체의 onClick() 함수가 이벤트 발생 시 자동으로 호출된다.</p>
<pre><code>DialogInterface.OnClickListener dialogListener=new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface dialog, int which) {
           //...
   }
};</code></pre><p>onClick() 함수의 첫 번째 매개변수가 이벤트가 발생한 다이얼로그 객체이고, 두 번째 매개변수는 버튼의 종류이다. </p>
<h2 id="◼︎-목록">◼︎ 목록</h2>
<p>목록 형식의 다이얼로그를 띄워야 하는 경우에도 AlertDialg에서 기본으로 제공해 준다. </p>
<pre><code>∙ setItems(int itemsId, DialogInterface.OnClickListener listener)
∙ setItems(CharSequence[] items, DialogInterface.OnClickListener listener)</code></pre><p>두 함수 모두 목록을 구성하는데, 첫 번째 함수는 목록을 구성하기 위한 문자열을 배열 리소스로 만들어 사용하고, 두 번째 함수는 코드에서 직접 배열을 만들어 배열을 매개변수로 지정하여 사용한다. 목록 다이얼로그 내에서의 사용자 항목 선택 이벤트는 setItems() 함수의 두 번째 매개변수를 이용하여 구현할 수 있다.</p>
<pre><code>builder.setItems(R.array.dialog_array, dialogListener);</code></pre><p>setItem() 함수를 이용하여 목록 다이얼로그를 만들면서 두 번째 매개변수로 항목 선택 이벤트 핸들러 객체를 등록하였다. </p>
<pre><code>DialogInterface.OnClickListener dialogListener=new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
    }
};</code></pre><p>항목 선택 이벤트가 발생했을 때 호출되는 onClick() 함수의 두 번째 매개변수는 선택한 항목의 인덱스 값이다. </p>
<pre><code>∙ setMultiChoiceItems(CharSequence[] items, boolean[] checkedItems,
                      DialogInterface.OnMultiChoiceClickListener listener)
∙ setMultiChoiceItmes(int itemsId, boolean[] checkedItems,
                      DialogInterface.OnMultiChoiceClickListener listener)</code></pre><p>AlertDialog를 이용하여 목록 다이얼로그를 만들 때 각 항목 옆에 체크박스가 나와야 할 경우를 위해 위의 함수를 제공한다. 첫 번째 매개변수로 항목 구성을 위한 배열 값이나 배열 리소스를 지정할 수 있으며, 두 번째 매개변수는 선택되어 있어야 할 체크박스의 체크 상태 값이다. 세 번째 매개변수는 항목 선택 이벤트 핸들러다. </p>
<pre><code>∙ setSingleChoiceItems(CharSequence[] items, int checkedItem, 
                       DialogInterface.OnClickListener listener)
∙ setSingleChoiceItems(int itemsId, int checkedItem,
                       DialogInterface.OnClickListener listener)</code></pre><p>위의 함수를 사용하면 항목 옆에 라디오버튼을 나타 낼 수 있으며, 두 번째 매개변수가 초기 선택 항목의 인덱스 값이다. </p>
<pre><code>builder.setSingleChoiceItems(R.array.dialog_array, 0, dialogListener);</code></pre><p>배열 정보로 라디오버튼이 나오는 목록 다이얼로그를 띄우는 코드이다. 두 번째 매개변수를 0으로 지정하였으므로 처음 다이얼로그가 열릴 때 첫 번째 라디오버튼이 체크된 상태로 열린다. </p>
<h2 id="◼︎-날짜-선택">◼︎ 날짜 선택</h2>
<pre><code>DatePickerDialog dateDialog=new DatePickerDialog(this, null, year, month, day);
</code></pre><p>두 번째 매개변수는 사용자가 날짜를 선택할 때 발생하는 이벤트 처리 핸들러이며, 이후 숫자 3개가 다이얼로그에서 기본으로 보여야할 날짜 연, 월, 일이다. </p>
<pre><code>   DatePickerDialog dateDialog = new DatePickerDialog(this,
                    new DatePickerDialog.OnDateSetListener(){
   @Override 
    public void onDateSet(DatePicker view, int year, int monthOfYear,
                                              int datOfMonth){
          }
}, year, month, day);
</code></pre><p>이벤트가 발생하면 onDateSet() 함수가 호출되어 매개변수로 사용자가 선택한 연, 월, 일을 받을 수 있다. </p>
<h2 id="◼︎-시간-선택">◼︎ 시간 선택</h2>
<p>날짜 선택(DatePickerDialog)와 마찬가지로 사용자가 시간을 선택할 수 있도록 시간 선택도 제공한다. </p>
<pre><code>TimePickerDialog timeDialog=new TimePickerDialog(this, null, hour, minute, true);</code></pre><p>위의 코드처럼 매개변수로 시, 분으로 입력하면 초기 시간이 화면에 나온다. 세 번째 매개변수는 24시간 체계로 나타낼지를 설정하는 논릿값인데, 이 값을 true로 설정하면 24시간 체계로, false로 지정하면 12시간 체계로 오전, 오후를 선택할 수 있는 버튼이 나온다. </p>
<p>시간 조정 이벤트는 생성자의 매개변수에 이벤트 핸들러를 등록하여 처리한다. </p>
<pre><code>TimePickerDialog timeDialog=new TimePickerDialog(this, new TimePickerDialog.
OnTimeSetListener() {
    @Override
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
    }
}, hour,minute,false);    </code></pre><h2 id="◼︎-커스텀-다이얼로그">◼︎ 커스텀 다이얼로그</h2>
<p>라이브러리에서 제공하는 화면 외에 개발자가 원하는 임의의 화면을 다이얼로그 창에 띄워야하는 경우 이용하게 된다. </p>
<p>작성 방법은 우선 다이얼로그 화면을 구성하는 레이아웃 XML 파일을 만든다.</p>
<pre><code>LayoutInflater inflater=(LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
View view=inflater.inflater(R.layout.dialog_custom, null);
builder.setView(view);</code></pre><p>먼저 LayoutInflater 클래스를 이용하여 레이아웃 XML 파일을 초기화 한다. 그리고 초기화된 뷰를 Builder의 setView() 함수를 이용하여 AlertDialog의 본문에 지정하면 된다. 결국 커스텀 다이얼로그는 AlertDialog와 구현 방법이 같은데, 단지 개발자가 직접 구성한 뷰를 setView() 함수를 통해 다이얼로그 본문으로 지정하여 구현한다. </p>
<blockquote>
<p>✏️ 힘들어.. 진짜 불같은 11월 말~ 1월을 보냈다..  피자가게는 겨울이 피크니까.. 어쩔 수 없지만 정말 올 해 겨울은 눈도 많이 와서 유독 더 바빴던거 같다.. 나는 이걸로 취업하게 되면 절대 눈오는 날 배달음식 안 시켜먹어야지..
같이 일하는 배달 친구가 넘어지면서 까지 일을하니.. 안에서 일하는 나는 그냥 입꾹닫.. 다이얼로그는 그렇게 어렵고 복잡한 부분이 아니였는데, 이거 하나 작성하는데 한달은 매달린거 같다(자투리 시간에 했으니..) 오늘은 한 개만 더 하고 집가서 쉬련다!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[진동과 소리]]></title>
            <link>https://velog.io/@so__ok/%EC%A7%84%EB%8F%99%EA%B3%BC-%EC%86%8C%EB%A6%AC</link>
            <guid>https://velog.io/@so__ok/%EC%A7%84%EB%8F%99%EA%B3%BC-%EC%86%8C%EB%A6%AC</guid>
            <pubDate>Wed, 07 Dec 2022 16:17:15 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/so__ok/post/c6c88c0c-460b-4a84-bc6a-ef0e78191683/image.png" alt=""></p>
<blockquote>
<p>오늘은 진동과 효과음을 재생하는 프로그램을 작성해 보려 한다. 
세 개의 버튼을 눌렀을 때, 각각의 버튼에 등록되어 있는 진동과 효과음을 재생하는 형태로 구현해 보자.</p>
</blockquote>
<h2 id="◼︎-진동-울리기">◼︎ 진동 울리기</h2>
<p>앱의 상황을 알리기 위해 진동과 소리가 사용되며, 그 중 진동을 울리기 위해서는** 퍼미션(Permission)**이 설정되어 있어야 한다. 앱에 퍼미션을 설정하지 않으면 진동이 울리는 순간 오류가 발생하고 앱이 자동으로 종료된다. </p>
<p>퍼미션은 앱의 메인 환경파일인 AndroidManifest.xml 파일에서 설정한다.</p>
<pre><code>&lt;uses-permission android:name=&quot;android.permission.VIBRATE&quot;/&gt;</code></pre><p>퍼미션을 설정했다면 이제 진동을 울리는 코드를 작성한다. 진동을 위한 Vibrator라는 SystemService가 제공되어 간단하게 작성이 가능하다. </p>
<p>Vibrator 객체를 획득하여 vibrate() 함수 호출로 진동을 울릴 수 있다.</p>
<pre><code>Vibrator vib=(Vibrator)getSystemService(VIBRATOR_SERVICE);
vib.vibrate(1000);</code></pre><p>vibrate() 함수의 매개변수는 진동이 울리는 시간이다. 
1000은 1초 동안 진동을 울리는데, 여러 번 반복하여 진동을 울려야 할 때는 </p>
<pre><code>vibrate(long[] pattern, int repeat)</code></pre><p>함수를 이용한다.</p>
<p>진동이 계속 울리게 하려면, 첫 번째 매개변수가 long 형 배열이야 한다. 
배열 값은 <strong>홀수 번째 값이 대기시간, 짝수 번째 값이 진동시간</strong>이다. 
두 번째 매개변수는 몇 번 반복할 것인가의 설정으로 값을 0으로 주면 코드에서 취소할 때까지 무한반복 되며, -1로 주면 한 번만 패턴대로 진동이 울린다. </p>
<p>►예시</p>
<pre><code>Vibrator vib=(Vibrator)getSystemService(VIBRATOR_SERVICE);
vib.vibrate(new long[]{500, 1000, 500, 1000}, -1);</code></pre><p>위의 예시는 진동이 처음 0.5초 대기 후 1초 진동이 울리고, 다시 0.5초 대기 후 1초간 진동이 울린다. 그리고 이 패턴의 반복 여부가 -1로 설정되었으므로 반복하지 않는다. </p>
<h2 id="◼︎-소리-울리기">◼︎ 소리 울리기</h2>
<p>■ 시스템 효과음
안드로이드 스마트폰에 내장된 기본 효과음을 앱에서 많이 사용한다. 
이를 이용하는 방법에 대해 알아보자. </p>
<pre><code>Uri notification = RingtonrManager.getDefaultUri(RingtoneManaget.TYPE_NOTIFICATION);
Ringtone ringtone = RingtoneManager.getRingtone(getApplicationContext(), notification);
ringtonr.play();</code></pre><p>안드로이드 시스템에는 간단한 알림, 알람, 전화 수신음 등 여러 가지 효과음이 등록되어 있다. 이 중 울리고자 하는 효과음의 식별자를 Uri 타입으로 획득해야 한다. </p>
<p>효과음은 RingtoneManager의 getDefaultUri() 함수를 이용하여 획득하는데, ALARM, NOTIFICATION, RINGTONE 등이 있다. Uri 값으로 식별되는 효과음을 재생할 수 있는 Ringtone을 얻어 play() 함수로 재생한다. </p>
<p>■ 개발자 임의의 효과음
안드로이드 시스템에 등록된 효과음이 아닌 직접 녹음한 효과음을 이용할 때, 녹음한 효과음을 리소스로 만들어 이용한다. 효과음을 리소스로 만들 때는 res 하위의 raw 폴더를 이용하는데, 등록한 효과음을 MediaPlayer를 이용해 간단하게 재생할 수 있다. </p>
<pre><code>MediaPlayer player=MediaPlayer.create(this, R.raw.fallbackring);
player.start();</code></pre><p>이제 위의 화면을 구현해 보자. </p>
<p>짧은 효과음을 직접 녹음해 리소스로 만들어 사용해보자. </p>
<p>[res → Android resource directory] 메뉴를 선택한다. </p>
<p>리소스 폴더명은 지정되어 있으므로 개발자가 직접 입력할 필요 없이 raw 타입을 선택하명 폴더명이 자동으로 &quot;raw&quot;가 된다.  이렇게 만들어진 raw 폴더에 준비한 음원 파일을 복사하면 된다. </p>
<p>진동을 울리기 위해서 AndroidManifest.xml 파일에 퍼미션을 설정한다. 
<img src="https://velog.velcdn.com/images/so__ok/post/96d15de9-cd44-410a-a654-b008a60c4036/image.png" alt=""></p>
<p>레이아웃 XML</p>
<pre><code>&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.Vibrate_sound&quot;
    android:orientation=&quot;vertical&quot;&gt;

    &lt;Button
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;Vibrator&quot;
        android:id=&quot;@+id/btn_vibrator&quot;/&gt;

    &lt;Button
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;system beep&quot;
        android:id=&quot;@+id/btn_system_beep&quot;/&gt;

    &lt;Button
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;custom sound&quot;
        android:id=&quot;@+id/btn_custom_sound&quot;/&gt;

&lt;/LinearLayout&gt;</code></pre><p>자바 코드</p>
<pre><code>public class Vibrate_sound extends AppCompatActivity implements View.OnClickListener {

    Button vibrationBtn;
    Button systemBeepBtn;
    Button customBeepBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_vibrate_sound);

        vibrationBtn = findViewById(R.id.btn_vibrator);
        systemBeepBtn = findViewById(R.id.btn_system_beep);
        customBeepBtn = findViewById(R.id.btn_custom_sound);

        vibrationBtn.setOnClickListener(this);
        systemBeepBtn.setOnClickListener(this);
        customBeepBtn.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        if (v == vibrationBtn){
            Vibrator vib = (Vibrator) getSystemService(VIBRATOR_SERVICE);
            vib.vibrate(1000);
        }else if (v == systemBeepBtn){
            Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
            Ringtone ringtone =
                    RingtoneManager.getRingtone(getApplicationContext(), notification);
            ringtone.play();
        }
    }
}</code></pre><blockquote>
<p>✏️ 일 끝나고선 하는건 역시나 너무 힘들지만.. 나중에 보면 매우 뿌듯할 듯 하다. 알람 앱을 만들면 소리와 진동은 필수적으로 필요한 부분이라 유익하게 쓸 수 있었다. 진동에 퍼미션 설정해야 하는 걸 까먹고 있었는데.. 화면이 꺼지는 이유를 다시 알았다 ㅋㅋ 휴 오늘도 두 개 채우도록 해야지.. </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[ConstraintLayout]]></title>
            <link>https://velog.io/@so__ok/ConstraintLayout</link>
            <guid>https://velog.io/@so__ok/ConstraintLayout</guid>
            <pubDate>Tue, 06 Dec 2022 17:39:49 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이번 벨로그에서는 실습이 아닌 ConstraintLayout에 대하여 다뤄보려 한다. 
ConstraintLayout은 레이아웃 XML 파일을 만들면 기본으로 작성되어 있으며, 개발자가 꼭 이용해야 하는 것은 아니다. </p>
</blockquote>
<h2 id="constraintlayout">ConstraintLayout</h2>
<p>ConstraintLayout은 RelativeLayout과 상당히 유사하다.</p>
<h3 id="◼︎-상대적-위치-지정">◼︎ 상대적 위치 지정</h3>
<p>RelativeLayout과 마찬가지로 상대 위치에 따라 뷰의 배치를 제공한다. 
▪︎ 가로 축: 특정 뷰를 기준으로 왼쪽(Left), 오른쪽(Right), 시작(start), 끝(end) 상대적인 위치 지정
▪︎ 세로 축: 특정 뷰를 기준으로 위쪽(Top), 아래쪽(Bottom), 문자열 기준선(BaseLine) 상대적인 위치 지정</p>
<p>▶︎ 예시를 들어보자.</p>
<pre><code>&lt;Button
    android:id=&quot;@+id/btn1&quot;
    .../&gt;
&lt;Button
    ...
    app:layout_constraintLeft_toRightOf=&quot;@+id/btn1/&gt;</code></pre><p>위의 첫 번째 Button에 id 속성을 지정하였고, 두 번째 Button에는 layout_constraintLeft_toRightOf 속성을 이용하여 첫 번째 Button의 id 값인 btn1인 뷰의 오른쪽에 자신의 왼쪽을 위치하게 하였다. </p>
<p>ConstraintLayout에서 제공하는 상대 위치 속성은 RelativeLayout보다 많다. </p>
<p>∙ layout_constraintLeft_toLeftOf: 뷰의 왼쪽을 대상 뷰의 왼쪽에 위치
∙ layout_constraintLeft_toRightOf: 뷰의 왼쪽을 대상 뷰의 오른쪽에 위치
∙ layout_constraintRight_toLeftOf: 뷰의 오른쪽을 대상 뷰의 왼쪽에 위치
∙ layout_constraintRight_toRightOf: 뷰의 오른쪽을 대상 뷰의 오른쪽에 위치 
∙ layout_constraintTop_toTopOf: 뷰의 위를 대상 뷰의 위에 위치
∙ layout_constraintTop_toBottomOf: 뷰의 위를 대상 뷰의 아래에 위치
∙ layout_constraintBottom_toTopOf: 뷰의 아래를 대상 뷰의 위에 위치
∙ layout_constraintBottom_toBottomOf: 뷰의 아래를 대상 뷰의 아래에 위치
∙ layout_constraintBaseline_toBaselineOf: 뷰의 텍스트 라인에 대상 뷰의 텍스트 라인에 위치
∙ layout_constraintStart_toEndOf: 뷰의 시작에 대상 뷰의 끝을 위치
∙ layout_constraintStart_toStartOf: 뷰의 시작에 대상 뷰의 시작을 위치
∙ layout_constraintEnd_toStartOf: 뷰의 끝에 대상 뷰의 시작을 위치
∙ layout_constraintEnd_toEndOf: 뷰의 끝에 대상 뷰의 끝을 위치</p>
<p>속성값으로 상대 뷰의 <strong>id 값 *<em>또는 *</em>&quot;parent&quot;</strong>로 상위 뷰를 지칭할 수 있다.</p>
<p>만약, 상반되는 두 가지 속성을 &quot;parent&quot; 값으로 사용하게 되면 뷰는 상반되는 두 가지 속성 중앙에 위치하게 된다. </p>
<h3 id="◼︎-여백margin">◼︎ 여백(margin)</h3>
<p>뷰와 뷰 사이의 간격을 표현하기 위해서 margin 설정을 다음의 속성으로 지정할 수 있다. </p>
<p>∙ android:layout_marginStart
∙ android:layout_marginEnd
∙ android:layout_marginLeft
∙ android:layout_marginTop
∙ android:layout_marginRight
∙ android:layout_marginBottom</p>
<p>또한, 상대 뷰가 View.GONE 상태일 때의 margin 값을 따로 설정할 수 있다. 
∙ android:layout_goneMarginStart
∙ android:layout_goneMarginEnd
∙ android:layout_goneMarginLeft
∙ android:layout_goneMarginRight
∙ android:layout_goneMarginTop
∙ android:layout_goneMarginBottom</p>
<blockquote>
<p>※ View.GONE 상태는 뷰가 화면에 보이지 않으며 크기도 확보하지 못하고 있는 상황이다. 상대 뷰가 화면에 보이고 있을 때의 margin 값을 다르게 설정하여 상대 뷰가 보이지 않는 상황에 대응할 수 있다. </p>
</blockquote>
<h3 id="◼︎-가운데-맞춤과-치우침bias">◼︎ 가운데 맞춤과 치우침(bias)</h3>
<p>뷰를 부모 중앙에 위치시키지만, 어느 한 쪽으로 치우펴서 나오게 하고 싶을 때 사용한다. </p>
<p>∙ layout_constraintHorizontal_bias: 가로 치우침 조절
∙ layout_constraintVertical_bias: 세로 치우침 조절</p>
<p>▶︎ 예시를 들어보자</p>
<pre><code>&lt;Button
    ...
    app:layout_constraintLeft_toLeftOf=&quot;parent&quot;
    app:layout_constraintRight_toRightOf=&quot;parent&quot;
    app:layout_constraintHorizontal_bias=&quot;0.2&quot;/&gt;</code></pre><p>layout_constraintHorizontal_bias을 설정하여 가로 방향 치우침을 명시하였다. 
값을 0.2로 지정하면 20% 의 의미로 왼쪽에서 20% 위치에 나타나게 된다. </p>
<h3 id="◼︎-비율ratio">◼︎ 비율(Ratio)</h3>
<p>뷰의 크기를 지정할 때 <strong>가로세로 비율</strong>에 의한 크기를 지정할 수 있다. 
이 속성을 이용하려면 전제 조건이 필요하다. 우선 크기 값이 0dp로 지정되어 있어야  한다. </p>
<p>► 예시를 들어보자</p>
<pre><code>&lt;Button
    android:id=&quot;@+id/btn1&quot;
    android:layout_width=&quot;wrap_content&quot;
    android:layout_height=&quot;0dp&quot;
    app:layout_constraintDimensionRatio=&quot;1:1&quot;
    app:layout_constraintLeft_toLeftOf=&quot;parent&quot;/&gt;</code></pre><p>위의 예는 layout_width의 크기를 지정하고 layout_height의 크기는 layout_width의 배율로 결정하고자 &quot;0dp&quot;로 지정한 예이다. </p>
<p>layout_width, layout_height 중 하나가 &quot;wrap_content&quot;, &quot;100dp&quot;  등의 크기를 지정하고 나머지를 &quot;0dp&quot;로 지정하여 크기를 layout_constraintDimensionRatio을 이용하여 배율로 결정할 수 있다.<br>**
또한, layout_constraintDimensionRatio 속성은 다른 constraint 속성이 함께 지정된 경우에만 적용된다. **</p>
<p>배율로 크기를 지정할 때** layout_width, layout_height 값이 모두 0인 경우**도 가능하다. </p>
<p>단, 그런 경우에는 속성값에 &quot;W&quot; 또는 &quot;H&quot; 값을 지정하여 어느 쪽의 크기를 배율에 맞게 조정할 것인지를 지정해야 한다. </p>
<p>▶︎ 예시를 들어보자</p>
<pre><code>&lt;Button
    android:id=&quot;@+id/btn1&quot;
    android:layout_width=&quot;0dp&quot;
    android:layout_height=&quot;0dp&quot;
    ...
    app:layout_constraintLeft_toLeftOf=&quot;parent&quot;
    app:layout_constraintRight_toRightOf=&quot;parent&quot;
    app:layout_constraintDimensionRatio=&quot;H, 2:1&quot; /&gt;</code></pre><p>위의 예는 layout_width, layout_height 값이 모두 0으로 설정되었으며, layout_constraintDimensionRatio 값에 가로를 기준으로 세로 값을 배율에 맞게 조정하기 위해 &quot;H&quot; 단어를 지정하였다. </p>
<blockquote>
<p>✏️ 오늘 벨로그 두 개 성공.. 내일 일 마치고 밀려서 못 하던거 다 올려야지.. 분명 금방 끝날 줄 알았는데 까먹었던 것 들 때문에 다시 서치하느라 시간을 많이 썼다. 모래는 저녁 출근이니까 낮에 알람 UI라도 만들어야 겠다. </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TableLayout, GridLayout]]></title>
            <link>https://velog.io/@so__ok/TableLayout-GridLayout-l4hhaxn3</link>
            <guid>https://velog.io/@so__ok/TableLayout-GridLayout-l4hhaxn3</guid>
            <pubDate>Tue, 06 Dec 2022 15:30:48 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/so__ok/post/41ec2f88-d9f5-4fbe-b6f9-f702b12e071f/image.png" alt=""></p>
<blockquote>
<p>오늘 구현해 볼 계산기 화면이다. 실제 계산기로 작동하게 하는 알고리즘은 추가하지 않고 TableLayout과 GridLayout을 활용하여 화면만 구현해 보려 한다. </p>
</blockquote>
<p>레이아웃 XML</p>
<pre><code>&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.Table_GridLayout&quot;
    android:orientation=&quot;vertical&quot;&gt;

    &lt;TextView
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;0dp&quot;
        android:layout_weight=&quot;1&quot;/&gt;

    &lt;LinearLayout
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:orientation=&quot;horizontal&quot;&gt;

        &lt;TableLayout
            android:layout_width=&quot;0dp&quot;
            android:layout_height=&quot;match_parent&quot;
            android:layout_weight=&quot;3&quot;&gt;
           &lt;TableRow&gt;
               &lt;Button
                   android:layout_width=&quot;0dp&quot;
                   android:layout_weight=&quot;1&quot;
                   android:text=&quot;7&quot;
                   android:paddingTop=&quot;32dp&quot;
                   android:paddingBottom=&quot;32dp&quot; /&gt;
               &lt;Button
                   android:layout_width=&quot;0dp&quot;
                   android:layout_weight=&quot;1&quot;
                   android:text=&quot;8&quot;
                   android:paddingTop=&quot;32dp&quot;
                   android:paddingBottom=&quot;32dp&quot;/&gt;
               &lt;Button
                   android:layout_width=&quot;0dp&quot;
                   android:layout_weight=&quot;1&quot;
                   android:text=&quot;9&quot;
                   android:paddingBottom=&quot;32dp&quot;
                   android:paddingTop=&quot;32dp&quot;/&gt;
           &lt;/TableRow&gt;

            &lt;TableRow&gt;
                &lt;Button
                    android:layout_width=&quot;0dp&quot;
                    android:layout_weight=&quot;1&quot;
                    android:text=&quot;4&quot;
                    android:paddingTop=&quot;32dp&quot;
                    android:paddingBottom=&quot;32dp&quot;/&gt;
                &lt;Button
                    android:layout_width=&quot;0dp&quot;
                    android:layout_weight=&quot;1&quot;
                    android:text=&quot;5&quot;
                    android:paddingTop=&quot;32dp&quot;
                    android:paddingBottom=&quot;32dp&quot;/&gt;
                &lt;Button
                    android:layout_width=&quot;0dp&quot;
                    android:layout_weight=&quot;1&quot;
                    android:text=&quot;6&quot;
                    android:paddingTop=&quot;32dp&quot;
                    android:paddingBottom=&quot;32dp&quot;/&gt;
            &lt;/TableRow&gt;
            &lt;TableRow&gt;
                &lt;Button
                    android:layout_width=&quot;0dp&quot;
                    android:layout_weight=&quot;1&quot;
                    android:text=&quot;1&quot;
                    android:paddingTop=&quot;32dp&quot;
                    android:paddingBottom=&quot;32dp&quot;/&gt;
                &lt;Button
                    android:layout_width=&quot;0dp&quot;
                    android:layout_weight=&quot;1&quot;
                    android:text=&quot;2&quot;
                    android:paddingTop=&quot;32dp&quot;
                    android:paddingBottom=&quot;32dp&quot;/&gt;
                &lt;Button
                    android:layout_width=&quot;0dp&quot;
                    android:layout_weight=&quot;1&quot;
                    android:text=&quot;3&quot;
                    android:paddingTop=&quot;32dp&quot;
                    android:paddingBottom=&quot;32dp&quot;/&gt;
            &lt;/TableRow&gt;
            &lt;TableRow&gt;
                &lt;Button
                    android:layout_width=&quot;0dp&quot;
                    android:layout_weight=&quot;1&quot;
                    android:text=&quot;*&quot;
                    android:paddingTop=&quot;32dp&quot;
                    android:paddingBottom=&quot;32dp&quot;/&gt;
                &lt;Button
                    android:layout_width=&quot;0dp&quot;
                    android:layout_weight=&quot;1&quot;
                    android:text=&quot;0&quot;
                    android:paddingTop=&quot;32dp&quot;
                    android:paddingBottom=&quot;32dp&quot;/&gt;
                &lt;Button
                    android:layout_width=&quot;0dp&quot;
                    android:layout_weight=&quot;1&quot;
                    android:text=&quot;=&quot;
                    android:paddingTop=&quot;32dp&quot;
                    android:paddingBottom=&quot;32dp&quot;/&gt;
            &lt;/TableRow&gt;
        &lt;/TableLayout&gt;
        &lt;GridLayout
            android:layout_width=&quot;0dp&quot;
            android:layout_height=&quot;match_parent&quot;
            android:layout_weight=&quot;1&quot;
            android:orientation=&quot;vertical&quot;&gt;
            &lt;Button
                android:text=&quot;DEL&quot;
                android:paddingBottom=&quot;24dp&quot;
                android:paddingTop=&quot;24dp&quot;/&gt;
            &lt;Button
                android:text=&quot;/&quot;
                android:paddingTop=&quot;24dp&quot;
                android:paddingBottom=&quot;24dp&quot;/&gt;
            &lt;Button
                android:text=&quot;x&quot;
                android:paddingBottom=&quot;24dp&quot;
                android:paddingTop=&quot;24dp&quot;/&gt;
            &lt;Button
                android:text=&quot;-&quot;
                android:paddingTop=&quot;24dp&quot;
                android:paddingBottom=&quot;24dp&quot;/&gt;
            &lt;Button
                android:text=&quot;+&quot;
                android:paddingBottom=&quot;24dp&quot;
                android:paddingTop=&quot;24dp&quot;/&gt;
        &lt;/GridLayout&gt;
    &lt;/LinearLayout&gt;

&lt;/LinearLayout&gt;</code></pre><h2 id="tablelayout">TableLayout</h2>
<p>TableLayout은 뷰를 테이블(Table) 구조로 나열하는 레이아웃이다.
대표적으로 사용된 예가 위에 구현한 계산기 앱 화면이다. </p>
<p>위에 화면을 구현하기 위해서 숫자 버튼 부분을 TableLayout으로 묶고, 각각의 행(row)을 TableRow로 표현해 주었다. 그리고 이 TableRow에 포함된 뷰 하나가 셀 하나를 차지하게 된다. </p>
<p><strong>TableLayout 내에서 하나의 뷰가 여러 셀을 차지</strong>하게 할 수도 있으며, 여백이 발생할 경우 특정 열(column)을 확장할 수도 있고, 화면 크기를 벗어나는 순간 특정 열을 축소하는 등 다양하게 설정할 수 있다. </p>
<p>∙ android:shrinkColumns=&quot;0,1&quot;: 화면 크기를 벗어나는 경우 인덱스 0, 1의 열 크기를 줄임 
∙ android:stretchColumns=&quot;1&quot;: 화면 여백이 발생하는 경우 인덱스 1의 열 크기를 늘림<br>∙ android:layout_<del>~</del>column=&quot;1&quot;: 뷰의 위치 지정, 인덱스 1의 위치에 뷰가 위치
∙ android:layout_span=&quot;2&quot;: 두 개의 열을 하나의 뷰가 차지</p>
<h2 id="gridlayout">GridLayout</h2>
<p>GridLayout은 뷰가 테이블 구조로 나열된다는 점에서 TableLayout과 유사하고, 뷰가 레이아웃에 포함된 순서대로 가로나 세로 방향으로 나열된다는 점에서 LinearLayout과도 유사하다. </p>
<p>또한, LinearLayout에는 없는 자동 개행 능력도 있어서, 화면에는 TableLayout 처럼 보이기도 한다.  </p>
<h3 id="▪︎-gridlayout의-속성">▪︎ GridLayout의 속성</h3>
<p> ∙ orientation: 뷰의 배치 방향을 지정. 기본값은 가로 방향
 ∙ columnCount: 가로 방향일 때 한 줄에 몇 개의 뷰를 나열할 것인지 지정
 ∙ rowCount: 세로 방향일 때 한 줄에 몇 개의 뷰를 나열할 것인지 지정</p>
<p> <strong>orientation이 &quot;horizontal&quot;이면 columnCount만 의미가 있고 rowCount는 적용되지 않으며, &quot;vertical&quot;이면 rowCount만 의미가 있고columnCount는 적용되지 않는다.</strong></p>
<p> 또한, GridLayout에 뷰를 포함할 때 다음의 속성을 지정하여 다양하게 꾸밀 수 있다. </p>
<p> ∙ layout_column: 뷰가 위치할 열 인덱스 지정
 ∙ layout_row: 뷰가 위치할 행 인덱스 지정
 ∙ layout_columnSpan: 가로 방향으로 여러 열을 하나의 뷰가 차지하고자 할 때
 ∙ layout_rowSpan: 세로 방향으로 여러 행을 하나의 뷰가 차지하고자 할 때
 ∙ layout_gravity: 하나의 열 내에서 뷰의 정렬 위치 지정</p>
<blockquote>
<p>※ 안드로이드 스튜디오는 <strong>버튼 색상이 기본 보라색</strong>으로 되어있어 변경되지 않는다. 검색해 보니 이는 안드로이드 스튜디오가 앱 테마를 Theme.MaterialComponents를 기본 디폴트로 사용하게 되면서 발생한 문제라고 한다.  위에 완성된 화면처럼 회색으로 변경할 수 있는 방법은 두 가지지만 여기서는 한 가지만 다뤄 볼 생각이다. </p>
</blockquote>
<p>프로젝트의 app→ res→</p>
<p><img src="https://velog.velcdn.com/images/so__ok/post/1c7e02ab-4c63-48fc-9fc0-3549911c46be/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/so__ok/post/9ce3bd3c-0239-48f2-bc6a-3c5799fe973e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/so__ok/post/148e13b9-d37d-4f89-9cfe-d629ca3d8a8a/image.png" alt=""></p>
<p>위 폴더들을 통해 들어오면 아래와 같은 코드가 기본으로 셋팅되어 있을 것이다.
<img src="https://velog.velcdn.com/images/so__ok/post/48b14b61-2363-4062-a033-2f0b93622068/image.png" alt=""></p>
<p>이 코드의 세 번째 줄의 parent 값을 &quot;Theme.MaterialComponents.DayNight.DarkActionBar&quot; 에서 
<img src="https://velog.velcdn.com/images/so__ok/post/2e31acec-eb83-4ab3-839d-a8b587bde037/image.png" alt=""></p>
<p>로 변경해 주면 색상변경이 가능하다. </p>
<blockquote>
<p>10일 만에 올리게 됐네.. 게으른 내 자신.. 이번 주는 쉬는 날이 아직 더 남아 있으니까 틈틈히 하도록 해야겠다.. Button 색상 변경하는 걸 적으려는데, 오래 전에 했던 프로젝트라서 잠시 까먹었다. 그래도 오늘 작성하면서 다시 기억도 나고 나름 유익한 시간이였다.  알람 앱 디자인은 생각해 두긴 했는데 다른 사람들은 어떤 기능을 넣었는지 살펴보고 있다. 
그래도 관심있는 걸로 프로젝트를 하니까 막혀도 답답은 하지만 나름 성취감이 있다. 하나만 더 하고 집가야지.. 다음 벨로그로..</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[첫 번째 앱<알람 앱>]]></title>
            <link>https://velog.io/@so__ok/%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EC%95%B1%EC%95%8C%EB%9E%8C-%EC%95%B1</link>
            <guid>https://velog.io/@so__ok/%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EC%95%B1%EC%95%8C%EB%9E%8C-%EC%95%B1</guid>
            <pubDate>Fri, 25 Nov 2022 17:19:11 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/so__ok/post/465048ee-5b59-43b2-ba8b-95ae6b47051b/image.png" alt=""></p>
<h2 id="알람-앱을-선택한-이유">알람 앱을 선택한 이유?</h2>
<p>학창 시절 알람 어플이 타이머 수준으로만 개발되어 앱으로 나왔었을 때, 나는 알람의 기능보다는 음악소리에 중점을 두고 더 시끄럽고 자극적인 소리를 찾았었다. 그때 조금 더 효율적이고 아침을 재밌게 시작할 수 있는 알람이 있었으면 했던 바렘이 가장 먼저 생각나 알람 어플을 첫 번째 개발할 어플로 선택하게 되었다. </p>
<h2 id="생각하고-있는-기능">생각하고 있는 기능?</h2>
<p>이미 많이 나와 있는 재미있는 알람 어플 중에 움직이는 사물을 모두 클릭해야 꺼지는 알람부터 패턴을 맞춰야 풀리는 알람까지 있기 때문에 무슨 기능을 넣어야 사람들이 3분 5분 후에 다시 울리는 기능을 사용하지 않을까를 중점적으로 생각해 보기로 했다. </p>
<p>얕게 깬 잠은 다시 잠들기 쉽다. 잠을 깨기 위해서는 역시 뇌를 움직이는 것이 최적이라고 생각되어, 간단한 상식 퀴즈를 내어 알람을 깨는 형식으로 만들어 볼 까 한다. </p>
<h2 id="앞으로의-계획">앞으로의 계획</h2>
<p>벨로그에 전에 했던 간단한 기능들을 업로드하는건 계속 이어갈 생각이다. 이제 깃허브에 알람어플 만드는 걸 기록하는 일만 늘어 났을 뿐,, 잠은 죽어서 자자
다음엔 미션 클리어라는 하루 일과를 정리하는 어플을 만들어 보고싶다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[FrameLayout, TabHost]]></title>
            <link>https://velog.io/@so__ok/FrameLayout-TabHost</link>
            <guid>https://velog.io/@so__ok/FrameLayout-TabHost</guid>
            <pubDate>Fri, 25 Nov 2022 16:28:47 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/so__ok/post/6e81162b-5b64-4e3d-8f82-2842de9f6e4b/image.png" alt=""></p>
<blockquote>
<p>오늘 구성할 화면이다. 보는 것 처럼 3개의 탭 버튼 중 하나를 클릭했을 때, 연결된 화면을 하나씩 보여주는 형태이다. 오늘은 이 화면을FrameLayout 클래스와 TabHost 클래스를 이용하여 구성하려 한다. </p>
</blockquote>
<p>액티비티를 생성하고 탭 버튼을 이미지로 출력하기 위해 아이콘 이미지 3개를 res/drawable 폴더에 복사한다.  나는 아이콘 이미지라서 36x36 크기로 사용하였다. </p>
<h2 id="framelayout">FrameLayout</h2>
<p>FrameLayout은 레이아웃에 포함된 뷰들을 같은 영역에 겹쳐서 배치할 때 사용한다. 레이아웃 자체의 특별한 속성은 없다. FrameLayout 안에 뷰를 배치할 때, 포함된 순서대로 배치되기 때문에 맨 마지막에 포함한 뷰가 가장 위에 보인다. 
FrameLayout을 이용하는 목적은 대부분 같은 영역에 여러 뷰를 겹치게 한 다음, 한 순간에 하나의 뷰만 보이기 위함이다. </p>
<h2 id="tabhost">TabHost</h2>
<p>UI 프로그램에서 가장 많이 제공하는 화면 중 하나이다. 화면 여러 개를 준비한 다음, 사용자가 버튼을 누를 때 버튼과 연결된 화면을 하나씩 보여주는 형태이다. </p>
<h4 id="▪︎tabhost-구조">▪︎TabHost 구조</h4>
<p>TabHost로 탭 화면을 구성하면 TabHost 하위에는 꼭 TabWidget과 FrameLayout으로 구성해야 한다. </p>
<p>∙ TabHost: 탭 전체 영역을 지칭
∙ TabWidget: 탭 버튼이 들어갈 영역을 지칭
∙ FrameLayout: 탭 버튼 클릭 시 나올 화면 영역을 지칭</p>
<p>레이아웃 XML</p>
<pre><code>&lt;TabHost xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:id=&quot;@+id/host&quot;&gt;

    &lt;LinearLayout
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;
        android:orientation=&quot;vertical&quot;&gt;

        &lt;TabWidget
            android:layout_width=&quot;match_parent&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:id=&quot;@android:id/tabs&quot;
            android:background=&quot;#B15656&quot;/&gt;

        &lt;FrameLayout
            android:layout_width=&quot;match_parent&quot;
            android:layout_height=&quot;match_parent&quot;
            android:id=&quot;@android:id/tabcontent&quot;&gt;
            &lt;TextView
                android:layout_width=&quot;match_parent&quot;
                android:layout_height=&quot;match_parent&quot;
                android:id=&quot;@+id/tab_content1&quot;
                android:text=&quot;Tab 1&quot;
                android:textSize=&quot;30sp&quot;
                android:textStyle=&quot;bold&quot;
                android:gravity=&quot;center&quot; /&gt;
            &lt;TextView
                android:layout_width=&quot;match_parent&quot;
                android:layout_height=&quot;match_parent&quot;
                android:id=&quot;@+id/tab_content2&quot;
                android:textSize=&quot;30sp&quot;
                android:textStyle=&quot;bold&quot;
                android:text=&quot;tab 2&quot;
                android:gravity=&quot;center&quot; /&gt;
            &lt;TextView
                android:layout_width=&quot;match_parent&quot;
                android:layout_height=&quot;match_parent&quot;
                android:id=&quot;@+id/tab_content3&quot;
                android:text=&quot;tab 3&quot;
                android:textStyle=&quot;bold&quot;
                android:textSize=&quot;30sp&quot;
                android:gravity=&quot;center&quot;/&gt;



        &lt;/FrameLayout&gt;
    &lt;/LinearLayout&gt;
&lt;/TabHost&gt;</code></pre><p>위에서  전체 화면 영역은 TabHost 클래스를 이용하여 지칭하였고,   LinearLayout으로 나열 방향을 세로로 표현하였다. LinearLayout 아래 TabWidget을 포함하여 탭 버튼이 들어갈 자리를 지정하였으며, 탭 본문에 들어갈 자리는 FrameLayout을 이용하여 지정하였다. </p>
<p>*<em>여기서 중요한 점은 TabHost  하위에는 무조건 탭 버튼을 위해 TabWidget, 탭 본문을 위해 FrameLayout을 선언해야 한다. 본문은 FrameLayout이 아닌 다른 레이아웃 클래스로 선언할 수 없다. *</em></p>
<p>자바 코드</p>
<pre><code> @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_frame_layout);

        TabHost tabhost = findViewById(R.id.host);
        tabhost.setup();

//    각 TabSpec을 만들 때 매게변수의 문자열은 각 TabSpec의 식별자 문자열
        TabHost.TabSpec spec = tabhost.newTabSpec(&quot;tab1&quot;);
//    tab button 구성
        spec.setIndicator(null, ResourcesCompat.getDrawable(
                getResources(), R.drawable.tab_icon1, null));
//    tab의 본문 구성
        spec.setContent(R.id.tab_content1);
        tabhost.addTab(spec);

        spec = tabhost.newTabSpec(&quot;tab2&quot;);
        spec.setIndicator(null, ResourcesCompat.getDrawable(
                getResources(), R.drawable.tab_icon2, null));
        spec.setContent(R.id.tab_content2);
        tabhost.addTab(spec);

        spec = tabhost.newTabSpec(&quot;tab3&quot;);
        spec.setIndicator(null, ResourcesCompat.getDrawable(
                getResources(), R.drawable.tab_icon3, null));
        spec.setContent(R.id.tab_content3);
        tabhost.addTab(spec);

    }</code></pre><p>TabHost를 레이아웃 XML에서 획득하여 setup() 함수로 초기화한다.  탭 하나를 구성하기 위해 TabSpec을 newTabSpec() 함수로 생성해 준다. newTabSpec() 함수의 매개변수를 &quot;tab1&quot;로 지정하였는데, 이 문자열은 내가 지정한 임의의 문자열이며, 각 탭 화면을 코드에서 식별할 때 사용된다. </p>
<p>그리고 하나의 TabSpec에 탭 버튼을 구성하고자 setIndicator() 함수를 이용하였으며, 탭 버튼을 문장열이나 이미지로 구성할 수 있다. 
또한, TabSpec의 본문을 지정하고자 setContent() 함수를 이용하였다. 
이렇게 만들어진 TabSpec을 최종 TabHost에 host.addTab() 함수로 포함해 주면 된다. </p>
<blockquote>
<p>✏️ 하.. 오늘 엄마가 냉장고 청소 시켜서 세시간이 공중 분해 됐다.. 그래도 난 효년이 되었으니.. 오늘의 벨로그도 완료 . 태그가 늘어 갈 때 마다 뿌듯하다. 
첨엔 그렇게 어렵고 하나도 이해가 안 됐는데, 이렇게 정리해서 글을 올리게 될 날이 온게 너무 신기하다.. 물론 나도 많이 더 배워야 하지만 ㅎㅎ.. 
오늘은 대청소로 체력을 너무 소모해서 음주 공부를 했다.. 이런거 말하면 안 되낭 ㅎㅎㅎㅎ 뭐 할 일만 하면되지.. 아.. 아닌가..ㅎㅎㅋㅋㅋㅋ
근데 1시간이면 끝 날 일이 좀 길어져서 다음부터는 그냥 빡 집중해서 해야 겠다. 그래도 기분 좋게 마무으리~</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[RelativeLayout]]></title>
            <link>https://velog.io/@so__ok/RelativeLayout</link>
            <guid>https://velog.io/@so__ok/RelativeLayout</guid>
            <pubDate>Thu, 24 Nov 2022 16:36:22 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/so__ok/post/45b0ddf0-965b-444d-9f5c-1fe53c6ad61b/image.png" alt=""></p>
<blockquote>
<p>전에 LinearLayout으로 구성한 화면이다. 이번에는 RelativeLayout으로 구성해 보자.</p>
</blockquote>
<h1 id="relativelayout">RelativeLayout</h1>
<p>화면에 이미 배치된 뷰를 기준으로 다른 뷰의 위치를 지정하는 레이아웃이다.
LinearLayout은 orientation 속성으로 방향을 지정하였지만, RelativeLayout은 뷰의 상대 위치를 지정하여 배치하며, 자동으로 가로나 세로로 나열하지는 않는다. </p>
<h3 id="■-relativelayout-속성">■ RelativeLayout 속성</h3>
<p>기준 뷰를 기준으로 위치를 정해야 하기 때문에 모든 뷰에 id 속성을 지정해야 한다.
∙ android: layout_above: 기준 뷰의 윗부분에 배치
∙ android: layout_below: 기준 뷰의 아랫부분에 배치
∙ android: layout_toLeftOf: 기준 뷰의 왼쪽에 배치
∙ android: layout_toRightOf: 기준 뷰의 오른쪽에 배치</p>
<h3 id="■-align-속성">■ align 속성</h3>
<p>기준이 되는 뷰와 왼쪽 변을 맞추거나 윗변을 맞추는 등 정렬 작업을 지원하기 위한 속성이다. 
∙ android: layout_alignTop: 기준 뷰와 윗부분을 정렬
∙ android: layout_alignBottom: 기준 뷰와 아랫부분을 정렬
∙ android: layout_alignLeft: 기준 뷰와 왼쪽을 정렬
∙ android: layout_alignRight: 기준 뷰와 오른쪽을 정렬
∙ android: layout_alignBaseline: 기준 뷰와 텍스트 기준선을 정렬</p>
<h3 id="■-alignparentxxx-속성">■ alignParentXXX 속성</h3>
<p>안드로이드 스마트폰의 크기가 다양하여 align와 margin 값으로 뷰의 위치를 지정하기 아쉬운 부분이 있다. 이 부분을 보완하여 RelativeLayout으로 뷰를 배치할 때 특정 뷰를 상하좌우로 밀 수 있는 속성이alignParentXXX 이다.
∙ android: layout_alignParentTop: 부모의 윗부분에 뷰의 상단을 위치
∙ android: layout_alignParentBottom: 부모의 아랫부분의 뷰의 상단을 위치
∙ android: layout_alignParentLeft: 부모의 왼쪽에 뷰의 왼쪽을 위치
∙ android: layout_alignParentRight: 부모의 오른쪽에 뷰의 오른쪽을 위치
∙ android: layout_centerHorizontal: 부모의 가로 방향 중앙에 뷰를 위치
∙ android: layout_centerVertical: 부모의 세로 방향 중앙에 뷰를 위치
∙ android: layout_centerInParent: 부모의 가로세로 중앙에 뷰를 위치</p>
<p>뷰를 어디에 정렬할 것인가를 지정하는 속성이므로 값은 &quot;true&quot;로 설정한다.</p>
<blockquote>
<p>✏️ 개발할 때 속성을 하나하나 모두 쓰는 것이 아니라서 다 기억할 수는 없는데, 잊고있던 속성 덕에 코드 한줄이라도 줄이는 덕을 봤다. 
오늘 축구 때문에 집중을 잘 하지 못 했는데 꾸역꾸역 해내고 오늘 마무리~
디자인은 대충 정해 두어서 내일부터 알람어플 작업 들어갈 것 같다. 그리고 오늘 다른 사람들 벨로그를 많이 봤는데, 배워야 할 점이 참 많았다. 경력도 없는 신입이 무슨 처음부터 프로젝트하면 뭐 얼마나 대단한 걸 하겠다고.. 그저 할 수 있는 한 열심히하고 차차 경험부터 쌓아야 하는데 잠시 망각하고 있던 것 같다. 내일부터 알람 개발 화이팅!!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[LinearLayout: 채팅 화면 구성]]></title>
            <link>https://velog.io/@so__ok/LinearLayout-%EC%B1%84%ED%8C%85-%ED%99%94%EB%A9%B4-%EA%B5%AC%EC%84%B1</link>
            <guid>https://velog.io/@so__ok/LinearLayout-%EC%B1%84%ED%8C%85-%ED%99%94%EB%A9%B4-%EA%B5%AC%EC%84%B1</guid>
            <pubDate>Thu, 24 Nov 2022 14:38:21 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/so__ok/post/efeb8ea6-70d5-47e8-9fb4-d998e9f38a47/image.png" alt=""></p>
<p>오늘은 이 채팅 화면을 LinearLayout의 속성을 적용해서 작성해 보려한다. 그 전에 LinearLayout에 대해 알아보자. </p>
<h1 id="linearlayout">LinearLayout</h1>
<h3 id="◼︎linearlayout">◼︎LinearLayout</h3>
<p>가장 많이 이용되는 레이아웃 중 하나이다. 
▪︎ orientation: 다른 레이아웃에는 없는 방향성을 지정하는 속성으로 Linearlayout의 대표적인 속성이다. 세로 방향인 &quot;vertical&quot;과 가로 방향인 &quot;horizontal&quot;가 있다. </p>
<h3 id="◼︎-레이아웃-중첩">◼︎ 레이아웃 중첩</h3>
<p>오늘 구성할 채팅 화면을 만들기 위해서 LinearLayout의 중첩을 사용해야 한다. 자동 개행을 제공하지 않는 LinearLayout은 한번 방향을 지정하면 화면을 벗어나더라도 계속 지정한 방향으로만 나열된다. 즉, 레이아웃 클래스 하위에 다른 레이아읏 클래스를 배치하는 것이다. </p>
<h3 id="◼︎-linearlayouy-속성">◼︎ LinearLayouy 속성</h3>
<p> ▪︎ gravity, layout_gravity
뷰의 정렬과 관련된 속성은 gravity와 layout_gravity 두 가지이다. 두 속성 모두 left, right, top, bottom 속성값 등을 설정할 수 있지만, 적용되는 대상은 다르다. 
gravity 속성은 뷰의 내용(content)을 뷰 영역 내에서 어디에 나타날지를 설정하고, layout_gravity 속성은 뷰를 LinearLayout 영역 내에서 어디에 나타낼지를 설정한다. </p>
<p>▪︎ weight
LinearLayout에서 중요 속성이며 이용빈도도 높다. 레이아웃에 뷰를 배치했을 때, 발생한 가로, 세로 방향의 여백을 화면에 배치된 뷰들이 확장해서 차지하게 할 때 이용된다. </p>
<p>레이아웃 XMl 파일</p>
<pre><code>&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
   android:layout_width=&quot;match_parent&quot;
   android:layout_height=&quot;match_parent&quot;
   android:orientation=&quot;horizontal&quot;
   android:padding=&quot;16dp&quot;&gt;

    &lt;ImageView
        android:layout_width=&quot;98dp&quot;
        android:layout_height=&quot;90dp&quot;
        android:src=&quot;@drawable/xboy&quot; /&gt;
    &lt;LinearLayout
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:orientation=&quot;vertical&quot;
        android:layout_weight=&quot;1&quot;&gt;

        &lt;TextView
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:text=&quot;전남친&quot;
            android:textSize=&quot;20dp&quot;
            android:textStyle=&quot;bold&quot;
            android:layout_marginLeft=&quot;16dp&quot;/&gt;

        &lt;TextView
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:text=&quot;잘 지내?&quot;
            android:layout_marginTop=&quot;16dp&quot;
            android:layout_marginLeft=&quot;16dp&quot;/&gt;
    &lt;/LinearLayout&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;22.11.20&quot;
        android:layout_marginLeft=&quot;16dp&quot;/&gt;
&lt;/LinearLayout&gt;</code></pre><p>두 번째 LinearLayout에 weight 속성을 1로 부여했다. 이렇게 하면 LinearLayout 옆에 나오는 날짜 출력 Textview가 화면 오른쪽으로 밀리기 때문이다 . </p>
<p>✏️오늘 한 양은 그렇게 많지 않았는데, 우루과이랑 경기하는 날이라서 축구보며 하느라 오래 걸렸다 ㅎ.. 내일은 오늘 못 한 만큼 더 해야지... </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TextView 활용]]></title>
            <link>https://velog.io/@so__ok/TextView-%ED%99%9C%EC%9A%A9</link>
            <guid>https://velog.io/@so__ok/TextView-%ED%99%9C%EC%9A%A9</guid>
            <pubDate>Thu, 24 Nov 2022 12:58:35 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/so__ok/post/17812932-cbb2-42c4-a2e4-2f9b18fb0e52/image.png" alt=""></p>
<p>*<em>오늘 기초 뷰를 활요하여 실습할 결과 화면이다. 
*</em></p>
<h4 id="◼︎-레이아웃-xml">◼︎ 레이아웃 XML</h4>
<pre><code>&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:orientation=&quot;vertical&quot;&gt;

    &lt;TextView
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;가나다라 http://www/google.com 마바사 a@a.com 아자차카타 02-1234-5678&quot;
        android:autoLink=&quot;web|email|phone&quot;/&gt;

    &lt;TextView
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;@string/long_text&quot;
        android:layout_marginTop=&quot;16dp&quot;
        android:ellipsize=&quot;end&quot;
        android:maxLines=&quot;3&quot;/&gt;

    &lt;TextView
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/fontView&quot;
        android:text=&quot;Custom Font&quot;
        android:layout_marginTop=&quot;16dp&quot;/&gt;

    &lt;ImageView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:src=&quot;@drawable/sample&quot;
        android:maxWidth=&quot;100dp&quot;
        android:maxHeight=&quot;100dp&quot;
        android:adjustViewBounds=&quot;true&quot;
        android:layout_marginTop=&quot;16dp&quot;/&gt;
    &lt;EditText
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:inputType=&quot;phone&quot;
        android:hint=&quot;전화번호 입력&quot;
        android:layout_marginTop=&quot;16dp&quot;/&gt;
    &lt;CheckBox
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/checkbox&quot;
        android:text=&quot;in unChecked&quot;
        android:layout_marginTop=&quot;16dp&quot;/&gt;

&lt;/LinearLayout&gt;
</code></pre><p><strong>■ TextView</strong>
앱을 개발할 때 가장 많이 사용하는 뷰로, 문자열을 화면에 출력한다. </p>
<p>∙ text: 화면에 출력할 문자열을 지정하는 속성
∙ typeface: 화면에 출력할 문자열의 폰트를 지정하는 속성
∙ textStyle: 문자열 효과로 normal(default), bold, italic 중 하나를 입력한다.
∙ textColor: 문자열의 색상을 16진수 RGB 포맷으로 지정할 때 사용한다.
∙ textSize: 문자열의 폰트 크기를 지정할 때 사용한다. 
∙ autoLink: TextView의 문자열을 분석해 자동으로 브라우저, 이메일 앱, 전화 앱이 실행된다. 
∙ maxLines: TextView에 긴 문자열을 대입할 때, 틀정 줄만큼만 출력할 때 사용한다. 
∙    ellipsize:  maxLines로 지정한 중만큼만 출력할 수 있는데, 이때 줄임 표시(...)를 하고 싶을 때, 문자열 줄임 표시를 자동화하는 속성이다. 속성값으로 end, start, middle 등을 지정하여 줄임 표시의 위치를 정할 수 있다.</p>
<p><strong>■ ImageView</strong>
화면에 이미지를 출력하고자 할 때 사용하는 뷰이다. </p>
<p>∙ src: 화면에 출력할 이미지를 지정하는 속성이다.
∙ maWidth, maxHeight: 화면에 출력할 이미지의 최대 크기를 지정하는 속성이다. adjustViewBounds라는 속성과 함께 사용해야 하며, adjustViewBounds에 따라 이미지의 가로세로 비율을 유지할 수 있다.
∙ adjustViewBounds: 이미지의 크기를 변경할 때 가로세로 비율을 유지할 지(true, false) 지정하는 속성이다.
∙ tint: 이미지 위에 다른 색상을 입힐 때 사용하는 속성이다. </p>
<p><strong>■ EditText</strong>
사용자에게 데이터를 입력받을 때 사용하는 뷰이다. TextView와 성격이 같아 TextView를 상속받아 작성되었다. </p>
<p>∙ lines: 처음 화면에 보일 때부터 특정 줄만큼 보이게 할 때 사용하는 속성이다. 만약 lines=&quot;3&quot;로 지정하면, 처음부터 세로 방향 3줄 입력 크기로 출력되고 더는 늘거나 줄지 않는다.
∙ maxlines: lines 속성과 차이가 있는데 lines=&quot;3&quot;로 지정하여도 처음 화면 보일 때는 한 줄 입력크기로 보인다. 
∙inputType: 아주 중요하고 자주 이용하는 속성으로 두 가지 목적으로 이용한다. 첫 번째 목적은 EditText에 글을 입력하기 위해 올라오는 키보드의 모드를 제어하는데 있고, 두 번째 목적은 사용자에게 한 줄 혹은 여러 줄 입력을 강제하는 데 있다.</p>
<p>✓inputType의 속성값<img src="https://velog.velcdn.com/images/so__ok/post/80a93bd0-1d76-4e02-b24a-e6cd57c7ba10/image.png" alt=""></p>
<p>∙ gravity
EditText에 입력할 글의 위치를 지정할 수 있다. 기본값은 left top이며 이를 center 혹은 right 등을 지정하여 위치를 조정한다.
gravity 속성은 EditText만을 위한 속성은 아니며, 모든 뷰에서 내용이 정렬되는 위치를 지정할 때 사용된다. </p>
<p>*<em>■ Button *</em>
Button은 화면에서 사용자의 이벤트를 처리하기 위한 가장 기본이 되는 뷰이다. 
사용자 클릭 이벤트는 Button에서 뿐만 아니라 모든 뷰에서 클릭 이벤트를 처리할 수 있다. 문자열을 출력한다는 의미에서 TextView의 서브클래스로 만들어져서 TextView에서 설정했던 대부분의 속성을 그대로 사용할 수 있다.
Button 하위 클래스로 CheckBox, RadioButton, ToggleButton 등이 있다.</p>
<p><strong>■ CheckBox, RadioButton</strong></p>
<p>▪︎ CheckBox
선택과 선택되지 않은 두 가지 상태를 표현하기 위한 뷰이다. TextView의 서브 클래스이므로 TextView에서 설정하는 textColor, textSize 등의 문자열 속성을 그대로 적용할 수 있다. </p>
<p>속성 설정 설명을 위해서 예시를 들어보자.</p>
<pre><code>&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;&gt;

    &lt;CheckBox
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/checkbox&quot;
        android:text=&quot;is unChecked&quot;/&gt;

&lt;/LinearLayout&gt;</code></pre><p>위 레이아웃 XML 파일은 CheckBox를 하나 준비한 코드다. 
문자열로 &quot;is unCheckBox&quot;라는 문자열을 출력하였다. CheckBox는 사용자에게 글이 아니라 true, false 값을 입력받을 때 사용하므로 코드에서 CheckBox의 상태를 획득하거나 조정할 필요가 있다. </p>
<p>이때 다음 함수를 이용한다.</p>
<p>∙isCheckBox(): 해당 Checkbox가 체크된 상태인지를 반환한다. true가 반환되면 체크된 상태, false가 반환되면 체크가 안 된 상태이다.
∙setChecked(): Checkbox의 체크 상태를 바꾸기 위한 함수로 매개변수 값을 true로 지정하면 체크 상태로 바뀌고, false로 지정하면 체크가 안 된 상태로 바뀐다. 
∙toggle(): 이 함수를 이용하면 현재 Checkbox의 상태와 상관없이 반대로 바뀐다. </p>
<p>또한, 때에 따라 사용자가 체크 상태를 바꾼 순간의 이벤트 처리로 상태를 파악해야 하는 경우 <strong>OnCheckedChangeListener</strong> 이벤트를 이용한다. </p>
<pre><code>public class MainActivity5 extends AppCompatActivity {

    CheckBox checkBox;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main5);

        checkBox=findViewById(R.id.checkbox);
        checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked){
                    checkBox.setText(&quot;is Checked&quot;);
                }else {
                    checkBox.setText(&quot;is unChecked&quot;);
                }
            }
        });

    }
}</code></pre><p>위의 코드는 체크 상태 변경 시 이벤트를 처리하는 코드이다. 체크 상태가 바뀔 때마다 onCheckedChanged() 함수가 자동으로 출력되며, 두 번째 매개변수인 boolean isChecked를 통해 체크 상태를 알려준다. 
체크 상태이면 Checkbox의 문자열을 &quot;is Checked&quot;, 체크 되지 않은 상태이면 &quot;is unChecked&quot;라고 조정한 코드이다. </p>
<p>▪︎RadioButton
Checkbox처럼 체크 상태를 표현하기 위한 뷰이다. 여러 개 중 하나만 선택할 수 있으며, RadioGroup 클래스가 제공된다. 같은 클래스에 묶인 RadioButton의 체크 상태를 파악하기 위해서 각각의 RadioButton에 id 값을 부여하는 것이 편리하다.
RadioButton에서 제공하는 함수는 다음과 같다. 
∙ check(): 매개변수로 체크하고자 하는 RadioButton의 id 값을 주면 해당 RadioButton이 체크된다. 
∙ clearCheck(): RadioGroup의 RadioButton의 체크 상태를 해제한다. 
∙ getCheckedRadioButtonId(): 체크된 RadioButton의 id 값을 획득한다.  </p>
<h4 id="◼︎-자바-코드">◼︎ 자바 코드</h4>
<pre><code>
public class MainActivity4 extends AppCompatActivity {


    CheckBox checkBox;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main4);

        TextView textView = findViewById(R.id.fontView);
        Typeface typeface = Typeface.createFromAsset(getAssets(), &quot;xmas.ttf&quot;);
        textView.setTypeface(typeface);

        checkBox = findViewById(R.id.checkbox);
        checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    checkBox.setText(&quot;is Checked&quot;);
                }else {
                    checkBox.setText(&quot;is unChecked&quot;);
                }
            }
        });
    }
}</code></pre><p>✏️ 저번주에 올린 줄 알았는데.. 비몽사몽해서 잘 못 봤나보다.. ㅜㅜ
다음주 부터는 근무일이 많아져서 새벽 공부 위주로 해야 할 것 같다... 
오늘은 하나만 더 올리고 알람어플 작업해야 한다..  깃허브 올리고 정신없는 하루</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[사용자 인터페이스 2]]></title>
            <link>https://velog.io/@so__ok/%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-2</link>
            <guid>https://velog.io/@so__ok/%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-2</guid>
            <pubDate>Thu, 17 Nov 2022 04:27:51 GMT</pubDate>
            <description><![CDATA[<h2 id="2-뷰-아키텍처">2. 뷰 아키텍처</h2>
<p>액티비티 화면을 구성하기 위해 다양한 뷰 클래스들을 이용한다. 안드로이드 라이브러리에는 Button, TextView, EditText, ImageView, Spinner, ListView 등 많은 뷰 클래스가 준비되어 있고 안드로이드 버전이 변경되면서 계속 추가되고 있다. </p>
<p>이번 글에서는 안드로이드 뷰 클래스들이 어떤 구조로 설계되어 있는지에 대해 간단하게 다뤄보고 다음 글부터는 실습 위주의 간단한 설명으로 벨로그를 작성하려 한다. </p>
<h3 id="뷰의-계층구조">뷰의 계층구조</h3>
<p>뷰의 기본 구조는 뷰 객체 간의 계층으로 이루어져 있다. 
<img src="https://velog.velcdn.com/images/so__ok/post/b8c85cac-f7de-4e76-a37f-34d6f493e009/image.png" alt=""></p>
<p>위 그림은 안드로이드 뷰 클래스의 기본 골격을 클래스 다이어그램(Class Diagram)으로 나타낸 모습이다. </p>
<p>각 클래스에 대한 자세한 설명이다.
▪︎ View: 안드로이드 뷰 클래스의 최상위 클래스이다. 최상위 클래스 명이 View여서 안드로이드에서는 Button같은 UI 위젯 클래스를 뷰라고 부른다. 액티비티에 출력되는 클래스는 모두 이 View의 서브 클래스여야 한다. 
▪︎ ViewGroup: 뷰의 서브 클래스여서 화면에 출력되지만, 뷰그룹 자체만의 UI는 가지지 않아서 화면에 아무것도 나오지 않는다. 그렇다고 필요가 없는 것은 아니다. 뷰 그룹의 역할은 UI출력이 아니라 다른 뷰 여러 개를 뷰그룹에 포함(Add)하여 한꺼번에 제어하기 위한 목적으로, 일반적으로 컨테이너(Container) 기능을 담당한다. 실제 뷰그룹도 이용되지만, 뷰그룹의 서브 클래스들인 레이아웃 클래스들이 사용된다. 
▪︎TextView:  특정 UI를 출력할 목적으로 제공되는 클래스 중 대표적이다. </p>
<p><img src="https://velog.velcdn.com/images/so__ok/post/3b74b607-1d71-47a2-83dd-dfa381ed4977/image.png" alt=""></p>
<p>위 사진은 TextView 같은 객체를 뷰그룹 객체에 포함하여 출력하는 구조를 잘 이해하기 위해 윈도우 탐색 창의 파일과 디렉터리의 계층구조로 나타낸 것이다. 
앞에서 설명한 뷰의 계층구조와 동일한 구조임을 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/so__ok/post/a757d51c-e7d1-4b0f-973b-5d48315a75f5/image.png" alt=""></p>
<p>위 윈쪽 그림을 보면 파일 여러 개를 디렉터리에 포함하여 한꺼번에 제어하듯이, 안드로이드에서도 뷰 클래스의 객체들을 계층구조로 묶어서 사용한다. 
결국, &#39;ViewGroup 1&#39;을 화면에 출력하면 전체가 함께 출력된다.</p>
<h3 id="뷰-계층구조-구현">뷰 계층구조 구현</h3>
<p><strong>▪︎ 레이아웃 XML로 계층구조 구현</strong>
뷰의 계층구조를 레이아웃 XML로 구현하려면 태그의 상하 관계를 이용하면 된다. 서브 태그로 등록한 뷰 객체가 상위 태그 객체에 포함된다고 생각하면 된다. </p>
<pre><code>&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:orientation=&quot;horizontal&quot;&gt;

    &lt;Button
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;Button 1&quot;/&gt;
    &lt;Button
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;Button 2&quot;/&gt;

&lt;/LinearLayout&gt;</code></pre><p>레이아웃 XML을 위와 같이 작성하면 LinearLayout(뷰그룹)에 Button(뷰)이 두 개 포함된다. </p>
<p><strong>▪︎ 자바 코드로 계층구조 구현</strong>
자바 코드에서 직접 뷰릐 계층구조를 구현하려면 addView() 함수를 이용한다.</p>
<pre><code>public class MainActivity3 extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);

        LinearLayout linearLayout = new LinearLayout(this);
        Button bt1 = new Button(this);
        linearLayout.addView(bt1);
        Button bt2 = new Button(this);
        linearLayout.addView(bt2);

    }
}</code></pre><p>✏️ 다음 장에서는 기초 뷰를 활용한 실습을 위주로 작성할 것이다. 
오늘 목표치 벨로그 4개! 화이팅이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[뷰 기초 속성]]></title>
            <link>https://velog.io/@so__ok/%EB%B7%B0-%EA%B8%B0%EC%B4%88-%EC%86%8D%EC%84%B1</link>
            <guid>https://velog.io/@so__ok/%EB%B7%B0-%EA%B8%B0%EC%B4%88-%EC%86%8D%EC%84%B1</guid>
            <pubDate>Mon, 14 Nov 2022 17:18:14 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/so__ok/post/8ef1a871-e649-4eee-8dcb-e908c4196d88/image.png" alt=""></p>
<p>왼쪽 초기 상태
가운데 visible
오른쪽 invisible</p>
<p>두 가지 버튼을 만들어 <strong>VISIBLE TRUE</strong>     버튼을 클릭하면 &quot;hello world&quot;가 나타나고 <strong>VISIBLE FALSE</strong> 버튼을 클릭하면 뷰의 크기만 확보한 채로 화면에서 보이지 않는다. </p>
<h3 id="ui-구성을-위해-레이아웃-xml"><strong>UI 구성을 위해 레이아웃 XML</strong></h3>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:orientation=&quot;vertical&quot;&gt;

    &lt;Button
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;VISIBLE true&quot;
        android:id=&quot;@+id/btn_visible_true&quot;/&gt;

    &lt;TextView
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/text_visible_target&quot;
        android:text=&quot;hello world&quot;
        android:background=&quot;#FF0000&quot;
        android:textColor=&quot;#FFFFFF&quot;
        android:padding=&quot;16dp&quot;
        android:visibility=&quot;invisible&quot;/&gt;

    &lt;Button
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/btn_visible_false&quot;
        android:text=&quot;VISIBLE FALSE&quot;/&gt;

&lt;/LinearLayout&gt;</code></pre><p>버튼 두 개를 넣고 가운데 TextView를 넣었다. 각 뷰에 layout_width, layout_height가 설정되어 있으며, TextView에는 background, textColor 속성으로 바탕색과 전경색을 설정하였다. </p>
<p>visibility 속성은 &quot;invisible&quot;로 지정하여 화면에 안 보이게 하였다. </p>
<h3 id="자바-코드">자바 코드</h3>
<pre><code>package com.example.part3_1;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity3 extends AppCompatActivity implements View.OnClickListener{
// 버튼 클릭 이벤트

    Button trueBtn;
    TextView targetTextView;
    Button falseBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);

// View 객체 획득 
        trueBtn = findViewById(R.id.btn_visible_true);
        targetTextView = findViewById(R.id.text_visible_target);
        falseBtn= findViewById(R.id.btn_visible_false);

// Button 이벤트 등록
        trueBtn.setOnClickListener(this);
        falseBtn.setOnClickListener(this);
    }
// 버튼 이벤트 콜백 함수
        @Override
        public void onClick(View v){
            if (v == trueBtn) {
               //  trueBtn이 눌리면 targetTextView를 visible 상태로 변경
               targetTextView.setVisibility(View.VISIBLE);
            }else if (v ==falseBtn){
                // falseBtn이 눌리면 targetTextView를 invisible 상태로 변경
                targetTextView.setVisibility(View.INVISIBLE);
            }
        }

}</code></pre><p>✏️ 오늘 이렇게 뷰의 기초 속성을 이용하여 실습을 해 보았다. 
아직 velog에 작성하는게 능숙하지는 못 하지만 이 책의 실습내용을 모두 다루다 보면 점차 실려과 벨로그 작성 능력도 향상될 것이라고 생각한다. 수요일부터는 깃허브에도 지금까지 실습했던 내용도 올리고 간단한 프로젝트를 시작해 볼 생각이다. 앱개발은 워낙 실습할 수 있는 인강의 선택 폭이 넓지 않은거 같다.. 이렇게 관심이 가게 될 줄 알았으면 국비지원으로 수업받을 때 더 열심히 할 걸 후회된다. 앞으로라도 꾸준히 하면 좋아지겠죠 ㅜㅜㅜㅜ </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[사용자 인터페이스]]></title>
            <link>https://velog.io/@so__ok/%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@so__ok/%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Mon, 14 Nov 2022 16:51:14 GMT</pubDate>
            <description><![CDATA[<h2 id="1-ui의-기본-구조">1. UI의 기본 구조</h2>
<p>안드로이드 애플리케이션의 가장 기본적인 소프트웨어 아키텍처(architecture)는 컴포넌트(component) 기본의 개발인 것이다.</p>
<p>앞에서 다뤄 본 것처럼 안드로드에서는 &#39;액티비티&#39;, &#39;서비스&#39;, &#39;콘텐츠 프로바이더&#39;, &#39;브로드캐스트 리시버&#39; 등 4개의 컴포넌트를 취사선택해서 앱을 구성한다. 
이 중 화면을 출력하는 컴포넌트는 액티비티(activity)뿐이다. 
결국, 안드로이드에서 화면을 출력하려면 액티비티를 만들어야 하고, 이 액티비티가 런타임 때 실행되어야 한다. </p>
<h3 id="액티비티-⏤-뷰-구조">액티비티 ⏤ 뷰 구조</h3>
<p>앱의 실행 단위인 컴포넌트인 액티비티는 버튼, 문자열, 이미지 등을 출력해주어야만 화면이 나온다. 이러한 클래스를 뷰(View) 클래스라고 부른다. 
결국, 안드로이드 UI프로그램은 다양한 뷰를 화면에 출력하는 구조이다. 
<img src="https://velog.velcdn.com/images/so__ok/post/65ea949f-b7d6-40d2-9ca8-8dd014f16053/image.png" alt=""></p>
<p>🟥 Activit 화면
🟦 View
위 그림은 페이스북 메신저 앱의 첫 화면이다. 
하나의 액티비티 클래스에 5개의 뷰 클래스로 화면을 구성하여 출력한 결과이다. 
액티비티에서 뷰를 출력하는 기능을 제공하는 함수는 <strong>setContentView()</strong>이다.</p>
<p>이 함수는  두가지 형태로 제공한다.
▪︎ public void setContentView(View view)
▪︎ public void setContentView(int layoutResId)</p>
<p>액티비티 내에서 화면 출력을 위한 setContentView() 함수를 실행하지 않는다면 에러는 발생하지 않지만 화면에는 아무것도 나오지 않는다. 액티비티가 화면이고, 액티비티 화면의 내용이 뷰라고 보면 된다.</p>
<h3 id="ui-프로그램-작성-방법-자바-코드-vs-레이아웃-xml">UI 프로그램 작성 방법: 자바 코드 VS 레이아웃 XML</h3>
<p>안드로이드에서 UI를 작성하는 방법은 액티비티의 자바코드로 작성하는 방법과 레이아웃 XML을 작성하는 방법이 있다. 
<img src="https://velog.velcdn.com/images/so__ok/post/d53800f4-5533-43e6-a261-da48344a50be/image.png" alt=""></p>
<p>다음 버튼 2개가 있는 화면을 자바 코드와 레이아웃 XML으로 각각 출력해보자. </p>
<h4 id="◼︎-자바-코드로-화면-구성">◼︎ 자바 코드로 화면 구성</h4>
<p>자바 코드로 모든 뷰를 직접 생성하며, 메서드를 이용하여 뷰 설정을 일일이 지정하는 방법이다. 이렇게 준비한 뷰를 setContentView() 함수의 매개변수로 넘겨서 화면을 출력한다. 
<img src="https://velog.velcdn.com/images/so__ok/post/81e6aedc-10a5-42ae-a4c8-491835d99cc9/image.png" alt=""></p>
<p>새로 생성한 모듈에 자동으로 만들어진 MainActivity.java 파일을 작성했다. 위의 코드는 LinearLayout에 버튼 2개를 생성해서 추가한 다음, 화면에 출력하는 코드다. 버튼 2개가 LinearLayout에 포함되었으므로 setContentView() 함수에 linear만 전달해도 버튼 2개가 함께 출력된다. </p>
<h4 id="◼︎-레이아웃-xml로-화면-구성">◼︎ 레이아웃 XML로 화면 구성</h4>
<p>안드로이드 스튜디오에서 레이아웃 XML 파일을 작성하는 방법은 두 가지이다. 하나는 디자인(Design)모드로서 개발자가 필요한 뷰를 마우스로 끌어놓기(Drag &amp; Drop)하는 방법이고, 다른 하나는 텍스트(Text)모드로 직접 XML 코드를 작성하는 방법이다. 여기서는 직접 XML 코드를 작성할 것이다. 
<img src="https://velog.velcdn.com/images/so__ok/post/99c0a8b4-598c-4303-9b01-c945ef8d0a9c/image.png" alt=""></p>
<p>레이아웃 XML 파일에서 작성할 때는 뷰 클래스명을 XML의 태그명으로 작성하고, 필요한 속성을 XML attribute로 지정하여 작성하면 된다. </p>
<h3 id="뷰의-기초-속성">뷰의 기초 속성</h3>
<p>UI 구성을 레이아웃 XML로 작성할 때 뷰 태그에 다양한 속성을 추가할 수 있으며, 속성들은 뷰에 따라 다르다. 모든 뷰에 공통으로 지정할 수 있는 중요 속성들에 대해 알아보자. </p>
<p>안드로이드에서 가장 많이 이용되는 뷰 중 하나는 <strong>TextView</strong>이며,  화면에 문자열을 출력하는 뷰이다. 레이아웃 XML에 TextView를 사용하는 예를 통해 뷰의 중요 속성을 알아보자. </p>
<h4 id="◼︎-id-속성">◼︎ id 속성</h4>
<p>뷰의 식별자 속성으로 필수 속성은 아니며 필요할 때 추가할 수 있다.</p>
<pre><code>&lt;TextView
    android:layout_width=&quot;wrap_contnet&quot;
    android:layout_height=&quot;wrap_contnet&quot;
    android:text=&quot;hello&quot; /&gt;</code></pre><p>화면에 뷰의 내용을 출력만 하는 목적이라면 id 속성을 생략해도 화면 출력에는 문제가 없지만 이렇게 하면 레이아웃 XML에 등록하여 자동으로 생성된 뷰 객체를 자바 코드에서 이용할 수 없다. </p>
<p>뷰 객체를 자바 코드에서 직접 생성한게 아니기 때문에 객체명을 알 수 없기 때문이다. </p>
<p>이때, 이용하는 속성이 id이다. </p>
<pre><code>&lt;TextView
    android:id=&quot;@+id/myText&quot;
    android:layout_width=&quot;wrap_contnet&quot;
    android:layout_height=&quot;wrap_contnet&quot;
    android:text=&quot;hello&quot; /&gt;</code></pre><p>위 코드처럼 android:id=&quot;@+id/myText&quot;라고 설정하면 개발자가 임의의 이름으로 id 값을 하나 부여한 것이 된다. 이렇게 지정한 id값은 앱의 R.java 파일에 등록된다. </p>
<p>이제 자바 코드에서 뷰 객체를 획득해서 이용할 수 있다. 
자바 코드에서 findViewById() 함수를 사용한다. </p>
<pre><code>TextView myTextView=findViewById(R.id.myText);</code></pre><p>정리하자면, 자바 코드에서 setContentView() 함수를 호출하면 레이아웃 XML 파일의 내용을 화면에 출력하고, XML에 정의한 객체가 자동으로 생성된다. 이렇게 생성된 객체는 R.java의 변수, 즉 XML에서 등록한 id값을 매개변수로 하여 findViewById() 함수로 획득해서 사용할 수 있다. </p>
<h4 id="◼︎-layout_width-layout_height-속성">◼︎ layout_width, layout_height 속성</h4>
<p>뷰 크기를 지정하는 속성으로 가로, 세로 크기를 결정하며, 선언하지 않으면 빌드할 때 에러가 발생한다. </p>
<pre><code>&lt;TextView
    android:layout_width=&quot;wrap_contnet&quot;
    android:layout_height=&quot;wrap_contnet&quot;
    android:text=&quot;hello&quot; 
    android:background=&quot;#0000FF&quot;
    android:textColor=&quot;#FFFFFF&quot;/&gt;</code></pre><p>위의 코드는 layout_width, layout_height를 이용하여 &quot;hello&quot;라는 문자열이 화면에 보일 때, 뷰가 화면에서 차지해야 하는 크기를 지정하는 예이다. </p>
<p>크기 속성값은 다음 4가지로 지정할 수 있다.
<strong>▪︎ wrap_contnet
▪︎ match_parent
▪︎ fill_parent
▪︎ 100px</strong></p>
<p><strong>match_parent</strong>와 <strong>fill_parent</strong>는 의미상 동일하므로 어떤 속성값을 주더라도 결과는 같다. 이 값의 의미는 해당 뷰의 크기를 부모 계층의 뷰가 지정한 크기에 꽉 들어차게 자동으로 결정하라는 의미다. </p>
<p><strong>wrap_content</strong>는 해당 뷰의 내용을 화면에 보이기 위한 적절한 크기를 계상해서 결정하라는 의미다. 문자열 폰트 크기를 늘리면 그만큼 뷰의 크기도 늘어난다. </p>
<p>또는 100xp처럼 수치로 뷰의 크기를 직접 설정할 수도 있다. 
<img src="https://velog.velcdn.com/images/so__ok/post/e27cc522-5598-4e70-8a6f-a920370c09e8/image.png" alt=""></p>
<p>위의 코드는 LinearLayout 하위에 TextView 두 개가 있다. 
결과는 다음과 같다. 
<img src="https://velog.velcdn.com/images/so__ok/post/be3f499a-b419-4ebc-93de-311dd2898323/image.png" alt=""></p>
<h4 id="◼︎-margin-padding-속성">◼︎ margin, padding 속성</h4>
<p>margin은 뷰와 뷰 사이의 간격을 지정하는 속성이며, padding은 뷰 내부에서 내용과 뷰의 테두리 간의 간격을 지정하는 속성이다. 
뷰의 기본 margin과 padding 값이 있는데, 이를 증감하고자 할 때 사용하는 속성이다. </p>
<p>margin과 padding 속성을 이용하면 간격이 네 방향 모두 같은 크기로 설정되며, 단일 방향의 간격만 조정하고 싶다면 다음 속성을 이용하면 된다. 
▪︎ 단일 방향 margin 속성
: layout_marginleft, layout_marginRight, layout_marginTop, layout_marginBottom
▪︎ 단일 방향 padding 속성 
: paddingLeft, paddingRight, paddingTop, paddingBottom</p>
<h4 id="◼︎-clickable-속성">◼︎ clickable 속성</h4>
<p>안드로이드의 모든 뷰는 기본적으로 clickable 속성을 지정하지 않아도 사용자의 클릭 이벤트나 롱클릭 이벤트에 반응하지만 TextView, ImageView은 명시적으로 지정(true)하지 않으면 클릭 이벤트가 발생하지 않는데,  이때 사용되는 것이 clickable 속성이다. </p>
<h4 id="◼︎-visibility-속성">◼︎ visibility 속성</h4>
<p>뷰를 화면에 출력할지를 지정하는 속성이다. 기본값은 &quot;true&quot;이며, &quot;invisible&quot;, &quot;gone&quot;으로 지정하면 화면에 안 보이게 할 수 있다. </p>
<p>gone과 invisible의 차이점은 둘 다 화면에 출력은 안 되지만, gone은 확보하지 못하고 invisible은 크기 확보가 가능하다. </p>
<p>visibility 속성은 액티비티 화면을 레이아웃 XML로 구성할 때 초기에 특정 뷰를 화면에 출력하지 않으려는 의도로 사용한다. 이때 자바 코드에서는 setVisibility() 함수를 이용하여 뷰의 표시 상채를 조정한다. 
※ visibility = &quot;gone&quot;</p>
<p>✏️ 하나는 이론을 정리하고 다음 글에 여기서 다루었던 뷰와 속성을 이용해 작은 활용을 해 볼 생각이다. 초반이라 이론적인 내용을 많이 쓰게되는데 시간이 너무 들어서 다음부터는 실습위주의 글을 쓰려고 한다. 
그래도 어제 오늘 기록할 수 있어서 너무 다행이고 뿌듯하다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[개발 디렉터리와 파일 구조]]></title>
            <link>https://velog.io/@so__ok/%EA%B0%9C%EB%B0%9C-%EB%94%94%EB%A0%89%ED%84%B0%EB%A6%AC%EC%99%80-%ED%8C%8C%EC%9D%BC-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@so__ok/%EA%B0%9C%EB%B0%9C-%EB%94%94%EB%A0%89%ED%84%B0%EB%A6%AC%EC%99%80-%ED%8C%8C%EC%9D%BC-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Sun, 13 Nov 2022 16:49:18 GMT</pubDate>
            <description><![CDATA[<h3 id="2-앱의-디렉터리와-파일"><strong>2. 앱의 디렉터리와 파일</strong></h3>
<p> 안드로이드 스튜디오에서 앱을 개발하면 실제 많은 디렉터리와 파일이 만들어 진다. 하지만 대부분 개발자와 관련이 없으며, 안드로이드 스튜디오 내부에서 빌드 작업 등을 목적으로 사용된다. 이곳에서는 개발자가 알아야 하는 디렉터리와 파일을 소개하려 한다. </p>
<p> <img src="https://velog.velcdn.com/images/so__ok/post/b9df5789-40d6-4445-a501-de74b9a3c1e8/image.png" alt=""></p>
<p> 위 그림은 안드로이드 스튜디오의 탐색 창이다. 내가 했었던 프로젝트 파일을 가져 온 것이며 파일명을 Mission1으로 지정하였다. 안드로이드 앱을 개발하려면 이 디렉터리와 파일이 각각 어떤 역할을 하는지 이해해야한다. </p>
<p>∙ <strong>AndroidManifest.xml</strong> : 앱의 메인 환경 파일
∙ <strong>java/Mission1.java</strong> : 화면 구성을 위한 액티비티 컴포넌트로 실제 이 파일이 수행되어 화면에 UI가 출력된다. 
∙ <strong>res</strong> : 앱의 모든 리소스 파일은 res 폴더 하위에 위치한다. 
∙ <strong>res/drawable</strong> : 리소스 중 이미지 파일을 저장하기 위한 폴더이다.
∙ <strong>res/layout *<em>: 리소스 중 UI 구성을 위한 레이아웃 XML 파일을 위한 폴더이다.
∙ *</em>res/mipmap</strong> : 리소스 중 앱의 아이콘 이미지를 위한 폴더이다.
∙ <strong>res/valuse</strong> : 리소스 중 문자열 값 등을 위한 폴더이다. </p>
<p>앱의 메인 환경 파일은 AndroidManfest.xml 이며, 이곳에 정의된 대로 앱이 실행된다. 개발자는 AndroidManfest.xml 파일을 열어 분석하거나 수정하면서 앱을 개발한다. 또한, 개발자가 작성하는 모든 자바 파일(컴포넌트 자바 파일 포함)은 java 폴더에 위치하며, 모든 리소스 파일은 res 하위 폴더에 위치한다. </p>
<h4 id="rjava의-이해">R.java의 이해</h4>
<p>안드로이드 앱을 작성할 때 많은 리소스 파일을 이용한다. 모든 리소스 파일은 res 폴더 하위에 있어야 하며, 이름이 지정되어 있어 개발자 임의로 이름을 추가할 수 없다. 또한, 각 폴더의 서브 폴더를 만들 수 없다.<br>하지만 개발을 하다보면 수백 개의 리소스가 만들어지는데, 코드 영역에서 이를 식별할 방법이 필요하다. 
이를 도와주기 위한 파일이 R.java 이다. R.java 파일은 툴이 자동으로 만들어주며, 이 파일을 열어보면 단순하게 int형 변수만 선언되어 있다. 
개발자가 만들지도 않은 파일을 열어서 수정하는 것도 아니라서 개발시 R.java 파일을 분석하지도 않는다.  그러나 R.java 파일을 중심으로 리소스가 어떻게 관리되는지, 어떠한 역할을 하는지는 안드로이드 학습 초반에 정리해 두면 좋다. </p>
<h3 id="3--그레이들gradle-파일">3.  그레이들(gradle) 파일</h3>
<p>앱을 개발할 때 테스트를 진행하거나 개발이 완료된 후 사용자에게 배포하려면 빌드 작업이 필요하다. 앱에 포함된 리소스와 자바 코드를 컴파일하고 준비된 키로 서명해서 배포용 파일인 APK 파일을 추출해야 한다. 
안드로이드 스튜디오에서는 이 모든 작업을 그레이들(gradle) 이라는 도구를 이용한다. </p>
<p><img src="https://velog.velcdn.com/images/so__ok/post/b2abae54-d9d6-4621-9282-19fc4bbbc01c/image.png" alt=""></p>
<p>개발자는 build.gradle 파일에서 그레이들 관련 설정을 할 수 있는데, 그레이들 파일은 위 그림 처럼 &#39;Gradle Scripts&#39; 영역에 있다.
그레이들 파일은 크게 프로젝트 수준과 모듈 수준으로 구분된다. 
안드로이드 스튜디오에서는 앱이 모듈 단위이며, 여러 모듈을 묶어서 관리하기 위한 개념이 프로젝트 개념이다.  프로젝트 수준의 그레이들 파일은 전체 프로젝트를 위한 설정이며, 모듈 수준의 그레이들 파일은 각 모듈을 위한 설정 파일이다. 모듈 하나당 하나의 그레이들 파일이 만들어 진다.</p>
<h4 id="settingsgradle"><strong>settings.gradle</strong></h4>
<p><img src="https://velog.velcdn.com/images/so__ok/post/0a7cd909-e3eb-43c2-922f-ecad317af27f/image.png" alt=""></p>
<p> 위 그림의 settings.gradle 파일은 그레이들에 모듈을 포함하여 스레이들이 모듈을 관리하고 빌드하게 설정하는 파일이다. </p>
<pre><code>include &#39;:app&#39;</code></pre><p>settings.gradle 파일을 열어보면 위의 코드처럼 그레이들이 관리하고 빌드해야 하는 모듈이 등록되어 있다. 
프로젝트에 여러 모듈을 만들어 개발하면 모듈을 만들 때마다 모듈명이 자동으로 settings.gradle에 포함되므로 개발자가 직접 조정하는 일은 많지 않다. </p>
<h4 id="프로젝트-수준의-그레이들">프로젝트 수준의 그레이들</h4>
<p>프로젝트 수준의 그레이들 파일은 안드로이드 참색 창에서 &#39;Gradle Scripts&#39; 영역의 최상위에 있는 build.gradle로, 옆에 프로젝트명이 표시된 파일이다. 
이 파일은 모든 모듈을 위한 최상위 설정을 목적으로 한다. 
<img src="https://velog.velcdn.com/images/so__ok/post/5c4f9f0e-80c4-4467-91ea-4793e9417d44/image.png" alt=""></p>
<p>build.gradle은 위처럼 작성된 파일인데, 대부분 모듈 수준의 그레이들 파일 설정이 자주 이루어지며, 프로젝트 수준의 그레이들 파일 설정은 비번하지 않다. </p>
<h4 id="모듈-수준의-그레이들">모듈 수준의 그레이들</h4>
<p>개발자가 스레이들을 설정한다면 대부분은 모듈 수준의 그레이들 파일이다.</p>
<p><img src="https://velog.velcdn.com/images/so__ok/post/40d09fd2-9462-44d2-83c6-446078695a66/image.png" alt=""></p>
<p>위의 파일은 모듈 수준의 그레이들 파일이며, 이곳의 설정은 모듈을 개발하고 빌드할 때 아주 중요한 역할을 한다. </p>
<p>∙ compileSdkVersion : 사용하는 컴파일러 버전
위의 설정은 빌드 시 이용되는 툴 버전 정보이다. 모듈 생성 시 기본으로 설정된 값을 사용하지만, 때에 따라 외부에서 작성된 코드를 안드로이드 스튜디오에 불러와서(import) 사용하는 경우, 위의 정보가 맞지 않아 빌드가 안 될 때가 있다. 이럴 때는 그레이들 파일을 수정하여 설치된 툴 버전을 명시하거나 SDK 매니저를 이용하여 맞는 버전의 툴을 추가 설치해야 한다.  </p>
<p>∙ application &quot;com.example.myapplication&quot; 
∙ minSdk 
∙ targetSdk 
∙ versionCode </p>
<p>위의 4가지 설정도 중요하다. </p>
<p><strong>applicationId</strong>는 앱의 식별자 정보를 설정하는 곳이다. 안드로이드 앱은 특정 키로 식별되지 않고, 개발자가 그레이들 파일에 applicationId 속성으로 지정한 문자열로 식별한다.  개발의 임의의 문자열이지만, 앱의 식별자이기 때문에 유일무이해야 한다.</p>
<p><strong>minSdk</strong>는 개발자가 이 앱을 몇 버전의 스마트폰까지 지원할 것인지에 대한 설정이다. 위의 그림에서 API Level이 25인데 이는 Android 7.0을 지칭하며, 앱이 이 버전까지 지원한다는 의미이다. </p>
<p><strong>targetSdk</strong>은 개발 시 이용하고 있는 라이브러리 버전이다. 위에는 31로 지정되어 있으며 31버전의 라이브러리로 개발하겠다는 의미이다. </p>
<p><strong>versionCode</strong>는 앱의 버전 정보이다. 스마트 폰 앱들은 수시로 버전이 업데이트되므로  앱을 사용자에게 서비스하는 도중 앱이 업데이트되면, 이 정보를 변경하여 다시 구글 play 스토어에 등록하면 된다. </p>
<p>그레이들 파일에서 개발자가 가장 많이 변경하는 부분이 dependencies이다. 
앱을 개발하면서 표준 라이브러리와 다양한 외부 라이브러리를 사용하게 되는데 이를 앱에서 이용하기 위해서는 꼭 그레이들 파일의 dependencies에 등록해 주어야 한다. </p>
<h3 id="4-hello-world-앱-코드-분석">4. Hello World 앱 코드 분석</h3>
<p><strong>AndroidManifest.xml</strong>
AndroidManifest.xml은 앱의 메인 환경 파일이다. 
개발자가 AndroidManifest.xml 파일을 열어 분석하고 수정하는 작업은 빈번하게 이뤄지며, 앱이 스마트폰에서 동작할 때도 이 파일에 정의된 대로 동작한다. </p>
<p><img src="https://velog.velcdn.com/images/so__ok/post/6ad080f4-3e88-4b92-9569-5fe46df87df2/image.png" alt=""></p>
<p>AndroidManifest.xml 파일에서 각 구성요소의 의미를 살펴보자. </p>
<p>package=&quot;com.example.helloworld&quot;
앱의 식별자 정보이다. 이 정보로 앱을 식별한다. 그레이들 파일에 설정된 applicationId의 값과 같으며 값은 유일무이해야 한다. </p>
<p>*<em>application *</em>
앱의 구성요소를 등록하기 위한 태그이다. 
안드로이드 앱은 컴포넌트 기반의 개발이다. 안드로이드 컴포넌트 클래스는 개발자 클래스이지만, 생명주기를 안드로이드 시스템이 관리한다. 따라서 컴포넌트 클래스들은 AndroidManifest.xml에 등록해야 하며, application 태그의 하위 태그로 등록한다. </p>
<p><strong>android:icon=&quot;@mipmap/ic_launcher&quot;, android:label=&quot;@string/app_name&quot;</strong> 
application 태그에 등록된 속성으로, 사용자 스마트폰에 앱이 설치되었을 때 앱의 아이콘 이미지와 앱의 이름을 명시하기 위한 속성이다. 
XML 속성값이 @로 지정되었다는 건 res 하위 폴더의 리소스들을 지칭한다. </p>
<p><strong>activity android:name=&quot;MainActivity&quot;</strong>
앱의 액티비티 컴포넌트를 등록하기 위한 태그이다. 위의 AndroidManifefst.xml 파일에는 activity 태그가 하나만 등록되어 있으므로 이 앱은 하나의 액티비티로 구성된 앱이다. 또한 서비스, 프로바이더, 리시버 컴포넌트가 있다면 service, provider, receiver 태그로 등록해 주어야 한다.
activity 태그에 액티비티를 등록할 때 name 속성은 생략할 수 없으며, name 속성은 등록하고자하는 클래스명을 명시하기 위한 속성으로 위의 코드는 MainActivity라는 클래스를 액티비티로 등록하는 구문이다. </p>
<p><strong>intent-filter</strong>
intent-filter을 자세히 이해하기 위해서 다음 포스터에 상세하게 기재하려 한다. 이 곳은 간단하게 사용자가 앱의 아이콘을 클릭했을 때 실행되는 액티비티를 설정하는 용도로 이해하고 넘어가면 된다. </p>
<h4 id="mainactivityjava">MainActivity.java</h4>
<p>사용자가 앱을 클릭했을 때 실행되는 액티비티 클래스이다. 즉, 화면을 구성을 주목적으로 하는 클래스이다. 
<img src="https://velog.velcdn.com/images/so__ok/post/1e5e3706-42d8-46b4-a049-74d58947dc48/image.png" alt=""></p>
<p>액티비티 클래스들은 Activity를 상속받아 작성하는데, 위의 코드는 AppCompatActivity를 상속받아 작성하였다. 액티비티가 실행되면 자동으로 onCreat() 함수가 호출되는데, 이 함수의 setContentView() 함수가 화면 출력 함수이다. R.layout.activity_main을 매개변수로 지정하였으므로 res/layout.activity_main.xml의 구성대로 액티비티 화면이 출력된다. </p>
<p>✏️ 오늘 2개의 포스터를 올렸는데 이제 이론을 막 벗어나서 너무 후련하다.. 이렇게 작성한게 누군가한테 꼭 도움이되었으면 좋겠다. 앞에 내용은 공부한지 오래되서 표면적인 의미만 기억하고 있었는데 다시 한번 정리하며 읽어보니 꽤 유익한 정보를 얻을 수 있었다. 내일은 전에 만들었던 기본적인 앱에 대해 다뤄 볼 생각이다. 일이 끝나고 작성해야 해서 새벽 쯤에야 올릴 수 있을 듯 하다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[안드로이드 앱 개발 특징]]></title>
            <link>https://velog.io/@so__ok/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%B1-%EA%B0%9C%EB%B0%9C-%ED%8A%B9%EC%A7%95</link>
            <guid>https://velog.io/@so__ok/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%B1-%EA%B0%9C%EB%B0%9C-%ED%8A%B9%EC%A7%95</guid>
            <pubDate>Sun, 13 Nov 2022 13:50:38 GMT</pubDate>
            <description><![CDATA[<h3 id="1-안드로이드의-특징">1. 안드로이드의 특징</h3>
<p> 안드로이드는 다른 소프트웨어 개발 분야와 비교했을 때 많은 부분이 오픈되어 있다는 특징이 있다. 특히 애플의 iOS와 비교했을 때, 이 특징은 더욱 크게 느껴진다. 다음은 주요 특징들이다.</p>
<h4 id="▪︎-안드로이드는-공개-운영체제인-리눅스-기반이다">▪︎ 안드로이드는 공개 운영체제인 리눅스 기반이다.</h4>
<h4 id="▪︎-안드로이드-앱-개발은-자바와-코틀린을-이용해-개발한다">▪︎ 안드로이드 앱 개발은 자바와 코틀린을 이용해 개발한다.</h4>
<h4 id="▪︎-운영체제의-핵심-코드-라이브러리-코드-스마트폰에-기본으로-탑재된----구글에서-만든-앱-코드-등-대부분-코드가-오픈되어-있다">▪︎ 운영체제의 핵심 코드, 라이브러리 코드, 스마트폰에 기본으로 탑재된    구글에서 만든 앱 코드 등 대부분 코드가 오픈되어 있다.</h4>
<h4 id="▪︎-안드로이드-플랫폼-기반의-스마트폰을-여러-제조업체에서-만들-수-있다">▪︎ 안드로이드 플랫폼 기반의 스마트폰을 여러 제조업체에서 만들 수 있다.</h4>
<h4 id="▪︎-개발자가-만든-앱은-구글-play-스토어뿐만-아니라-다양한-방법으로-사용자에게-배포할-수-있다">▪︎ 개발자가 만든 앱은 구글 play 스토어뿐만 아니라 다양한 방법으로 사용자에게 배포할 수 있다.</h4>
<h4 id="▪︎-안드로이드-기반의-모든-애플리케이션은-평등하다는-사상으로-스마트폰에-기본-탑재된-앱과-개발자들이-만드는-앱이-같은-환경에서-같은-api를-이용한다">▪︎ 안드로이드 기반의 모든 애플리케이션은 평등하다는 사상으로 스마트폰에 기본 탑재된 앱과 개발자들이 만드는 앱이 같은 환경에서 같은 API를 이용한다.</h4>
<p> 이 처럼 많은 부분이 오픈되어 있다는 점은 소프트웨어 개발자 관점에서 장점일 수 있다.</p>
<p> 다음은 <strong>안드로이드의 단점</strong>에 대해 알아보자. </p>
<p> 개발자가 느끼는 가장 큰 단점 중 하나가 기기의 <strong>파편화(Fragmentation)</strong>이다. 스마트폰 제조사들이 스마트폰을 제조할 때 구글에서 만든 플랫폼을 그대로 사용하지 않고 수정과 추가 과정을 통해 제작한다. 
 따라서 표준 API로 개발된 코드가 특정 스마트폰에서 수행되지 않거나 에러가 발생할 수 있다.
 이러한 기기 파편화 때문에 테스트의 중요성이 더욱  커졌다. </p>
<p> 또 다른 대표적인 단점으로는 <strong>멀티테스킹(Multitasking)</strong>이 있다. 
 iOS와 다르게 안드로이드는 여러 프로세스가 동시에 작동할 수 있다. 어떤 프로세스가 화면을 점유하여 사용자가 이용하고 있어도 우리의 앱이 백그라운드에서 동작하여 계속 업무를 수행할 수 있다.  하지만  이는 개발자 관점에서는 이점이지만 사용자 관점에서는 백그라운드에서 너무 많은 앱이 수행됨으로써 배터리 소비, 네트워크 트래픽 증가, 메모리 부족 등의 현상이 발생할 수 있어 단점일 수 있다. </p>
<h3 id="2-안드로이드의-플랫폼-아키텍처">2. 안드로이드의 플랫폼 아키텍처</h3>
<h4 id="안드로이드-아키텍처">안드로이드 아키텍처</h4>
<p>앱 개발자 관점에서는 안드로이드 플랙폼의 아키텍처를 자세히 응용할 필요는 없다. 안드로이드 플랫폼이 어떻게 설계되었는지, 앱이 어떤 환경에서 수행되는지 둥 상식 수준에서 알아두면 좋다. 
<img src="https://velog.velcdn.com/images/so__ok/post/d73fb4c7-663e-471b-93e1-c57293f6bbfb/image.png" alt="">
그림) 안드로이드 소프트웨어 아키텍처 스택</p>
<p>안드로이드 플랙폼은 리눅스 커널 기반이다. HAL(Hardware Abstaction Layer)은 자바 API 프레임워크에 하드웨어 기능을 이용한 표준 인터페이스를 제공한다. 자바 API 프레임워크에서 하드웨어 기기(카메라, 블루투스 등)를 이용하기 위한 코드가 실행되면 내부적으로 HAL의 라이브러리 모듈이 로딩되어 처리된다. </p>
<p>안드로이드 런타임(Android Runtime)은 ART 기상 머신을 이용하며 그 위에 일반 어플리케이션 개발 시 이용할 수 있는 자바 API 프레임워크를 제공한다. 대부분의 앱 개발자들은 이 자바 API 프레임워크에서 제공하는 다양한 클래스를 이용해 앱을 개발한다. </p>
<h4 id="안드로이드-런타임art">안드로이드 런타임(ART)</h4>
<p>자바로 개발된 다른 애플리케이션은 런타임 때 JVM(Java Virtual Machine)이 수행하지만, 안드로이드의 VM(Virtual Machine)은 ART(Android Runtime)을 이용한다. 참고로 ART는 API Level 21(Android 5.0)에서 새로 추가된 VM이며, 이전 버전의 VM은 Dalvik이었다. ART는 앱을 실행 DEX 파일을 실행한다. 일반적으로 자바를 이용하여 개발하면 실행 시 JVM이 class 파일을 해석하는데, 안드로이드는 개발 언어만 자바를 이용한다고 보면 된다. 
<img src="https://velog.velcdn.com/images/so__ok/post/9ffa0ee7-f14b-4bee-bdf3-7652bc037a00/image.png" alt=""></p>
<p>자바로 개발된 개발자 코드는 컴파일러(compiler)가 자동으로 DEX 파일로 변경하며, 런타임 때 ART가 이 DEX 파일을 해석하여 수행하는 구조이다. </p>
<p>※ 달빅 가상 머신(영어: Dalvik virtual machine 댈빅 버추얼 머신[*])은 레지스터 머신 형태의 (register-based) 가상 머신이다. 댄 본스타인이 다른 구글 엔지니어들의 도움 하에 설계/구현하였다. 현재 안드로이드 (4.4.4이전)휴대 전화 플랫폼에 들어간다.</p>
<h4 id="애플리케이션-프레임워크">애플리케이션 프레임워크</h4>
<p>앱을 개발할 때 안드로이드 앱 개발자 관점에서 가장 중요한 요소는 &#39;자바 API 프레임워크&#39; 이다. 자바 API 프레임워크는 개발자가 안드로이드 앱을 만들 때 이용하는 표준 라이브러리라고 생각하면 된다. 
많은 기능의 라이브러리 클래스를 제공하지만, 대표적으로 UI를 구성할 수 있는 View 클래스부터 리소스 관리, 데이터 영속화 들의 기능을 제공한다. 
앱 개발자들은 하위의 커널이나 시스템 라이브러리를 직접 이용할 필요없이, 자바 API 프레임워크에서 제공하는 클래스를 이용하여 앱의 모든 기능을 구현할 수 있다. </p>
<h3 id="3-컴포넌트-기반-개발">3. 컴포넌트 기반 개발</h3>
<h4 id="컴포넌트-란">컴포넌트 란?</h4>
<p>안드로이드 앱의 아키텍처에서 가장 큰 특징은 컴포넌트 기반이라는 것이다. 컴포넌트의 개념과 특징을 잘 이해하면 이후 안드로이드 앱을 개발할 때 큰 발판으로 이용할 수 있을 것이다. </p>
<p><strong>▪︎ 컴포넌트는 앱의 구성 단위이며, 컴포넌트 여러 개를 조합하여 하나의 앱을 만든다.</strong> 
안드로이드 앱에서 컴포넌트의 물리적 모습은 클래스이다. 안드로이드 클래스는 컴포넌트와 일반 클래스로 나뉘는데, 이 둘의 차이는 클래스의 생명주기를 누가 관리하는지에 있다. 일반 클래스의 생명주기는 개발자 코드로 관리한다.
즉, 필요한 순간 new 연산자로 생성해 이용하고, 필요 없는 순간 null을 대입해서 소멸시킨다. 안드로이드 컴포넌트는 똑같은 클래스라도 생명주기를 개발자 코드로 관리하지 않고, 안드로이드 시스템이 생성하여 관리하다가 소멸한다.</p>
<h4 id="▪︎-컴포넌트-앱-내에서-독립적인-실행-단위이다">▪︎ 컴포넌트 앱 내에서 독립적인 실행 단위이다.</h4>
<p>앱을 만들 때 컴포넌트도 다른 여러 클래스와 조합해야 한다.
그렇다면 객체지향 프로그램이라고 지칭하면 되는 것을 굳이 컴포넌트라고 하는 이유는 컴포넌트 클래스는 독립적인 수행 단위로 동작하기 때문이다. </p>
<p><img src="https://velog.velcdn.com/images/so__ok/post/de60760a-98f9-4da1-9678-ce1025b54f93/image.png" alt=""></p>
<p>왼쪽 그림처럼 MainActivity 클래스에서 DetailActivity 클래스를 실행하기 위해 객체를 생성해서 실행하려 하지만 되지 않는다. 실행하고자 하는 DetailActivity 클래스가 컴포넌트 클래스이기 때문에 직접 개발자 코드로 생성해서 실행할 수 없는 것이다.</p>
<p>오른쪽 그림처럼 MainAvtivity 컴포넌트가 DetailActivity 컴포넌트를 실행할 때 개발자 코드로 직접 결합하여 실행하지 않고, 인텐트(Intent)라는 매개로 하여 결합하지 않은 상태에서 독립적으로 실행하는 구조이다. </p>
<p>이처럼 안드로이드 앱이 컴포넌트 기반으로 설게되어 발생하는 부가적인 특징이 있다. </p>
<h4 id="▪︎-main-함수-같은-애플리케이션의-진입-지점이-따로-없다">▪︎ main 함수 같은 애플리케이션의 진입 지점이 따로 없다.</h4>
<p>앱의 프로세스가 구동되어 최초로 실행되는 개발자 코드 부분을 흔히 main 함수라고 부르는데, 안드로이드에는 이런 개념이 없다. 
안드로이드 컴포넌트가 앱 내에서 각각 독립적인 단위로 수행되기 때문에 모든 컴포넌트는 프로세스가 구동되었을 때 최초로 실행되는 수행 시점이 될 수 있다. </p>
<h4 id="▪︎-애플리케이션-라이브러리-개념이-있다">▪︎ 애플리케이션 라이브러리 개념이 있다.</h4>
<p>개발자가 따로 만들지 않았는데 어떤 앱이 기능처럼 느껴진다면, 그것은 라이브러리이다. 개발자 관점에서는 안드로이드 스마트폰의 다른 앱들을 라이브러리로 생각할 수 있다.</p>
<h4 id="▪︎-안드로이드-컴포넌트-종류">▪︎ 안드로이드 컴포넌트 종류</h4>
<p> ∙ 액티비티(Activity) 
 ∙ 서비스(Service) 
 ∙ 콘텐츠 프로바이더(ContentProvider)
 ∙ 브로드캐스트 리시버(BroadcastReceiver)</p>
<p>** 액티비티(Activit)**는 사용자 화면을 제공하는 컴포넌트이다. 안드로이드 앱은 클라이언트 측 애플리케이션이므로 화면 구성이 중요하다. 따라서 가장 많이 작성하는 컴포넌트이다. </p>
<p><strong>서비스(Service)</strong>는 화면과 전혀 상관없이 사용자 눈에는 보이지 않지만, 백드라운드에서 장시간 무언가를 수행할 수 있는 컴포넌트로 이해하면 된다. </p>
<p><strong>콘텐츠 프로바이더(ContentProvider)</strong>는 앱 간의 데이터 공유 목적으로 사용하는 컴포넌트이다. </p>
<p><strong>브로드캐스트 리시버(BroadcastReceiver)</strong>는 흔히 이벤트 모델로 수행되는 컴포넌트라고 이야기한다. 안드로이드 개발 시 자주 이용하지만, 인텐트 원리를 이해하지 못하면 이해가 쉽지 않다. 그냥 시스템에서 베터리가 부족하거나 시스템 부팅이 완료되는 등의 이벤트가 발생했을 때, 이 이벤트를 받기 위해 작성하는 컴포넌트 정도로 이해하면 된다.  </p>
<h3 id="4-리소스를-이용한-개발">4. 리소스를 이용한 개발</h3>
<p>안드로이드 앱 개발의 또 하나의 큰 특징 중 하나가 리소스 외부화를 극대화해서 개발한다는 것이다. 리소스 외부화란, 코드 영역에 누가 언제 실행하든 항상 같은 결과가 나오는 정적인(static) 콘텐츠를 코드에서 분리하여 개발하자는 개념이다. 모든 개발 분야에서 리소스 외부화는 프로그램의 유지보수의 도움이 된다는 사실에 이견이 없다. 정적인 문자열을 리소스 파일로 분리하면 코드 영역은 알고리즘 위주의 코드만 남게되고, 그만큼 코드가 짧아져서 프로그램 유지보수가 좋아진다는 개념이다. </p>
<p>✏️ 오늘 늦게라도 작성할 수 있어서 다행이다. 오늘 작성한 내용은 안드로이드의 기본적인 개념과 특징들을 정리해 보았다. 
내일부터는 간단한 UI와 이벤트를 실행해 보도록 할 것이다. 
처음에 공부했을 때 올렸으면 좋았을 거라는 생각이 들지만 앱을 만들 때 정리된 메모장이라고 생각하니 시간낭비라는 느낌은 받지 않는다. 
오늘은 두개를 올려 볼 생각이다. 이걸 다 하고나면 앞으로 만들어야할 알람과 예약 어플 기획을 해야 한다. 그건 깃허브에 올린 후 여기에 올릴 생각이다.. 오늘도 오공완</p>
<p>이미지
출처) <a href="https://developer.android.com/guide/platform?hl=ko">https://developer.android.com/guide/platform?hl=ko</a>
출처) <a href="https://velog.io/@alsgk721/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-6-Intent">https://velog.io/@alsgk721/안드로이드-6-Intent</a>
달빅 가상 머신
출처) <a href="https://bugday.tistory.com/45">https://bugday.tistory.com/45</a></p>
]]></description>
        </item>
    </channel>
</rss>