ListView의 재활용 메커니즘 작동 방식


146

그래서 나는 이전에이 문제가 있었고 당연히 여기 에 도움을 요청 했다 . Luksprog의 대답은 ListView와 GridView가 재활용 뷰로 어떻게 최적화되는지 전혀 알지 못했기 때문에 훌륭했습니다. 그래서 그의 조언으로 그리드 뷰에 뷰를 추가하는 방법을 변경할 수있었습니다. 문제는 이제 이해가되지 않는 것입니다. 이것은 getView나의 것입니다 BaseAdapter:


public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.day_view_item, parent, false);
        }
        Log.d("DayViewActivity", "Position is: "+position);
        ((TextView)convertView.findViewById(R.id.day_hour_side)).setText(array[position]);
        LinearLayout layout = (LinearLayout)convertView.findViewById(R.id.day_event_layout);

        //layout.addView(new EventFrame(parent.getContext()));

        TextView create = new TextView(DayViewActivity.this);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 62, getResources().getDisplayMetrics()), 1.0f);
        params.topMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        params.bottomMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        create.setLayoutParams(params);
        create.setBackgroundColor(Color.BLUE);
        create.setText("Test"); 
        //the following is my original LinearLayout.LayoutParams for correctly setting the TextView Height
        //new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, getResources().getDisplayMetrics()), 1.0f)   
        if(position == 0) {
            Log.d("DayViewActivity", "This should only be running when position is 0. The position is: "+position);
            layout.addView(create);
        }

        return convertView;
    }

}

스크롤 할 때 문제가 발생하며 위치 0이 아닌 위치 6에 위치합니다. 위치 6과 위치 8처럼 보이고 위치 8에 2를 넣습니다. 이제 여전히 ListView와 GridView를 사용하여 정지하려고합니다. 왜 이런 일이 일어나고 있는지 이해하지 못합니다. 내가이 질문을하는 주된 이유 중 하나는 ListView와 GridView의 재활용보기 또는이 기사 가 그것을 어떻게 다루는 지에 대해 모르는 다른 사람들을 돕기 위해 ScrapView 메커니즘입니다.

여기에 이미지 설명을 입력하십시오

나중에 편집

기본적으로 ListView 작동 방식을 이해하는 데 필요한 모든 Google IO 토크에 대한 링크 추가 댓글에서 링크가 죽었습니다. 따라서 user3427079는 해당 링크를 업데이트하기에 충분했습니다. 여기 에 쉽게 접근 할 수 있습니다.


하하, 그 링크 주셔서 감사합니다. 지금 시청하기 시작 :)
Andy

이전 질문의 코드 샘플을 완전히 구현하지 않았습니다. 아이디어는 항상 다른 행 (귀하의 경우 position 0) 에 대한 변경 사항을 되돌릴 수있는 코드가 필요하다는 것 입니다.
Luksprog

글쎄, @Luksprog 이해하지만 그것이 왜 다른 위치에 뷰를 두는지를 설명합니까? 어떤 경우에는 위치 0에만 중복 된보기가있을 것으로 예상했습니다. Idk, ListView가이 내용을 바인딩하는 방법을 이해하는 데 여전히 어려움이 있습니다 : /
Andy

1
재활용은 화면에서 방금 사라진 행보기 (예 : 한 행을 아래로 스크롤 한 후 위치 0)가 나중에 표시 할 ListView새 행 이 필요할 때 (아래로 스크롤 / 업으로 계속 하는 등) 나중에 사용될 수 있음을 의미 합니다. 문제는 방금 사라지고 앞으로 필요한 위치에서 사용될 뷰가 TextView이미 추가 되었다는 것 입니다. 이것이 문제입니다. 문제를 해결하려면 메소드가 이외의 위치에 대해 호출 된 getView경우 메소드 에서 제거해야합니다 . getView0
Luksprog

2
톱 게시물의 링크가 죽었습니다. youtube.com/watch?v=N6YdwzAvwOA
G_V

답변:


330

처음에 나는 또한 listview 재활용과 convertview 사용 메커니즘을 알지 못했지만 하루 종일 연구 한 후에 android.amberfog 의 이미지를 참조하여 목록보기의 메커니즘을 거의 이해했습니다 . 여기에 이미지 설명을 입력하십시오

