Thread with Handler & AsyncTask



reference: 

https://developer.android.com/reference/android/os/Handler?hl=ko

https://developer.android.com/reference/android/os/Looper?hl=ko

https://academy.realm.io/kr/posts/android-thread-looper-handler/

https://developer.android.com/reference/android/os/AsyncTask?hl=ko

https://stackoverflow.com/questions/12797550/android-asynctask-for-long-running-operations?noredirect=1&lq=1





Android 앱에서의 GUI 작업은 메인스레드(UI스레드)에서만 가능하도록 제한되어있습니다. 그 이유는 메인스레드에서는 GUI에 관련한 작업을 하도록 강제하여 오래 걸리는 작업은 다른 스레드를 이용하도록 하기 위함입니다. 만약 다른 스레드에서도 허용한다면 같은 View 요소에 대한 동기화 이슈가 생길 것입니다. 이를 해결하기 위해 Android에서는 스레드에 Handler & Looper 클래스를 도입하였습니다.


 Looper & Handler

Android의 메인스레드는 내부적으로 message queue와 Looper를 가집니다. Message queue의 역할은 이름 그대로 message를 전달받아 선입선출 방식으로 하나씩 해결하기 위한 자료구조입니다. Message는 다른 스레드나 현재 스레드로부터 받을 수 있습니다. Looper 클래스는 그 message queue로부터 message를 하나씩 꺼내어 Handler에게 처리하도록 전달하는 역할을 합니다. 그럼 Handler는 전달 받은 message를 처리하고, 반대로 message를 받아 message queue에 집어 넣는 역할도 합니다.


Handler

모든 Handler 객체는 하나의 스레드와 그 스레드의 message queue에 연결됩니다. Handler 객체를 하나 생성하면, 생성하고 있는 스레드에 바인드됩니다. 그 순간부터 Handler는 message나 Runnable 객체를 받아 처리하기 시작합니다.

Handler는 2가지 목적으로 사용됩니다.

1. 미래 언젠가에 처리될 message와 runnable을 스케쥴

2. 자기 자신이 아닌 다른 스레드에게 처리하도록 전달

1번의 경우 post...() 및 sendMessage...() 메소드로 이루어집니다. post가 붙은 메소드들은 runnable 객체를 message queue에 전달하고, send message류의 메소드들은 handleMessage() 메소드에 의해 처리될 message들을 전달합니다.

Handler에 post나 send를 할 땐, 준비가 됐을 때 바로 처리하도록 하게 하거나, 지연되어 처리하도록 할 수 있습니다. 지연된 처리의 경우 timeout, tick과 같은 timing 관련 처리도 지원합니다.


Looper

스레드의 message 관련 loop을 돌게 해주는 클래스입니다. 기본적인 스레드는 message loop 관련 처리를 하지 않습니다. 따라서 스레드 안에서 message loop을 돌게 하기 위해서는, 스레드 안에서 Looper의 prepare() 정적 메소드를 호출한 뒤 loop() 정적 메소드를 호출해야 합니다. 메인스레드는 기본적으로 Looper를 갖지만, 사용자가 정의한 스레드에서는 갖지 않기 때문에 message 처리를 하려고 한다면 필수적으로 이 과정이 수행되어야 합니다. Looper는 무한히 loop을 돌며 message queue의 message나 runnable을 차례로 꺼내 처리할 Handler에게 전달합니다.


다음은 전형적인 Looper와 Handler를 갖는 스레드의 예시입니다.

mHandler에 원하는 message나 runnable을 전달할 수 있습니다.

class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }

이런 이유로 편의를 위해 만들어진 클래스가 HandlerThread 클래스입니다. Java의 기본 스레드는 Android의 Looper를 갖지 않기 때문에 이것을 미리 가지도록 처리한 것이 HandlerThread입니다.

아래 그림은 위의 설명을 이해하는데 도움이 됩니다.

android looper message에 대한 이미지 검색결과


 AsyncTask

AsyncTask 클래스는 메인스레드를 적절하고 쉽게 사용할수 있도록 만들어진 클래스입니다. 백그라운드 작업을 가능하게 해주고, Handler나 Looper가 없이도 그 결과에 대한 UI 작업을 메인스레드에게 수행하도록 하게합니다.

