우리가 이제 알다시피 안드로이드 어플리케이션의 가장 기본적인 단위는 액티비티이다. 그리고 액티비티는 버튼, 라벨, 텍스트박스와 같은 안드로이드에 이미 내장되어 있는 위젯들을 활용한다.

정확히 말하면, 우리는 이 위젯들을 xml 파일에 정의하여 화면을 구성한다.

안드로이드의 위젯 활용법에 관한 공부를 시작하기 전, 우리는 스크린의 구성요소를 다시 한번 되짚고 가는 시간을 갖자.

 

1. Empty Project 화면 구성 (MainActivity.java 와 activity_main.xml 의 관계)

우리가 Empty project 를 만들면, 기본적으로 MainActivity.java 라는 클래스 파일과, activity_main 이라는 레이아웃 파일 기본을 만들어주고, 아무것도 작성하지 않은채 실행하면 아래와 같은 화면을 출력하는 어플리케이션이 된다.

Empty Project 생성 후 바로 실행 한 결과값

이 기본적으로 만들어주는 MainActivity.java 라는 클래스 파일은 크게 onCreate() 메서드와 setContentView() 메서드로 이루어져있는데, 그 기능은 아래와 같다.

1) onCreate() Method : Activity 창을 생성하는 작업을 한다

2) setContentView() Method : 연결된 UI (XML 파일)을 지정하는 기능을 한다. 따라서 어떤 자바 클래스 파일(기능을 담은 공간)이 어떤 XML 레이아웃 파일(화면을 담은 공간)과 연결되어 작용해야 하는지를 알려준다.

 

2. 뷰와 뷰그룹 (View and ViewGroups)

하나의 액티비티는 View와 ViewGroup을 포함하고 있다.

1) 뷰는 그냥 위젯이다.

2) 뷰는 화면에 보인다. (텍스트 박스, 버튼 등)

3) 하나 이상의 뷰는 뷰그룹으로 묶을수 있다.

4) 뷰 그룹도 뷰 이다. (뭔 개솔?) - 뷰그룹도 그냥 특별한 타입의 뷰일 뿐이다.

예를들어, 스크롤링 하는 드롭박스가 있다고 치면, 드롭박스 자체도 뷰다. 그리고 드롭박스 내의 선택할 수 있는 것들도 뷰다. 이런 경우, 우리는 뷰그룹이 뷰의 레이아웃을 정한다고 할 수 있다.

따라서 뷰 그룹도 뷰다. 다만 뷰의 정렬순서나 배열등을 지정하는 레이아웃을 정하는 작용을 한다고 볼 수 있다.

 

이렇게 뷰그룹이 뷰의 정렬을 위한 레이아웃으로 사용되는데,

그 예 중 하나가 FrameLayout 이다. 이 외에도 Linear Layout (Horizontal), LinearLayout(Vertical), RelativeLayout 등 다양한 레이아웃이 뷰그룹으로 존재한다.

 

다음시간부터는, 이 뷰그룹들에 대하여 어떠한 것들인지 간단하게 알아보고, 화면을 조금씩 꾸며보는 시간을 갖자!

Posted by 공급망관리 최선생
,

이게 무슨 뜻인고~?

Photo by  Markus Spiske  on  Unsplash

가끔 TV, 모바일, 컴퓨터 같은 다른 제품을 살 때 "Certified Refurbished" 제품이다. 라는걸 볼수가 있다.

이게 무슨 뜻인가? 

상품이 certified refurbished 라는 건, "본 상품은 원 소유자가 이전에 있었으며, 공정과정을 거쳐, 판매를 위해 다시 꾸며졌다" 라는 뜻이다.

그럼 어떤 상품이 certified refurbished 되는가?

고객이 상품을 구매한 뒤, 하자 문제나 단순변심등의 이유로 상품이 재반환 될 경우, 생산자는 다시 일련의 과정을 거쳐 해당 물품을 재판매 하기 위한 준비를 한다. 세척은 당연하거니와, 기능상의 문제 확인 및 테스트를 거쳐 다시 상품을 내 놓게 된다. 이렇게 재판매 준비가 된 제품들은 일반적인 경우에는 해당 회사의 공식 웹사이트에서 구매가 가능하다.

 

