출처 : Bill  Phillips, Android Programming: The Big Nerd Ranch Guide (Big Nerd Ranch Guides) 3st Edition


■ The Need for UI Flexibility

- 리스트를 관리하는 부분 하나, 항목을 관리하는 부분 하나

- 항목을 클릭하는 것은 항목에 관련된 액티비티를 실행

- UI Flexibility: 액티비티의 뷰를 런타임 시점에서 디바이스에 관계없이 재구성하는것

 

■ Introducing Fragments

- Fragment :  Controller Object. 액티비티가 UI 관리를 수행하도록 시킬수 있는 컨트롤러.

- UI Fragment : 각자 자신의 UI를 관리하는 Fragment. Fragment의 View는 UI elements포함 

- Activity의 View에는 다수 Fragment의 View가 삽입될 지점이 있다. 

- Activity는 Fragment로 분할된다.

- 액티비티의 뷰는  Fragment의 View인 FrameLayout으로 정의된다.

- 프래그먼트의 뷰는 Widget들로 구성된다.


■ Two types of fragments 

- Fragment는 어떤 fragment를 implements하느냐에 따라 종류가 나뉜다.

- Native  Fragment : 기기에서 지원하는 Fragment

- Support Fragment : 앱의 라이브러리에서 지원하는 Fragment

- 보통  Support Fragment를 많이 쓴다. 


■ Adding dependencies in Android Studio

- AppCompat 라이브러리를 app/build.gradle에 추가

- build.gradle은 app전체에서 하나, app moudle에 하나 있다.

- dependency추가를 위해서는 app module에서 추가

1
2
3
4
5
6
7
8
9
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0-beta01'
    implementation 'com.android.support.constraint:constraint-layout:1.1.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'com.android.support:recyclerview-v7:28.0.0-beta01'
}
cs

- Dependency 추가한 다음에 Tools-> Android -> Sync Project with gradles files


■ Creating the Crime class

- UUID : 안드로이드 프레임워크에서 사용하는 자바 유틸리티. universally unique ID값 생성.

1
2
3
4
5
6
7
8
9
10
    public class Crime {
        private UUID mId;
        private String mTitle;
        private Date mDate;
        private boolean mSolved;
        public Crime() {
            mId = UUID.randomUUID();
            mDate = new Date();
        }
    }
cs


■ The fragment lifecycle

- 프래그먼트는 액티비티를 대신해 작업하기 때문에 액티비티에 상응하는 프래그먼트의 라이프사이클이 필요하다.

- 프래그먼트 라이프사이클 메소드는 부모 액티비티에 의해 호출된다. 

- 액티비티가 OS에 의해 호출되는것과는 다른 면이다.

- OS는 프래그먼트에 대해 아무런 정보도 알 수 없다. 


■ Two approaches to hosting

- 액티비티에서 UI 프래그먼트를 호스팅하는 방법은 두가지가 있다.

1. 액티비티의 레이아웃에 프래그먼트 추가

2. 액티비티의 코드에서 프래그먼트 추가

- 유연성을 위해서는 코드에서 프래그먼트 추가하는 것이 좋다.


■ Defining a container view 

-  프래그먼트를 사용하기 위한 액티비티의 레이아웃을 FrameLayout가 좋다.

- 프리뷰가 잘 안보이면 Build->Rebuild하거나 앱 껏다켜기


■ Defining CrimeFragment’s layout

- 프래그먼트는 LinearLayout으로 설정


■ Creating the CrimeFragment class

- Fragment는 extends Fragment를 통해 만들어진다.

- android.support.v4.app을 상속한다.


■ Implementing fragment lifecycle methods

- 뷰와 모델을 통해 개별 항목을 표시하고, 유저의 변경사항을 반영하는 역할을 한다.

- Fragment의 onCreate(Bundle)은 public인 반면 Activity의 onCreate(Bundle)은 protected이다.  그 이유는 어떠한 액티비티에서라도 프래그먼트가 호출될 수 있어야하기 때문이다.

- .onSaveInstanceState(Bundle)을 통해 상태를 저장하고 불러올 수 있다.

- onCreate(Bundle)에서는 프래그먼트 인스턴스를 설정할 수 있지만 프래그먼트의 레이아웃은 onCreateVIew에서 해야한다.

1
2
3
4
5
6
7
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_crime, container, false);
        return v;
    }
 
