RecyclerView.Adapter, ViewHolder

Posted by ITPangPang
2016. 4. 2. 16:26 안드로이드(android)/Recyclerview


RecyclerView.Adapter, ViewHolder


- 이번에는 지난시간에 만든 코드로 로그캣을 찍어보면서 

   어떻게 프로그램이 돌아가는지 알아본다.

- 이전글에서 사용했던 코드를 그대로 사용하면서 + Log만 달면서

   천천히 알아보겠습니다.

- 추가적으로 조금더 설명이 필요하다고 생각되는 부분은 

  변형해서 다음글에서 올려보겠습니다.

- 뭐 분석까진 안해도 기본적으로 사용하는데에는 무리가 없으나 

  좀 더 정확히 알고 싶은 분들 위주로 보시길..


1. 지난번 .java파일에서 로그만 일부분 붙여보겠습니다


2. 일단 Main에서 클릭이 이루어졌을때 CountAdapter에서 값을 받는부분까지만 로그


MainActivity.java

public class MainActivity extends Activity
{
RecyclerView rv;
LinearLayoutManager llm;
List<Integer> count = null;
Button btn;
int i=0;
final static String TAG = "ITPANGPANG";

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

rv = (RecyclerView)findViewById(R.id.rv);
btn = (Button)findViewById(R.id.btn);
llm = new LinearLayoutManager(this);
rv.setHasFixedSize(true);
rv.setLayoutManager(llm);

count = new ArrayList<>();

btn.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Log.d(TAG,"==========MainActivity===========");
i++;
Log.d(TAG,"i==>"+i);
count.add(i);
Log.d(TAG, "count==>" + count.toString());
rv.setAdapter(new CountAdapter(getApplication(), count, i));
}
});
}
}


CountAdapter.java

public class CountAdapter extends RecyclerView.Adapter<CountAdapter.MyViewHolder>
{
Context mContext;
List<Integer> items;
int i = 0;
final static String TAG = "ITPANGPANG";
public CountAdapter(Context c, List<Integer> items, int i)
{
this.mContext = c;
Log.d(TAG,"==========CountAdapter===========");
Log.d(TAG, "this.items==>" + this.items);
Log.d(TAG, "this.i==>" + this.i);
this.items = items;
this.i = i;
Log.d(TAG, "건내받고 this.items==>" + this.items);
Log.d(TAG, "건내받고 this.i==>" + this.i);
}
@Override
public CountAdapter.MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i)
{
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recyclerview_custom, viewGroup, false);
return new MyViewHolder(v);
}
@Override
public void onBindViewHolder(CountAdapter.MyViewHolder myViewHolder, int position)
{
final Integer item = items.get(position);
myViewHolder.tv.setText(""+item);

}

@Override
public int getItemCount()
{
return this.items.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder
{
TextView tv;
public MyViewHolder(View itemView)
{
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
}
}
}


3. 버튼 클릭 한번 했을때(i가 1로 증가했을때겠죠)



로그캣을 보면 MainActivity에서 처음에 버튼을 클릭했을때

i==>1로 증가하는것이 보이고 List인 Count에 [1]이 add된걸 볼 수 있습니다.

이후에 setAdapter를 통해서 CountAdapter를 호출합니다.


생성자 부분에 값을 받기전과 값을 받은후에 각각 로그를 찍어봤습니다.

값을 받기전에는 items(Main에서 Count였는데 그냥 items라고 이름만 바꾼겁니다)

items는 Null값, i값은 0입니다.


값을 전달 받은후에 다시 찍어봤더니 각각 [1]과 1을 건내받은걸 확인 할 수 있습니다.



4. 버튼을 한번더 클릭했을때(i가 2로 증가했을때겠죠)

한번더 클릭을 해보니 i==>2로 증가했습니다

List에는 [1, 2] 2개를 저장하고 있습니다.


CountAdapter로 넘어온 후에

건내받기 전에는 null과 0이었지만

건내받은 후에는 Main값 그대로 [1,2]과 2의 값을 얻었습니다.


5. 여기까지가 버튼을 눌렀을때 List에 값이 추가되고, Adapter.java에서 값을 전달받는부분입니다.


6. 이제 여기까지는 알았으니 코드가 길어지니 이전로그는 지우고 Holder부분에 로그를 달아서 알아보겟습니다