근데 왜 새것도 있는데 남이 쓰다만거를 다시 꾸민것을 사는건가?

1) 사실상 다시 공수를 투입하여 공장에서 판매 준비를 마친것이기 때문에, 제품이 특별하게 나쁠게 없다.

2) 싸다. 당연한 시장논리겠지만, 반품되었던 상품이고, 신규 제품으로 분류되지 않다보니 "싸다."

 

단점은?

1) 최근에 나온 모델은 거의 없다. 

- 한번 판매되었던 상품이 수거되어 재공정을 거친것이다 보니, 보통 출시된지 좀 지난 모델들이 주를 이루는 편이다.

2) AS 기간(Product warranty) 기간이 짧은게 보통이다.

 

 

Posted by 공급망관리 최선생
,

Pixabay - Ulrike Leone; 상호작용을 나타냄

자!!! 병아리들~~!!!!

챕터 3이 거의 다 끝나간다 조금만 더 힘을 내자.

우리는 다시 한번 지난 프로젝트를 사용할 것이다. 

[[Ch3-7] Fragment, 프래그먼트란 무엇인고~? 간단하게 만들어 보기 (Using Fragments in Android)

https://mckal-story.tistory.com/29  

챕터 3-7에서 만들었던 기본 프래그먼트가 있는 사람은 그걸 사용/수정 해도 좋고

혹시나 없는 사람은 아래의 소스코드가 그리 복잡하지 않으니, 그걸 그냥 따라해도 좋다.

 

우리는 늘 그렇듯 어떤 기능을 만들지 목표 부터 확인한다.

 

목표 기능

Fragment #2 의 버튼 클릭 시 프래그먼트 #1의 텍스트를 가져옴
결과값

 

소스코드를 확인해보자.

main.xml (res->layout)

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

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.fragment_Interaction.MainActivity">

    <fragment
        android:name="com.example.fragment_Interaction.Fragment1"
        android:id="@+id/fragment1"
        android:layout_weight="1"
        android:layout_width="fill_parent"
        android:layout_height="match_parent" />

    <fragment
        android:name="com.example.fragment_Interaction.Fragment2"
        android:id="@+id/fragment2"
        android:layout_weight="1"
        android:layout_width="fill_parent"
        android:layout_height="match_parent"
        />
</LinearLayout>

fragment1.xml (res->layout) ; ※ Empty Project 로 생성하여 이 파일이 없다면, "생성"

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#00FF00"
    >

    <TextView
        android:id="@+id/lblFragment1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="이건 프래그먼트 1 이다."
        android:textColor="#000000"
        android:textSize="25sp" />
</LinearLayout>

fragment2.xml (res->layout) ; ※ Empty Project 로 생성하여 이 파일이 없다면, "생성"

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#FFFE00"
    >
<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="이건 프래그먼트 #2임 ㅋ"
    android:textColor="#000000"
    android:textSize="25sp"/>

<Button
    android:id="@+id/btnGetText"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Fragment #1 텍스트 가져오기"
    android:textColor="#000000"
    android:onClick="onClick"
    />
</LinearLayout>

AndroidManifest.xml (manifests)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.fragment_Interaction">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

dimens.xml (res->values) ; ※ 기존에 생성된것이 없다면 생성

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="activity_vertical_margin">4dp</dimen>
    <dimen name="activity_horizontal_margin">4dp</dimen>


</resources>

MainActivity.java (app->java)

package com.example.fragment_Interaction;


import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

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

Fragment1.java (app->java) ; ※ Empty Project 로 생성하여 이 파일이 없다면, "생성"

package com.example.fragment_Interaction;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Fragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {

        // -- Inflate the layout for this fragment --
        return inflater.inflate(
                R.layout.fragment1, container, false);
    }
}

Fragment2.java (app->java) ; ※ Empty Project 로 생성하여 이 파일이 없다면, "생성"

package com.example.fragment_Interaction;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;


public class Fragment2 extends Fragment {
    @Override
    public View onCreateView (LayoutInflater inflater,
                              ViewGroup container, Bundle savedInstanceState) {
        // -- Inflate the layout for this fragment --
        return inflater.inflate(
                R.layout.fragment2, container, false);
    }