cs

- inflater를 통해 뷰를 불러오고, container는 프래그먼트 뷰의 부모를 의미한다, Bundle은 프래그먼트의 뷰를 부모의 뷰에 inflate할지 여부를 결정한다.

  만약 코드로 자식의 뷰를 부모의 코드에 넣을 것이라면 false를 값으로 둔다.


■ Wiring widgets in a fragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class CrimeFragment extends Fragment {
    private Crime mCrime;
    private EditText mTitleField;
 ...
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_crime, container, false);
        mTitleField = (EditText) v.findViewById(R.id.crime_title);
        mTitleField.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(
                    CharSequence s, int start, int count, int after) {
// This space intentionally left blank
            }
            @Override
            public void onTextChanged(
                    CharSequence s, int start, int before, int count) {
                mCrime.setTitle(s.toString());
            }
            @Override
            public void afterTextChanged(Editable s) {
// This one too
            }
        });
        return v;
    }
}
 
cs

- findViewById(int)를 통해 뷰를 찾아온다.


■  Adding a UI Fragment to the FragmentManager 

- 프래그먼트를 관리하고 프래그먼트의 레이아웃을 액티비티의 뷰에 넣는 역할을 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class CrimeActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_crime);
        FragmentManager fm = getSupportFragmentManager();
        Fragment fragment = fm.findFragmentById(R.id.fragment_container);
        if (fragment == null) {
            fragment = new CrimeFragment();
            fm.beginTransaction()
                    .add(R.id.fragment_container, fragment)
                    .commit();
        }
    }
}
 
cs

- 액티비티의 프래그먼트 메니저에서 트랜잭션을 생성하고
- 액티비티의 컨테이너(프래그먼트 리스트) ID와, 새로 생성된 프래그먼트를 매개변수로하여  추가한다는 의미
- 액티비티가 종료될때 프래그먼트 매니저가 리스트를 저장했다가 다시 액티비티가 생성되면 프래그먼트를 프래그먼트 매니저에서 불러온다.
- 만약 해당하는 프래그먼트가 생성된적이 없으면 재생성한다.


■  Application Architecture with Fragments
- 프래그먼트를 남용하는것이 좋지않다. 한 페이지에 프래그먼트가 너무 많으면 어떤 동작을 어떤 프래그먼트가 처리하는지 헷갈리게 된다.
- 따라서 커스텀 뷰를 사용하여 한 페이지에 프래그먼트가 3개 이상 되지 않도록 하는 것이 좋다.
3개 이상이 될 일이 있으면 커스텀 뷰를 사용해 다음 단계로 계층화한다.

'Web Programming > Android' 카테고리의 다른 글

5. Your Second Activity  (0) 2018.08.16
3. The Activity Lifecycle  (0) 2018.08.14
2. Android and Model-ViewController  (0) 2018.08.14
1. Your First Android Application  (0) 2018.08.14

출처 : Bill  Phillips, Android Programming: The Big Nerd Ranch Guide (Big Nerd Ranch Guides) 3st Edition

■ Creating a new activity

- 액티비티를 새로만들때는 Activity, layout, manifest 3개를 건드려야한다.


■ manifest 에서 activities 선언

- 매니페스트는 메타데이터를 안드로이드 OS에 전달하는 파일이다.

1
2
<activity android:name=".CheatActivity">
</activity>
cs

- 위와 같은 형태로 액티비티를 메니페스트 파일에 선언한다.


- Wiring은 다음과 같이 한다 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class QuizActivity extends AppCompatActivity {
 ...
 private Button mNextButton;
 private Button mCheatButton;
 private TextView mQuestionTextView;
 ...
 @Override
 protected void onCreate(Bundle savedInstanceState) {
...
mNextButton = (Button) findViewById(R.id.next_button);
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
updateQuestion();
}
});
mCheatButton = (Button)findViewById(R.id.cheat_button);
mCheatButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(QuizActivity.this, CheatActivity.class); startActivity(intent);
}
});
updateQuestion();
 }
...
}
cs

■ Activity 시작

- Intent를 통해 한 Activity가 다른 Activity를 실행한다.

- startActivity(Intent)는 OS의 ActivityManager로 보내지고, ActivityManager가 Activity 인스턴스를 만들고 새로 만들어진 인스턴스의 onCreate(Bundle) 메소드를 실행한다. 이때 Intent에 담긴 정보를 바탕으로 ActiivtyManager가 어떤 Activiy를 실행할지 알게된다. 