목록보기는 어댑터로 채워질 때마다 기본적으로 목록 보기가 화면에 표시 할 수있는 수를 표시하며 목록을 스크롤해도 행 수는 증가하지 않습니다. 이것은 안드로이드가 사용하는 트릭으로 목록보기가보다 효율적이고 빠르게 작동합니다. 이제 이미지를 참조하는 listview의 내부 이야기에서 볼 수 있듯이 처음에는 listview에 7 개의 표시 가능한 항목이 있으며, 항목 1이 더 이상 표시되지 않을 때까지 위로 스크롤하면 getView ()가이보기 (예 : item1)를 재활용과 당신은 사용할 수 있습니다

System.out.println("getview:"+position+" "+convertView);

당신의 내부

public View getView(final int position, View convertView, ViewGroup parent)
{
    System.out.println("getview:"+position+" "+convertView);
    ViewHolder holder;
    View row=convertView;
    if(row==null)
    {
        LayoutInflater inflater=((Activity)context).getLayoutInflater();
        row=inflater.inflate(layoutResourceId, parent,false);
        
        holder=new PakistaniDrama();
        holder.tvDramaName=(TextView)row.findViewById(R.id.dramaName);
        holder.cbCheck=(CheckBox)row.findViewById(R.id.checkBox);
        
        row.setTag(holder);
        
    }
    else
    {
        holder=(PakistaniDrama)row.getTag();
    }
            holder.tvDramaName.setText(dramaList.get(position).getDramaName());
    holder.cbCheck.setChecked(checks.get(position));
            return row;
    }

처음에는 리사이클 러에 뷰 (예 : 항목)가 없으므로 getcat ()은 각 보이는 항목에 대해 새보기를 작성하지만 위로 스크롤하고 항목 1이 화면 밖으로 이동하면 재활용기로 전송됩니다. 체크 박스가 체크 상태의 경우, 현재 상태 (예를 들어 텍스트 뷰 '텍스트'또는 광산 경우로,이 뷰와 연관되며, 리사이클 러에 보관).

이제 위 / 아래로 스크롤하면 목록보기가 새보기를 만들지 않고 재활용기에있는보기를 사용합니다. 당신의에서 로그 캣 는 'convertView는'null이 아닌 것을 알 수 새 항목 (8) 즉, convertview를 사용하여 그려집니다 때문에는, 기본적으로는 그 자리에서 재활용 및 팽창 항목 8에서 항목 1보기를합니다, 당신은 관찰 할 수있다 내 코드에서. 체크 박스가 있고 위치 0에서 체크하면 (항목 1에 체크 박스가 있고 체크했다고 가정 해 봅시다) 아래로 스크롤하면 항목 8 체크 박스가 이미 선택된 것을 볼 수 있습니다. 이것은 listview가 동일한보기를 다시 사용하는 이유입니다. 성능 최적화로 인해 새로운 것을 만들지 않습니다.

중요한 것들

1 . 설정된 절대 layout_heightlayout_width귀하의 목록보기의 wrap_contentgetView()목록보기에서 인출되는 뷰의 높이를 측정하기위한 몇 가지 아이를 얻을 수 있도록 어댑터를 강제하고 convertview에게조차 목록을 반환하는 등의 예기치 않은 동작이 발생할 수 있습니다이 scrolled.always 사용하지 않습니다 match_parent또는 고정 너비 / 높이.

2 . 당신이 당신의 목록보기 및 질문의 힘 후 일부 레이아웃이나 뷰를 사용하려면 내가 설정하면 당신의 마음에 와서 layout_heightfill_parent내부에 목록보기를 넣어하기 위해 더 나은 그래서, 화면을 내려갑니다 표시되지 않습니다 목록보기 이후보기 layout.For 예를 선형 레이아웃 및 요구 사항으로 그 레이아웃의 높이와 폭을 설정하고 확인 높이 에 목록보기의 속성을 레이아웃의 등이다 (폭 레이아웃 경우 같은 320 과 높이가 280) 당신의 목록보기 다음 높이너비 가 같아야합니다. 이것은 getView ()에게 렌더링 할 뷰의 정확한 높이와 너비를 알려주고 getView ()는 몇 번의 임의의 행을 호출하지 않으며 스크롤하기 전에도 변환 뷰를 반환하는 것과 같은 다른 문제가 발생하지 않습니다. 이것은 내 목록보기가 lineaLayout 내부에 있지 않으면 LinearLayout 내부의 Listview를 마법처럼 작동시키는 것처럼 뷰 호출을 반복하고 뷰를 변환하는 것과 같은 문제가있었습니다. (이유를 몰랐습니다)