7. 일단 CountAdapter로 넘어왔을때 어떤 메서드에 먼저 들어오는지 로그에 제목만 찍어보고 돌려보겠습니다.

public class CountAdapter extends RecyclerView.Adapter<CountAdapter.MyViewHolder>
{
Context mContext;
List<Integer> items;
int i = 0;
final static String TAG = "ITPANGPANG";

public CountAdapter(Context c, List<Integer> items, int i)
{
this.mContext = c;
this.items = items;
this.i = i;
}
@Override
public CountAdapter.MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i)
{
Log.d(TAG,"onCreateViewHolder");
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recyclerview_custom, viewGroup, false);
return new MyViewHolder(v);
}
@Override
public void onBindViewHolder(CountAdapter.MyViewHolder myViewHolder, int position)
{
Log.d(TAG,"onBindViewHolder");
final Integer item = items.get(position);
myViewHolder.tv.setText(""+item);
}

@Override
public int getItemCount()
{
Log.d(TAG, "getItemCount");
return this.items.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder
{
TextView tv;
public MyViewHolder(View itemView)
{
super(itemView);
Log.d(TAG, "MyViewHolder");
tv = (TextView) itemView.findViewById(R.id.tv);
}
}
}


8. 클릭을 한번 해보면



getItemCount, onCreateViewHolder, MyViewHolder, onBindViewHolder순으로 들어오는것을 확인했습니다.



9.이번에는 각 메서드에 각각 어떤 값이 들어오는지 찍어보겠습니다(클릭 한번 했을때)

봐야되는 부분은 처음 1이 찍히는 순간

42b9어쩌구의 View가 하나 생기고 그값을 MyViewHolder에 넘겨줍니다


그리고 onBindViewHolder부분에서는 holder에 대한 정보가 나옵니다

그리고 마지막으로 position에 0이 들어온것을 확인 할 수 있습니다.


9.이번에는 버튼을 8번정도 눌러서 확인해보겟습니다.

폰 화면은 이렇게 찍혀있습니다


음 여기서 주목할점은 position이야 다들 아시겠지만 0부터 시작해서 7 입니다.