AsyncTask는 최대 수초 정도의 비교적 작은 작업을 하는 것이 이상적입니다. 만약 긴 작업을 해야한다면, Executor, ThreadPoolExecutor나 FutureTask 등의 java.util.concurrent 패키지를 사용하는 것이 권장됩니다.

여기에서 비동기 작업이란, 백그라운드 스레드에서 작업을 한 뒤 그 결과를 메인스레드에 전달하는 것을 의미합니다. AsyncTask는 Params, Progress, Result의 세가지 generic type을 갖습니다. 또한 4개onPreExecute() -> doInBackground -> onProgressUpdate -> onPostExecute 의 순서로 전개됩니다. 아래는 전형적인 AsyncTask의 사용 예입니다.

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
     
protected Long doInBackground(URL... urls) {
         
int count = urls.length;
         
long totalSize = 0;
         
for (int i = 0; i < count; i++) {
             totalSize
+= Downloader.downloadFile(urls[i]);
             publishProgress
((int) ((i / (float) count) * 100));
             
// Escape early if cancel() is called
             
if (isCancelled()) break;
         
}
         
return totalSize;
     
}

     
protected void onProgressUpdate(Integer... progress) {
         setProgressPercent
(progress[0]);
     
}

     
protected void onPostExecute(Long result) {
         showDialog
("Downloaded " + result + " bytes");
     
}
 
}
 

URL을 Param으로 가지고, 정수를 Progress로, 그 결과 값을 Long으로 택했습니다. doInBackground() 메소드에서 URL로부터 파일을 다운 받으면서, 중간에 publicProgress()를 호출해서 그 progress를 UI에 업데이트 하도록 하고있습니다. onProgressUpdate()에서 그 것을 수행합니다. 또한 작업이 완료 된 후 onPostExecute()가 호출되어 다운로드한 최종 사이즈를 표시합니다.

이 모든 과정에서 Handler에 message를 전달하거나 처리하는 과정이 없이, 자연스럽게 GUI 작업을 한 것을 확인할 수 있습니다.


Conclusion

Android의 메인스레드에서는 GUI 작업만하고, 그외의 작업은 GUI가 블락되는 것을 막기 위해 다른 스레드에서 작업하도록 권장됩니다. 이를 수행하기 위해 Looper와 Handler 클래스가 제공됩니다.

스레드에서 Looper를 동작 시키면 내부의 message queue를 돌면서 하나씩 Handler에게 전달합니다. Handler는 전달받은 message나 runnable을 처리하거나, 반대로 message나 runnable을 전달 받아 message queue에 넣습니다.

AsyncTask는 위의 Handler나 Looper의 직접 구현 없이도 간단하게 사용되도록 고안된 클래스입니다. 다만 수초 정도의 비교적 작은 작업을 수행하면서 GUI 업데이트를 하는 데에 이상적이기 때문에, 더 오래걸리는 작업은 다른 방법을 생각해야 합니다.

결론적으로, 두가지 방법 모두 익힌 상태에서 상황에 따라 Handler와 Looper 혹은 AsyncTask를 선택할 수 있어야 겠습니다.

'programming > android' 카테고리의 다른 글

View - RecyclerView  (0) 2018.08.26
ViewGroup  (0) 2018.08.24
View - AppWidget  (0) 2018.08.23
View - Widget  (0) 2018.08.23
Companion Objects in Kotlin  (0) 2018.04.08

View - RecyclerView



reference:

https://developer.android.com/guide/topics/ui/layout/recyclerview 





 RecyclerView 개요

RecyclerView는 양이 많거나 자주 바뀌는 데이터의 목록을 표시하는 데에 사용합니다. ListView의 개선된 버전이라고 볼 수 있습니다. 

RecyclerView에서 데이터를 표현하기 위해서는 몇가지 요소들이 같이 작동해야 합니다. 

RecyclerView 

기본적으로 ListView와 마찬가지로 뷰를 표시하는 컨테이너로서, layout에 추가되는 요소입니다.

Layout manager

RecyclerView에 제공하여 목록에 아이템을 채우는 역할을 합니다. LinearLayoutManager, GridLayoutManager 등 기존의 layout manager를 사용하거나 직접 만들 수 있습니다.

View holder

목록 안의 아이템들은 view holder 객체로서 표현됩니다. 이 객체들은 RecyclerView.ViewHolder 클래스를 상속 받은 객체입니다. 하나의 아이템을 표현하는 역할을 합니다. RecyclerView는 목록의 아이템 갯수 만큼 view holder 객체를 생성합니다. 사용자가 스크롤 하면 ReyclerView는 화면에서 가려진 아이템을 회수해서 새로 보여질 아이템을 보여주는데 사용합니다.