■ Intent로 정보 주고받기 : ActivityManager에게 무엇을 해야할지 알려주는 용도.

- Intent : Component(Activity) 가 OS와 정보를 주고받기위해 사용하는 객체

- Intent는 전달을 하는 이동 수단이고, Bundle은 저장을 하는 저장 공간이다.

  전달하고자 하는 값을 Bundle에 담아 Intent에 저장하여 전송하는 편이 더 좋다.

1
public Intent(Context packageContext, Class<?> cls)
cs

- Context는 ActivityManager에게  어떤 어플리캐이션 패키지  에서 실행 주체 Activity 클래스를 찾을수 있는지 알려준다.

# Context기본 역할

1) 어플리케이션에 관하여 시스템이 관리하고 있는 정보에 접근하기

  -> getPackageName(), getResource()등의 함수 호출

2) 안드로이드 시스템 서비스에서 제공하는 API 를 호출 할 수 있는 기능 

  -> startActivity(), bindService()등의 함수 호출  

- 매개변수 클래스 부분은 실행 대상 액티비티를 의미한다. 

-  액티비티를 시작하기 전에 ActivityManager는 AndroidMaifest.xml파일을 확인하여 실행하고자하는 액티비티와 같은 이름의 클래스파일이 선언되어있는지 확인한다. 


■ Intent extra로 정보 주고받기 : 다른 엑티비티로 정보 전달(단방향)

-  Intent extra : 실행주체 액티비티에서 Intent에 포함하는 임의의 정보.  인텐트에서의 생성자 매개변수와 같은 의미.

Key,Value 쌍으로 구성되어있다.

1
public Intent putExtra(String name, boolean value)
cs

- OS는 인텐트를 실행대상 액티비티에 전송하고 , EXTRA에 접근해서 데이터를 수신한다.

- EXTRA key 와 Value 추가하기. 그리고 EXTRA_ANSWER_IS_TRUE란 이름으로 answerIsTrue값을 저장. 

1
2
3
4
5
6
7
8
9
10
public class CheatActivity extends AppCompatActivity {
 private static final String EXTRA_ANSWER_IS_TRUE =
"com.bignerdranch.android.geoquiz.answer_is_true";
 public static Intent newIntent(Context packageContext, boolean answerIsTrue) {
Intent intent = new Intent(packageContext, CheatActivity.class);
intent.putExtra(EXTRA_ANSWER_IS_TRUE, answerIsTrue);
return intent;
 }
 ...
 

cs

- 실행주체 액티비티에서 정보를 주입하고 인텐트실행하여 실행 대상 액티비티 실행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class QuizActivity extends AppCompatActivity {
 ...
 private Button mNextButton;
 private Button mCheatButton;
 private TextView mQuestionTextView;
 ...
 @Override
 protected void onCreate(Bundle savedInstanceState) {
...
mNextButton = (Button) findViewById(R.id.next_button);
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
updateQuestion();
}
});
mCheatButton = (Button)findViewById(R.id.cheat_button);
mCheatButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(QuizActivity.this, CheatActivity.class);
boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue(); Intent intent = CheatActivity.newIntent(QuizActivity.this, answerIsTrue); startActivity(intent);
}
});
updateQuestion();
 }
...
}
cs

- 실행주체 액티비티에서 보낸 정보를 실행대상 액티비티에서 EXTRA에 담긴 정보를 불러온다.

1
2
public boolean getBooleanExtra(String name, boolean defaultValue)
 
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class CheatActivity extends AppCompatActivity {
 private static final String EXTRA_ANSWER_IS_TRUE =
 "com.bignerdranch.android.geoquiz.answer_is_true";
 private boolean mAnswerIsTrue;
private TextView mAnswerTextView;
 private Button mShowAnswerButton;
 //보내는부분
 public static Intent newIntent(Context packageContext, boolean answerIsTrue) {
Intent intent = new Intent(packageContext, CheatActivity.class);
intent.putExtra(EXTRA_ANSWER_IS_TRUE, answerIsTrue);
return intent;
}
//받는부분
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_cheat);
 mAnswerIsTrue = getIntent().getBooleanExtra(EXTRA_ANSWER_IS_TRUE, false);