    @Override
    public void onStart(){
        super.onStart();

        //-- Button View ---
        Button btnGetText = (Button)
                getActivity().findViewById(R.id.btnGetText);
        btnGetText.setOnClickListener(new View.OnClickListener() {

            public void onClick(View v) {
                TextView lbl = (TextView)
                        getActivity().findViewById(R.id.lblFragment1);
                Toast.makeText(getActivity(), lbl.getText(), Toast.LENGTH_LONG).show();

            }
        });
    }
}

 

위와같이 코드를 작성하면, 아까 보았던 기능을 구현할 수 있다.

눈치빠른 삐약이들은 알아챘겠지만, 대부분의 구조와 기능은 지난 챕터에서 다 했던것들이고

사실 중요한건 fragment2.xml 과 fragment2.java 소스코드이다.

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

getActivity() 메서드를 이용하여, 현재 프래그먼트가 임베디드(embeded; 속해있다는 것 정도로 해석하면 되겠다)되어있는 액티비티를 찾는다. 그 다음 findViewById() 메서드를 사용하여, 액티비티 내의 (정확히 말하면 "액티비티" 안에 "프래그먼트" 안에있는 "뷰를") 찾아낸다.

그 다음 토스트 메서드를 사용하여 글자를 화면 앞으로 띄워준다.

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

 

※ 현재는 프래그먼트가 어떻게 서로 상호작용 하는지를 알아보는데에 집중하자. 너무 세세한 것에 목메다가 개발 공부를 포기한 적이 한두번이 아니다.

 

Posted by 공급망관리 최선생
,

프래그먼트의 인생(생명주기가 옳은 말이다)에 관하여 알아보는 숙연한 시간을 갖자.... (내 인생도 돌이켜 볼겸...)

아니 근데 글쓴이 이 삼시세끼는 지난번에 액티비티의 생명주기 가르쳐줄 때에도

"나도 이게 왜 이게 중요한지를 몰라" 라고 하면서 그냥 공부하라고 난리피우네 라고 생각한다면, 내 글을 자세히 읽어준것에 대한 감사가 욕보다 먼저 나갈 것이다.

감사하다. 하지만 이제 대강 이유를 알겠다!!

그간에 다른 개발자 카카오방에서 엿들어본 결과, 액티비티와 프래그먼트의 생명주기가 중요한 이유는 하나의 어플리케이션은 보통 다수의 액티비티, 프래그먼트, 그 이외의 많은 기능들로 이루어져 있는데, 자신이 원하는 기능의 순서를 정밀하게 정의하기 위해 중요하다!

 

자! 액티비티의 생명주기와 마찬가지로, 프래그먼트의 생명 주기도 Debugging 을 하여 로그를 보면서 추적한다.

지난번에 생성했던 [Ch3-8] 에서 생성했던 프로젝트를 고대로 사용한다.

https://mckal-story.tistory.com/30 (참조)

 

[Ch3-8] Fragment 두번째! 가로형, 세로형 화면에 따라 동적으로 반응하는 프래그먼트 만들기 (Using dynamic Fragments depending on landscape/portrait mode)

지난번에 이어서 두 번째 프래그먼트 시간이다. 지난번 아주 유익한 시간을 갖었으니, 혹여나 첫 번째를 못 본 병아리는 지난번(Ch3-7) 꺼를 우선 보기 바란다. 지난번에는 activity_main.xml 에 프래그먼트 두개..

mckal-story.tistory.com

 

 

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

1. 우선 디버깅 할때 로그를 찍어내기 위해, Fragment1.java 파일에 다음과 같은 코드를 추가한다.

(길기만 하지 사실 별거 없으니, 그냥 복붙해서 사용해도 괜찮다.)

package com.example.fragment_dynamic;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.util.Log;

public class Fragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {

        Log.d("Fragment1", "onCreaeView");