View holder 객체는 RecyclerView.Adapter 클래스를 상속받은 adpater에 의해 관리됩니다. Adpater 클래스는 필요에 의해 view holder를 만들고, 또한 view holder에 표시할 데이터를 바인드합니다. 이것은 view holder를 아이템의 위치(position)에 할당한 후 adpater의 onBindViewHolder() 메소드를 호출하는 것으로 이루어집니다. 위치에 따라 데이터의 어떤 컨텐츠를 표시할 것인지 결정하는 것입니다.

RecyclerView에서는 다음과 같은 최적화가 되어있습니다:

    • 목록이 처음 결정되면, 그 목록 일부의 대한 view holder를 생성하고 바인드합니다. 예를 들어, 0부터 9 위치의 목록이 표시된다면 RecyclerView는 9까지 view holder를 만들고 바인드합니다. 그리고 10번의 view holder를 만들고 바인드할 수도 있는 것입니다. 이 방법으로, 스크롤 되었을 때 표시할 아이템을 준비시켜놓는 것입니다.

    • 유저가 목록을 스크롤하면, RecyclerView는 필요에 따라 새로운 view holder를 만듭니다. 그리고 화면 바깥으로 밀려난 아이템에 대한 view holder들을 저장하여 다시 사용될 수 있게 합니다. 만약 스크롤 중에 반대방향으로 스크롤하면 저장되어있던 view holder를 바로 불러와서 표시하게 됩니다. 반면에 계속 같은 방향으로 스크롤하면 밀려난 위치의 view holder가 새롭게 표시될 아이템에 다시 바인드 됩니다. View holder는 새로 생성되거나 새로운 view를 inlfate 시킬 필요가 없습니다. 대신 새로운 아이템에 대해서 바인드를 새로하는 것입니다.

    • 표시된 아이템이 변한다면, adapter에 알리면 됩니다(RecyclerView.Adapter.notify...() 메소드). Adpater의 코드가 영향을 미치는 아이템에 대해 다시 바인드시킵니다. 


 RecyclerView 사용하기

Activity에서 다음과 같이 layout에 추가된 RecyclerView를 사용하도록 합니다.

@Override
   
protected void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);
        setContentView
(R.layout.my_activity);
        mRecyclerView
= (RecyclerView) findViewById(R.id.my_recycler_view);

       
// use this setting to improve performance if you know that changes
       
// in content do not change the layout size of the RecyclerView
        mRecyclerView
.setHasFixedSize(true);

       
// use a linear layout manager
        mLayoutManager
= new LinearLayoutManager(this);
        mRecyclerView
.setLayoutManager(mLayoutManager);

       
// specify an adapter (see also next example)
        mAdapter
= new MyAdapter(myDataset);
        mRecyclerView
.setAdapter(mAdapter);
   
}

LinearLayoutManager로 설정해놓아 일렬로 표시되도록 하고, setAdapter()를 통해 adapter를 설정했습니다.

다음은 adapter 클래스입니다.

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
   
private String[] mDataset;

   
// Provide a reference to the views for each data item
   
// Complex data items may need more than one view per item, and
   
// you provide access to all the views for a data item in a view holder
   
public static class MyViewHolder extends RecyclerView.ViewHolder {
       
// each data item is just a string in this case
       
public TextView mTextView;
       
public MyViewHolder(TextView v) {
           
super(v);
            mTextView
= v;
       
}
   
}

   
// Provide a suitable constructor (depends on the kind of dataset)
   
public MyAdapter(String[] myDataset) {
        mDataset
= myDataset;
   
}

   
// Create new views (invoked by the layout manager)
   
@Override
   
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
                                                   
int viewType) {
       
// create a new view
       
TextView v = (TextView) LayoutInflater.from(parent.getContext())
               
.inflate(R.layout.my_text_view, parent, false);
       
...
       
MyViewHolder vh = new MyViewHolder(v);
       
return vh;
   
}

   
// Replace the contents of a view (invoked by the layout manager)
   
@Override
   