View를 보면 {앞에 글자랑 숫자가 다르므로 서로 다른 뷰가 생성되는것이 보입니다.

Holder도 {앞에 이름과 position만 다르게 변한것을 확인 할 수 있습니다.


10. 이번에는 버튼을 51번 누른상태에서 스크롤을 가장 아래로 내렸습니다


11. 누를동안 로그캣에 엄청난 값이 찍혔겠죠? 이상태에서 로그캣에 마우스오른쪽을 눌러서 Clear All을 눌러서 다 지워봅니다

12. 그리고 폰에서 스크롤을 위로 올리면서 새로 로그캣에 찍히는 값을 보겠습니다.

딱 이정도 올렸습니다 숫자 20이 보이는시점까지

여기까지 올리는동안 로그캣에 찍힌값을 보겠습니다.



여기서 볼점은 2가지입니다.

첫번째, 스크롤이 움직이는동안 onBindViewHolder만 계속 호출된다.

두번째, position의 의미를 알 수 있습니다.

마지막에 20이 살짝 보였을때 onBindViewHolder에 들어오면서 position이 19라는것을

알 수 있습니다. position이 고유 위치라는 것을 알 수 있습니다.


13. 다음실험은 앱도 껐다 키고 로그캣도 싹다 지운상태에서 로그캣의 검색부분에 View v라고 입력한다음에

     view가 생성되는것만 집중적으로 보겠습니다. 

    

버튼 1번 눌렀을때(1이 찍혀있을때)


버튼 2번 눌렀을때 (1,2 찍혀있을때)


버튼 3번 눌렀을때(1,2,3 찍혀있을때)


자 각각 버튼을 누를때마다 1줄, 1+2줄, 3+3줄이 추가되는 것을 볼 수 있습니다.

그리고 {뒤를 보니 중복되는 숫자도 없습니다 

이것으로 버튼을 누를때마다 view가 새롭게 생성되는 것을 알 수 있습니다.

당연한 결과긴 하지만.. 새로 생성안됬으면 버튼 3번 눌렀을때 로그캣에 3줄이 찍혔어야겠죠



13. 그렇다면 버튼을 55번째 누를때는 55줄이 생성되면서 View 55개가 생성될까요? 54까지 띄우고

     로그캣을 싹 지운후에 55번째 누를때 로그캣을 확인해보겠습니다.

계산상 View 55개가 생성되야 하는데 생각과 다르게 22개만 생성됬습니다.

왜 그런가 생각하고 화면을 보니


화면에 22까지 보이는것을 확인 했습니다.

여기서 알 수 있는건 처음 onCreateViewHolder에서 생성될 때에도

화면에 보이는 부분만 처음에 생성한다는 것을 알 수 있었습니다.

그리고 스크롤을 내리면 다시 딱 한번더 새로운 뷰를 생성합니다


한번 화면을 제일 하단 55글씨가 보이는곳까지 내려봤습니다.

생각한 것은 22줄+23줄이 더 생성되어서 55개의 View가 나올것 같았는데

생각한것 과 달리 22개+3개가 더 생성되서 25개의 뷰가 생성되었습니다.

알 수가 없네요


14. 자 이번에는 좀 더 확실히 하기 위해서 Text의 크기를 2배로 늘려서 20sp->40sp 화면에 적은숫자의 

    글자가 들어오게 해서 재실험 해보겠습니다.(똑같이 54까지 찍은후에 로그캣 clear하고 55번째 버튼을 눌렀을때)


화면에 11개의 글자가 들어오네요



버튼눌렀을때 로그캣을 보니 생각대로 11개가 찍혔습니다.

이제 스크롤을 내리면서 추가적으로 +되는것을 보겠습니다

아까처럼 25개가 고정이라 +14가 될까요?


쫙 55까지 내려봤는데 11개에서 +3개가 추가되서 14개의 View가 생성됩니다.

그렇다면 아까는 22+3, 이번에는 11+3 무조건 3개만 더 추가시켜서

재활용되는것일까요? 이건 다음번 글에서 코드를 더 추가해서 알아보겠습니다.



15. 이번에는 스크롤이 움직일때마다 호출되는 onBindViewHolder를 보기위해 로그캣 검색에 MyHolder=>를 검색해서

    실험해보도록 하겠습니다.(맨 위 화면에서 숫자 25까지 내린후에 다시 위로 올려보겠습니다.)


보니까 대충 어떤식으로 생성되는지는 알 것 같습니다.

처음 아래로 내릴때 12라는 숫자가 먼저 보이므로 position13이 찍히고

쫙 25까지 내리다가(position24) 다시 스크롤이 위로 올렸을때

position이 12로 변합니다(이때 화면은 14까지 보이다가 13이 보이는순간이겠죠)



16. ViewHodler는 스크롤이 움직일때마다 계속 호출됩니다. 그런데 이것들은 재활용일까요? 아님 계속 생성될까요?

스크롤을 위아래로 엄청 한다음에 한가지를 골라서 찾아보니

무려 33 matches나 되었습니다. 재활용 된다는 뜻이겠죠

그런데 각각 position이 다르네요 언뜻 보기에는 position차이가 12씩 나는줄 알았는데


내려보니 그런것만은 아니네요 갑자기 16 차이도 나고 합니다.



역시 기본코드로 분석을 하려고 했던것은 무리였던것 같습니다.

다음글에서 조금더 코드를 추가해서 조금더 정확하게 알아보겠습니다.

알아볼것이 대충


1.onCreateViewHolder에서 최초로 생성되는 View갯수가 

처음 화면에 보이는갯수 +3인지에 대한 여부와


2.onBindViewHolder에서 생성되는 Holder가 어떻게 재활용되는지를

알아보면 될 것 같습니다.


뭐 이렇게까지 하나하나 알아본다는게 의미없을수도 있는데

어짜피 배우는거 기초를 튼튼히 해야 변형하고 응용해서 쓰기도 편하니

한번씩 시간남을때 각자 테스트 하면서 이해해보셔도 좋을 것 같습니다.




'안드로이드(android) > Recyclerview' 카테고리의 다른 글

RecyclerView(Item Click) 2탄  (8) 2016.04.07
RecyclerView(Item Click) 1탄  (0) 2016.04.06
RecyclerView.Adapter, ViewHolder(추가)  (3) 2016.04.03
RecyclerView 기본  (2) 2016.04.02
RecyclerView  (0) 2016.04.01