01-01 14:49:36.606: I/System.out(13871): getview 0 null
01-01 14:49:36.636: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.636: I/System.out(13871): getview 1 android.widget.RelativeLayout@406082c0
01-01 14:49:36.646: I/System.out(13871): getview 2 android.widget.RelativeLayout@406082c0
01-01 14:49:36.646: I/System.out(13871): getview 3 android.widget.RelativeLayout@406082c0
01-01 14:49:36.656: I/System.out(13871): getview 4 android.widget.RelativeLayout@406082c0
01-01 14:49:36.666: I/System.out(13871): getview 5 android.widget.RelativeLayout@406082c0
01-01 14:49:36.666: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.696: I/System.out(13871): getview 0 android.widget.RelativeLayout@406082c0
01-01 14:49:36.706: I/System.out(13871): getview 1 null
01-01 14:49:36.736: I/System.out(13871): getview 2 null
01-01 14:49:36.756: I/System.out(13871): getview 3 null
01-01 14:49:36.776: I/System.out(13871): getview 4 null

그러나 이제는 해결되었습니다. 나는 잘 설명하지 못하지만 하루 종일 이해하기 위해 저와 같은 다른 초보자가 내 경험에 도움을받을 수 있다고 생각했으며 지금은 사람들이 조금 이해하기를 바랍니다. 의 의 ListView 정말 지저분하고대로 작동 방식 프레임 워크 까다로운 그래서 초보자는 이해 너무 많은 문제가 발견


36
"나는 잘 설명하지 못한다는 것을 알고있다">>>이 답변에 대한 +37 표는 당신이 설명 할 때 훌륭한 일을했다고 알려줍니다. 지식을 계속 공유하십시오! ;)
2Dee

1
큰 답변 raja ji +1 from .. :)
Ehsan Sajjad

2
이미지 주셔서 감사합니다. Android를 시작할 때 ListView에 많은 문제가 발생한 후 이미 재활용 문제를 겪은 후 재활용 원칙을 알고있었습니다. 그래도이 그림과 문장이 if you had a checkbox and if you check it at position 0(let say item1 has also a checkbox and you checked it) so when you scroll down you will see item 8 checkbox is already checked,this is why listview is re using the same view not creating a new for you due to performance optimization.찍혔습니다. 제 실수를 깨달았습니다. 내가 본 Android ListView 재활용에 대한 최고의 게시물!
Kevin Cruijssen

1
@MuhammadBabar 예, 질문을 게시했습니다 : stackoverflow.com/q/30122027/1318946
Pratik Butani

1
@MuhammadBabar 멋진 설명 남자! 시간이 절약됩니다. 알고 싶어요 RecyclerView, 같은 메커니즘에서도 작동합니까? 내 질문 stackoverflow.com/questions/47897770/… , 나는 작업이 조용히 불가능하다는 것을 알고 listView있습니다. 내가 시도해야합니까 recyclerView?
Shambhu

8

Holder 패턴에서 Holder 객체에 위치를 설정하는 경우 매번 다음과 같이 설정해야합니다.

@Override
public final View getView(int position, View view, ViewGroup parent) {
    Holder holder = null;
    if (view == null) {
        LayoutInflater inflater = (LayoutInflater) App.getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(getContainerView(), parent, false);
        holder = getHolder(position, view, parent);
        holder.setTag(tag);
        view.setTag(holder);
    } else {
        holder = (Holder) view.getTag();
    }
    holder.position = position;
    draw(holder);
    return holder.getView();
}

이것은 추상 클래스의 예입니다.

getHolder(position, view, parent);

에 대한 모든 설정 작업을 수행

ImageViews, TextViews, etc..

1
이 모든 시간 동안 내가하고있는 것을 보지 못했습니다. parent.setTag(holder);올바른 방법 대신에view.setTag(holder);
ArtiomLK

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.