public void onBindViewHolder(MyViewHolder holder, int position) {
       
// - get element from your dataset at this position
       
// - replace the contents of the view with that element
        holder
.mTextView.setText(mDataset[position]);

   
}

   
// Return the size of your dataset (invoked by the layout manager)
   
@Override
   
public int getItemCount() {
       
return mDataset.length;
   
}
}

위 adpater은 TextView를 하나 사용하는 아이템에 대한 행동을 정의하고 있습니다. 표시할 아이템들의 목록을 필드로 갖고, 바인드되었을 때 어떻게 아이템을 표시할 것인지 onBindViewHolder()에 정의했습니다. 또한 처음 bind되었을 때 어떻게 view holder를 만들 것인지 onCreateViewHolder()에 정의했습니다. 이 예시에서는 아이템에 대한 layout을 inflate하고 그 view를 가지고 view holder를 만들도록 했습니다. 이 메소드 안에서는 어떻게 아이템을 표시할 것인지 정의하지 않습니다.


추가적으로, 다음과 같은 기본 제공 layout manager를 사용할 수도 있습니다.

LinearLayoutManager : 1차원의 목록을 일렬로 표시, ListView와 같이 표시됨

GridLayoutManager : 2차원의 grid로 표시함.

StaggeredGridLayoutManger : 아이템의 offset이 살짝 서로간에 다른 2차원 grid로 표현함(예를 들어미국 국기의 별들)

 

Conclusion

RecyclerView은 여러 최적화를 거친 ListView입니다. 데이터를 view holder에 바인드 시켜 어떻게 표시할 것인지 정의합니다. 또한 layout manager를 통해 어떤 식으로 목록을 표시할 것인지 customize할 수 있습니다. 

View holder는 아이템이 바인드될 view를 갖는 클래스입니다. 기존에 ListView에서 방식은 findView하는 과정이 반복되었는데, view holder를 통해 이를 개선한 효과도 있습니다.

표시할 데이터가 변했을 때 adpater에 이를 알리면 onBindViewHolder() 메소드가 호출되어 다시 표시하도록 합니다. 


아이템 목록

-> Adpater에 연결

-> Adpater가 position에 따라 해당 위치의 데이터를 표시가능한 형태(View holder)로 연결

-> 목록이 변했음을 adapter에 알리면, 목록을 view holder에 다시 바인드

-> 새롭게 bind된 목록을 다시 RecyclerView에 표시

'programming > android' 카테고리의 다른 글

Thread with Handler & AsyncTask  (0) 2018.08.27
ViewGroup  (0) 2018.08.24
View - AppWidget  (0) 2018.08.23
View - Widget  (0) 2018.08.23
Companion Objects in Kotlin  (0) 2018.04.08

ViewGroup



reference: 

https://developer.android.com/reference/android/view/ViewGroup

https://developer.android.com/guide/topics/ui/layout/linear?hl=ko

https://developer.android.com/guide/topics/ui/layout/relative

https://developer.android.com/reference/android/support/constraint/ConstraintLayout#DimensionConstraints




 ViewGroup 개념

ViewGroup이란 다른 View를 자식으로 가질수 있는 특별한 형태의 View입니다. Layout, View containers의 부모 클래스이기도 합니다. ViewGroup은 LayoutParam 클래스를 포함하고 있는데, 이는 ViewGroup이 자식 View들에게서 어떤 속성을 받아 사용할 것인지 정의하는 역할을 합니다. 흔히 "android:layout_" 으로 시작하는 속성들이 이것에 해당합니다.  


 ViewGroup 중 대표적인 Layout들

Android를 개발하면서 흔히 접할 수 있는 layout은 다음과 같습니다.

LinearLayout

RelativeLayout

FrameLayout

ConstraintLayout

위 layout들의 차이점을 분명히 인식하고 있어야 어느 상황에서 적절히 사용할 수 있는지 알 수 있을 것입니다. 구체적으로 어떻게 작성하는지는 참조 링크로 대체하겠습니다.


 LinearLayout

가로 혹은 세로 방향의 orientation("android:orientation" 속성)이 주어지고 그 단일 방향으로 모든 하위 View를 그립니다. 모든 항목은 순차적으로 배치되기 때문에 가로 방향의 경우 한 행에 한 View만 놓이게 됩니다. layout_weight 속성을 통해 가중치를 부여할 수 있습니다. 세로 방향인 경우 하위 View의 height를 0dp로 부여하고 weight를 1로 적은후, 나머지 View에는 weight를 부여하지 않으면 부여된 View가 남은 화면을 채우도록 확장됩니다.