mAnswerTextView = (TextView) findViewById(R.id.answer_text_view);
 mShowAnswerButton = (Button) findViewById(R.id.show_answer_button);
 mShowAnswerButton.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 if (mAnswerIsTrue) {
 mAnswerTextView.setText(R.string.true_button);
 } else {
 mAnswerTextView.setText(R.string.false_button);
 }
 }
 });
 }
 ...
}
 

cs

- getIntent()는 실행대상 activity를 start시킨 인텐트를 불러온다.



자식 엑티비티로 부터 실행결과 전달받기(양방향)

1
2
public void startActivityForResult(Intent intent, int requestCode)
 
cs

1. 부모가 자식에게 값을 보낼때 사용

request code : 유저가 정의한 값. Child activiy에 보내지고 부모로부터 결과를 전송받는 값. 어떤 자식이 부모로 전송했는지 알 수 있음

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class QuizActivity extends AppCompatActivity {
 private static final String TAG = "QuizActivity";
 private static final String KEY_INDEX = "index";
 private static final int REQUEST_CODE_CHEAT = 0;
 ...
 @Override
 protected void onCreate(Bundle savedInstanceState) {
...
mCheatButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Start CheatActivity
boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue();
Intent intent = CheatActivity.newIntent(QuizActivity.this,
answerIsTrue);
startActivity(intent);
startActivityForResult(intent, REQUEST_CODE_CHEAT);
}
});
cs

2. 자식이 부모로 값을 보냄

1
2
 public final void setResult(int resultCode)
 public final void setResult(int resultCode, Intent data)
cs

result code : Activity.RESULT_OK or Activity.RESULT_CANCELED. result코드에 따라 부모 액티비티에서 할 행동을 정함

setResult intent : 자식에 부모에게 보내는 값

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class CheatActivity extends AppCompatActivity {
 private static final String EXTRA_ANSWER_IS_TRUE =
 "com.bignerdranch.android.geoquiz.answer_is_true";
 private static final String EXTRA_ANSWER_SHOWN =
 "com.bignerdranch.android.geoquiz.answer_shown";
 ...
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 ...
 mShowAnswerButton.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
if (mAnswerIsTrue) {
 mAnswerTextView.setText(R.string.true_button);
 } else {
 mAnswerTextView.setText(R.string.false_button);
 }
setAnswerShownResult(true);
 }
 });
 }
 private void setAnswerShownResult(boolean isAnswerShown) {
 Intent data = new Intent();
 data.putExtra(EXTRA_ANSWER_SHOWN, isAnswerShown);
 setResult(RESULT_OK, data);
 }
}
 
cs

- 부모에서 EXTRA 리턴할수 있는 함수 작성

1
2
3
4
5
6
7
8
9
public static Intent newIntent(Context packageContext, boolean answerIsTrue) {
 Intent intent = new Intent(packageContext, CheatActivity.class);
 intent.putExtra(EXTRA_ANSWER_IS_TRUE, answerIsTrue);
 return intent;
}
public static boolean wasAnswerShown(Intent result) {
 return result.getBooleanExtra(EXTRA_ANSWER_SHOWN, false);
}
 
cs

3. 자식이 부모에게 보낸값 부모에서 처리하기

- 결과값 받는 onActivityResult

1
2
3
4
5
6
7
8
9
10
11
12
   @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode != Activity.RESULT_OK) {
            return;
        }
        if (requestCode == REQUEST_CODE_CHEAT) {
            if (data == null) {
                return;
            }
            mIsCheater = CheatActivity.wasAnswerShown(data);
        }
    }
cs

 



'Web Programming > Android' 카테고리의 다른 글

7. UI Fragments and the Fragment Manager  (0) 2018.08.20
3. The Activity Lifecycle  (0) 2018.08.14
2. Android and Model-ViewController  (0) 2018.08.14
1. Your First Android Application  (0) 2018.08.14

출처 : Bill  Phillips, Android Programming: The Big Nerd Ranch Guide (Big Nerd Ranch Guides) 3st Edition

■ 로그

1
2
public static int d(String tag, String msg)
 
cs
1
2
3
4
5
6
7
 @Override
 protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate(Bundle) called");
setContentView(R.layout.activity_quiz);
 }
cs


■ 로테이션과 액티비티 라이프사이클

