마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

android listview viewholder 적용 시 문제입니다...

0 추천

현재 ListView에 데이터를 ViewHolder 패턴을 이용해서 구현하였습니다.

 

또한 ListView item 내에 TextView의 클릭 이벤트를 감지하여

ViewHolder를 제어하기 위해서 ArrayList<ViewHolder>를 생성하였습니다.

 

그렇게해서 converView==null 일 때 ArrayList에 add를 하는데요.

 

다음은 getView 코드입니다.

@Override
    public View getView(final int position, View convertView, ViewGroup parent) {

        ViewHolder holder;

        if(convertView == null){
            convertView = mInflater.inflate(R.layout.row_coupon_list, null);

            holder = new ViewHolder();

            Log.i("size", String.valueOf(position));
            holder.rlcontent = (RelativeLayout) convertView.findViewById(R.id.row_coupon_rl_content);
            holder.txtname = (TextView) convertView.findViewById(R.id.row_coupon_tv_name);
            holder.txtenddate = (TextView) convertView.findViewById(R.id.row_coupon_tv_enddt);
            holder.txtstore = (TextView) convertView.findViewById(R.id.row_coupon_tv_store);
            holder.txtdiscount = (TextView) convertView.findViewById(R.id.row_coupon_tv_discount);
            holder.txtuse = (TextView) convertView.findViewById(R.id.row_coupon_tv_use);
            holder.ivimage = (ImageView) convertView.findViewById(R.id.row_coupon_iv_image);

            holder.rlbarcode = (RelativeLayout) convertView.findViewById(R.id.row_coupon_rl_barcode);
            holder.ivbarcode = (ImageView) convertView.findViewById(R.id.row_coupon_iv_barcode);
//            holder.txtbarcode = (TextView) convertView.findViewById(R.id.row_coupon_tv_barcode);

            mHolderList.add(holder);

            convertView.setTag(mHolderList.get(position));
        } else {
            Log.i("size2", String.valueOf(position));

            mHolderList.set(position, (ViewHolder) convertView.getTag());
//            holder = (ViewHolder) convertView.getTag();
        }

        mHolderList.get(position).txtname.setText(mCouponList.get(position).getName());
        mHolderList.get(position).txtenddate.setText(mCouponList.get(position).getEnd_dt() + "까지");
        mHolderList.get(position).txtstore.setText(mCouponList.get(position).getStore_summary());
        if(mCouponList.get(position).getType() == 1) {
            mHolderList.get(position).txtdiscount.setText(String.valueOf(mCouponList.get(position).getDiscount()) + "%");
        } else if(mCouponList.get(position).getType() == 2) {
            mHolderList.get(position).txtdiscount.setText("-" + String.valueOf(mCouponList.get(position).getDiscount()) + "원");
        }
        if(mCouponList.get(position).getImage() != null) {
            ImageLoader.getInstance().displayImage(mCouponList.get(position).getImage().getUrl(), mHolderList.get(position).ivimage, options);
        }

        mHolderList.get(position).txtuse.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i("position", String.valueOf(position));
                try {
                    // 해당 포지션의 바코드 정보를 가져온다.
                    Barcode barcode = new CouponUseTask(mContext).execute(mCouponList.get(position).getCoupon_id()).get();

                    // 바코드가 있으면 바코드를 보여줌
                    if (barcode != null) {
                        mCouponList.get(position).setBarcode(barcode);
                        if(UserAuthUtil.getAccessToken(mContext) != null) {
                            ImageLoader.getInstance().displayImage(mCouponList.get(position).getBarcode().getBarcode_image().getUrl() + "?access_token=" + UserAuthUtil.getAccessToken(mContext), mHolderList.get(position).ivbarcode, options);
                        } else {
                            ImageLoader.getInstance().displayImage(mCouponList.get(position).getBarcode().getBarcode_image().getUrl(), mHolderList.get(position).ivbarcode, options);
                        }
                        mHolderList.get(position).rlbarcode.setVisibility(View.VISIBLE);
                        mHolderList.get(position).rlcontent.setVisibility(View.INVISIBLE);
                    }

                    // 클릭된 포지션 외에 전환
                    for(int i = 0; i < mHolderList.size(); i++) {
                        if(i == position)
                            continue;
                        else {
                            mHolderList.get(i).rlcontent.setVisibility(View.VISIBLE);
                            mHolderList.get(i).rlbarcode.setVisibility(View.INVISIBLE);
                        }
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        return convertView;
}

문제점)

 - ListView를 드래깅하여 내려가다가 새로운 아이템이 생성될 때(예를들면 position 7번째의 아이템이 생성될 때) 

if(converView == null) 로 분기되어 ArrayList에 add를 해야하는데, 이 시점에 

else 구문을 타면서 add 받지 못한 position의 값이 set되려고 합니다. 그래서 어플이 강제종료 되버리는 현상이 발생합니다.

 

해결책이 있을까요? 혹은 다른 방법이 있는지 궁금합니다...

taejun (7,240 포인트) 님이 2015년 6월 16일 질문

2개의 답변

+1 추천
 
채택된 답변
뷰홀더는 리스트뷰를 표현할때 리스트뷰의 특성상 같은 뷰를 보여주게 되는데 매번 새로 생성하는 부하를 막기위해 쓰이는 방법입니다.

당연히 이미 리스트뷰가 생성 되면서 뷰홀더가 생성 되었기에 분기에서 elsel를 탈수 밖에요.

그리고 뷰 홀더를 제어 한다는 말이 무슨 말인지 모르겠어요.

왜 어레이에 뷰홀더를 담는 지도 모르겠구요~~!

정확한 해결 방법을 아시려면 좀더 많은 정보가 필요 해보입니다.
ThisPlus (46,920 포인트) 님이 2015년 6월 16일 답변
taejun님이 2015년 6월 17일 채택됨
답변 감사드립니다.
일단 ViewHolder를 ArrayList에 담은 이유는...
리스트뷰내의 텍스트뷰를 클릭 시, 클릭된 해당 리스트뷰의 아이템과 해당되지 않은 나머지 리스트뷰의 아이템을 모두 알아야 했기 때문이였습니다...
또한 음 재활용부분에서 에러가 나는 것이 아니라.
스크롤을 아래로 내리면서 새로운 리스트뷰의 아이템이 등장 시
converView가 null에 분기되면서 add되기를 기대했는데,
그렇지 않고 else로 분기되었습니다...
설계가 잘못된...거겠죠??

리스트뷰 내에서 텍스트뷰를 클릭하면 그 이벤트를 처리할 때
리스트뷰의 다른 아이템들을 제어할 방법이 있을까요??
뷰홀더 패턴 이해 후 완료하였습니다. 답변 감사해요!
+1 추천
뷰홀더는

각 getView에 생성된 childView의 setTag에다가 저장해두는겁니다.

배열로 관리하는게 아니라요.(그렇게 해도 되는데 지금 잘 구현이 안되니까..)

그래서 getTag해보고, null이면 새로 할당하고, setTag하고

null이 아니면 그냥 꺼내쓰고....

 

즉, convertView의 if와 else의 안쪽 어딘가 또는 조건 구문이 모두 끝나는 지점에서 getTag SetTag하는겁니다.
익명사용자 님이 2015년 6월 16일 답변
제가 리스트뷰 뷰홀더 개념에 대해 잘못이해하고 있었던 것 같습니다. 답변 감사해용!
...