RelativeLayout

하위 View들의 위치가 형제 View나 부모의 위치를 기준으로 설정될 수 있는 layout입니다. 주의할 점은 서로 상충되는 속성값을 갖도록 부여해서는 안된다는 점입니다. 예를들어 RelativeLayout의 height가 wrap_content인데, 자식 View가 ALIGN_PARENT_BOTTOM 속성을 갖도록 해서는 안됩니다. 아래는 주요하게 사용되는 주요 속성들입니다.


   android:layout_alignParentTop
   If "true", makes the top edge of this view match the top edge of the parent.
   android:layout_centerVertical
   If "true", centers this child vertically within its parent.
   android:layout_below
   Positions the top edge of this view below the view specified with a resource ID.
   android:layout_toRightOf
   Positions the left edge of this view to the right of the view specified with a resource ID.
참고로 이름에 right/left가 들어가는 속성은 RTL(Right to Left) 언어에서 의도치않게 동작할 수 있으므로 start/end로 대체하는 것이 권장됩니다. 실제로 Android Studio에서도 그렇게 권장합니다.

FrameLayout

화면의 부분 혹은 전체를 하나의 View로 덮어 씌우도록 의도된 layout입니다. 일반적으로 하나의 자식을 갖도록 하는데, 자식들의 위치를 조절하기가 쉽지 않기 때문입니다. 다만, "android:layout_gravity" 속성을 통해 자식들의 위치를 어느정도 지정할 수 있습니다. 자식들은 stack처럼 아래에서 부터 위로 올라가는 형태로 겹쳐집니다.


ConstraintLayout

자식 View들을 유연하게 배치할 수 있는 layout입니다. 아래와 같이 다양한 옵션을 제공합니다.

Relative positioning

Margins

Centering positioning

Circular positioning

Visibility behavior

Dimension constraints

Chains

Virtual Helpers objects

Optimizer


Relative positioning

left, right, top, bottom 혹은 baseline을 기준으로 형제간에 상대적으로 위치를 지정할 수 있습니다. 

 
Fig. 1 - Relative Positioning Example

A의 right를 기준으로 B의 left가 정해진 예시입니다. B의 "app:layout_constraintLeft_RightOf" 속성 값을 A의 id로 부여한 경우입니다. 같은 레벨의 형제 View나 부모만 값으로 가질 수 있습니다.


Margins

left, right, top, bottom를 기준으로 constraint target으로부터의 margin을 부여할 수 있습니다.

 
Fig. 3 - Relative Positioning Margins

위 예시는 B의 left가 A의 right로 relative positioning된 상태에서 marginLeft가 적용된 경우입니다. 또한 goneMargin을 부여할 수 있는데, 이는 constraint target의 visibility가 View.GONE이 된 경우의 margin을 뜻합니다. 위 경우에서 goneMarginLeft를 0dp로 한 상태에서 A를 gone 처리 하면 B가 A의 위치에 조정 될 것입니다.

Centering positioning and bias

Constraint target을 기준으로 위치에 대한 bias를 부여합니다. 

 
Fig. 4 - Centering Positioning

위는 A의 left와 right가 동시에 적용하면 기본적으로 그 중간에 위치하게 된다는 것을 보여줍니다.

 
Fig. 5 - Centering Positioning with Bias

horiziontal bias를 부여한 그림입니다. 0이면 left에 붙고 1이면 right에 붙게 됩니다. 즉, left와 right 사이 어느 위치에 오게될 지 bias로 정할 수 있습니다.


Circular positioning

Circular한 기준으로 위치를 정할 수 있습니다.

  
Fig. 6 - Circular Positioning   

constraintCircle 속성으로 기준을 정하고, constraintCircleRadius와 constraintCircleAngle을 정합니다. 위는 12시를 기준으로 45도 만큼의 angle이 주어진 예시입니다.


Visibility behavior

Constraint target이 View.GONE 처리 된 상태이더라도 여전히 그 자리에 있는 것처럼 동작합니다. 실제로 그 것이 보이지 않더라도 말이죠.

 
Fig. 7 - Visibility Behavior

위 예시는 A가 gone 처리 되었음에도 B의 left가 여전히 존재하는 것처럼 정상적으로 동작하는 것을 보여줍니다. 다만 A가 A의 left로 대체되었을 뿐입니다.