- 디바이스가 로테이션될때, 액티비티의 인스턴스는 destroyed되고 새로운 인스턴스가 생성된다. 

- 디바이스 로테이션은 디바이스 설정을 바꾼다. 디바이스 설정은 개별 기기의 현재상태를 결정하는 특성이다. 스크린방향 등

- 키보드 사용가능, 언어, 멂티윈도우 모드, 스크린 방향등은 런타임시 바꿀수 있따.

- 안드로이드는 디바이스 로테이션시 기존의 인스턴스를 destroy하고 새로운 디바이스 설정에 맞는 리소스(레이아웃)을 찾아 인스턴스를 실행한다..

- ex) landscape layout을 res폴더 우클릭 -> UI모드 -> landscape선택을 통해 만들면 디바이스 로테이션시 landscape 사용

- FrameLayout : 위젯들이 android:layout_gravity 속성을 통해 (ex>center_horizental, bottom_right) 정렬된다. 주로 여러 Fragment를 동일한 위치내에서 교차하여 표시하고자 할때사용.

- LinearLayout: 여러 View 위젯들을  한줄의 가로 또는 세로 방향으로 나열할 때 사용하는 Layout 클래스. 위젯을 비율,dp등으로 크기지정



■ 로테이션간 데이터 전달

  • 로테이션 등을 디바이스 설정이 바뀔때, 이전 디바이스 설정에서 이루어진 액티비티의 값을 새롭게 생성되는 액티비티에 전달하는방법
  • 그거슨 바로 onSaveInstanceState(Bundle outState)이다.
  • onSaveInstanceState(Bundle outState)은 onStop()이 실행되기 전에 호출된다. 
  • 단, 유저가 백버튼 눌렀을때는 onSaveInstanceState(Bundle outState)이 실행되지않는다. 왜냐면 백버튼 눌렀을 경우 인스턴스가 아예메모리에서 삭제되기 떄문. onStop은 아직 메모리에 남아있는상태를 의미한다.
  • Bundle : Key,Value 쌍으로 이루어진 데이터 구조이다.
  • onSaveInstanceState(Bundle outState)은 번들에 데이터를 저장하고, onCreate(Bundle)에서 다시 읽는 방식으로 사용한다.

       1. 키값추가

1
2
3
4
5
public class QuizActivity extends AppCompatActivity {
 private static final String TAG = "QuizActivity";
 private static final String KEY_INDEX = "index";
 private Button mTrueButton;
 
cs

  2. putInt로 번들에 집어넣기

1
2
3
4
5
6
7
8
 @Override
 public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
Log.i(TAG, "onSaveInstanceState");
savedInstanceState.putInt(KEY_INDEX, mCurrentIndex);
 }
 
 
cs

3. Bundle에 저장한 값을 onCreate에서 받아오기

1
2
3
4
5
6
7
8
9
10
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 Log.d(TAG, "onCreate(Bundle) called");
 setContentView(R.layout.activity_quiz);
 if (savedInstanceState != null) {
 mCurrentIndex = savedInstanceState.getInt(KEY_INDEX, 0);
 } 
 }
 
cs


■ 액티비티 라이프사이클


-Stashed : 액티비티의 객체는 남아있지 않지만 OS가 다시 되살릴수 있는 상태

- onStop: 유저가 수정한 값 등의 영구저장 데이터를 저장할때 사용

- onSaveInstatnceState(Bundle) : 임시값을 저장할때 사용


'Web Programming > Android' 카테고리의 다른 글

7. UI Fragments and the Fragment Manager  (0) 2018.08.20
5. Your Second Activity  (0) 2018.08.16
2. Android and Model-ViewController  (0) 2018.08.14
1. Your First Android Application  (0) 2018.08.14

1
private int mTextResId;
cs

리소스 아이디는 int로 구분되기 때문에 리소스 멤버변수는 int형이어야한다.


■ Activity가 Controller의 역할을 한다.

- Activity에서 layout을 멤버변수로 사용하여 interaction을 제어한다.

■ MVC 모델의 장점 -> 재사용성


'Web Programming > Android' 카테고리의 다른 글

7. UI Fragments and the Fragment Manager  (0) 2018.08.20
5. Your Second Activity  (0) 2018.08.16
3. The Activity Lifecycle  (0) 2018.08.14
1. Your First Android Application  (0) 2018.08.14

+ Recent posts