        // -- Inflate the layout for this fragment --
        return inflater.inflate(
                R.layout.fragment1, container, false);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        Log.d("Fragment", "onAttach");
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("Fragment1","onCreate");
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Log.d("Fragment1","onActivityCreated");
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.d("Fragment1","onStart");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.d("Fragment1","onResume");
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.d("Fragment1", "onPause");
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.d("Fragment1", "onStop");
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.d("Fragment1", "onDestroyView");
    }

    @Override
    public void onDetach() {
        super.onDetach();
        Log.d("Fragment1", "onDetach");
    }
}

 

2. 해당 프로젝트를 디버깅 모드로 실행한다. (또는 Shift + F9)

2.1 그리고 화면을 가로모드(landscape)모드로 전환한다.

안드로이드 스튜디오 화면 전환 버튼

2.1 (continued) 또는 나같은 단축키 충들은 Ctrl + 키보드 화살표 우측 or 좌측

 

그리고 디버깅창에 찍히는 로그를 보자.

화면 가로모드로 전환시

프래그먼트 실행 시 생명주기

여기서, 좀 더 세분화 하여 본다면

1) onAttach 2) onCreate 3) onCreatedView 4) onActivityCreated 까지는 프래그먼트가 생성될때 거치는 생명주기이다.

1) onStart 2) onResume 은 프래그먼트가 디바이스 화면으로 보일때 거치는 생명주기이다.

 

홈버튼 클릭시

홈버튼 클릭 시 생명주기

1) onPause 2) onStop 발생

 

앱을 백그라운드에서 다시 불러왔을 때

 

포그라운드로 활성화 시킬때 생명주기

아까와 마찬가지로 화면이 가시적(visible)으로 변하면, onStart 와 onResume 의 생명주기가 발생한다.

 

백버튼으로 app을 종료(kill)했을 때

어플리케이션을 종료하였을 때 생명주기

 

 

아까 했던 이야기를 다시 해보자

<!--"그간에 다른 개발자 카카오방에서 엿들어본 결과, 액티비티와 프래그먼트의 생명주기가 중요한 이유는 하나의 어플리케이션은 보통 다수의 액티비티, 프래그먼트, 그 이외의 많은 기능들로 이루어져 있는데, 자신이 원하는 기능의 순서를 정밀하게 정의하기 위해 중요하다!" -->

라고 주장하였는데 예를 하나 들어보자.

프래그먼트가 종료됨과 동시에 그 다음에 자동으로 실행하고 싶은 작업이 있다라고 하면, 개발자는 그 작업을 onStop, onDestroyView, onDetach 어디에 둘지 그 기능에 따라 고심하여 선택할 수 있을것이다.

 

액티비티와의 가장 큰 차이!

1. 프래그먼트는 항상 액티비티안에 있어야 한다. 프래그먼트를 포함하고 있는 액티비티를 호스트 액티비티라고 한다. (주인님 ㅠㅠ)

2. 액티비티 내에서 어떠한 작업을 하다가 back 버튼을 누르면, 사용자가 했던 일련의 작업(transaction)을 자동으로 저장한다. 이를 back stack 에 자동으로 저장한다고 한다. 근데 우리 말썽쟁이 프래그먼트는 그딴거 없다. back stack 에 자동으로 transaction이 쌓이기를 원하면, "addToBackStack() 메서드를 사용하여 지정하여야 한다.

 

 

Posted by 공급망관리 최선생
,

안드로이드 스튜디오에서 가상머신을 돌리다 보면,

 

Android Emulator 에서 아무것도 실행되지 않으며 그냥 검은색 화면만 나올때가 있다.

정말 개짱난다.

 

이때 Debug 모드로 로그를 찍어보면 "Launching Apps" 까지만 나오고 그 뒤의 로그는 찍히질 않는다.

 

그리고 화면 아래애 Waiting for the target device to come online 이라는 문구가 나온다.

 

뭔가 문제인지는 나도 잘 모르겠다. 그냥 에뮬레이터 에러같은데...

 

어쨌거나 해결방법은

AVD Manager 클릭

 

우측 상단에 있는 AVD Manager 클릭 ->

현재 사용하고 있는 emulator 삭제

 

그 다음 동일한 스펙으로 재설치하고 다시 실행해보자.

아주 very 스무스 하게 잘 실행 될 것이다.

끝.

Posted by 공급망관리 최선생
,