Dimension constraint

Layout의 크기를 minimum, maximum 값으로 부여할 수 있습니다. wrap_content와 함께 사용되어야 하겠죠. min(max)Width, min(max)Height로 지정합니다.


Layout의 높이와 너비는 세가지로 부여될 수 있습니다.  

1. 특정한 dimension 값

2. WRAP_CONTENT

3. 0dp (MATCH_CONSTRAINT와 같음)

주의할 점이 있다면, ConstraintLayout에의 자식들의 크기에는 MATCH_PARENT가 권장되지 않습니다.  비슷한 속성으로 MATCH_CONSTRAINT를 사용하는데, 이는 left, right, top, bottom의 constraint target이 부모로 지정되는 효과를 갖습니다.

1.1 버전 이후부터는 WRAP_CONTENT의 크기를 갖는 자식 View에 대해 constraint target을 침범할 것인지 여부를 정할 수 있습니다.

<app:layout_constrainedWidth="true"> 속성이 부여된 자식은, left와 right를 침범하지 않습니다. 반대로 height에 대한 속성도 있습니다.


Percent

또한 크기를 percent로 부여할 수 있습니다. 크기가 0dp(MATCH_CONSTRAINT)로 지정되고, 

<app:layout_constraintWidth_default="percent">

<app:layout_constraintWidht_percent="0.5">

위 속성들이 정의된다면, 이 View의 너비는 left와 right 사이에 너비의 정확히 절반을 가지게 될 것입니다.


Ratio

너비와 높이를 서로의 비율로 부여할 수 있습니다. 아래 Button은 높이가 너비에 맞춰 1:1이 되도록 설정한 예시입니다. (width:height)

<Button android:layout_width="wrap_content"

android:layout_height="0d"

app:layout_constraintDimensionRatio="1:1" />


Chain

양방향으로 서로 연결된 View들의 관계 Chain이라고 말합니다.

 
Fig. 9 - Chain

이 예시는 A와 B가 서로에게 chain 되어 있습니다.

 
Fig. 10 - Chain Head

가장 앞쪽(혹은 위쪽)에 있는 요소가 chain의 head가 됩니다.


Chain Style

Chain된 View들이 어떻게 배치될 것인가에 대한 style 입니다.


 
Fig. 11 - Chains Styles

어떻게 배치할 것인지 유용하게 사용될 수 있을 것 같습니다.


Virtual Helper objects

자세한 내용이 없어 원문으로 대체합니다.

In addition to the intrinsic capabilities detailed previously, you can also use special helper objects in ConstraintLayout to help you with your layout. Currently, the Guideline object allows you to create Horizontal and Vertical guidelines which are positioned relative to the ConstraintLayout container. Widgets can then be positioned by constraining them to such guidelines. In 1.1, Barrier and Group were added too.


Optimizer(in 1.1)

1.1 부터 app:layout_optimizationLevel 태그로 부여할 수 있습니다. 아직 실험 단계로 보여 지켜봐야할 것 같습니다. 이름 그대로 최적화에 관한 내용으로 추정됩니다.

    • none : no optimizations are applied
    • standard : Default. Optimize direct and barrier constraints only
    • direct : optimize direct constraints
    • barrier : optimize barrier constraints
    • chain : optimize chain constraints (experimental)
    • dimensions : optimize dimensions measures (experimental), reducing the number of measures of match constraints elements


Conclusion

Android에서 자주 사용되는 layout 4종류에 대해 간략히 알아봤습니다.


LinearLayout : 자식들이 단방향으로 일정한 순서로 배치될 때

RelativeLayout : 자식들의 위치가 서로에 관계가 있을 때

FrameLayout : 자식을 하나 정도 가지며 위치를 조정할 필요가 없고, 자식들이 서로 겹치도록 하고 싶을 때

ConstraintLayout : 복잡한 자식들의 위치나 크기 설정을 요구할 때


위와 같은 기준으로 적재적소에 layout을 사용할 수 있어야겠습니다.

'programming > android' 카테고리의 다른 글

Thread with Handler & AsyncTask  (0) 2018.08.27
View - RecyclerView  (0) 2018.08.26
View - AppWidget  (0) 2018.08.23
View - Widget  (0) 2018.08.23
Companion Objects in Kotlin  (0) 2018.04.08

+ Recent posts