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

스크롤뷰안에 뷰페이저와 리사이클러뷰 사용시 성능개선 질문

0 추천

안녕하세요 제가현재 스크롤러 뷰를 활용하여 리사이클러뷰와 뷰페이저에 인디케이터를 달아 같이 보여주는 화면을 설계했습니다. 뷰페이저에 10개정도의 이미지를 넣었고, 리사이클러뷰 또한 10개정도의 카드뷰를 넣었습니다. 그리고 카드뷰에는 이미지뷰와 텍스트뷰로 구성되어있습니다.

그 결과 최신폰에서는 괜찮은데 갤럭시s4정도의 스마트폰에서는 화면버벅임 현상과 아웃오브 메모리현상이 발생합니다.

그리하여 이것저것 시도한 결과 알아낸것이 뷰페이저에서 인디케이터를 빼버리거나, 리사이클러뷰 카드뷰 개수를 3개이하로 할 경우 버벅임 현상과 아웃오브 메모리현상이 발생하지 않았습니다.

이를 개선하고싶은데 방법을 알고싶습니다. 소스도 첨부하겠습니다.

리사이클러뷰 어댑터구요

public class DetailInfoAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private String TAG = getClass().getSimpleName();
    private static final int TYPE_TOP = -1;
    private static final int TYPE_COMMENT = -2;

    Context context;
    List<DetailInfoComment> answer_data;
    int item_layout;

    public DetailInfoAdapter(Context context, List<DetailInfoComment> answer_data, int itme_layout) {
        this.context = context;
        this.answer_data = answer_data;
        this.item_layout = itme_layout;

        Log.d(TAG, "어댑터시작");
        Log.d(TAG, context.toString());
        Log.d(TAG, answer_data.toString());
        Log.d(TAG, Integer.toString(itme_layout));
    }

    @Override
    public int getItemViewType(int position) {
        //Log.d(TAG, "getItemViewType 호출");
        if (position == 0) {
            //Log.d("DetailInfoAdapter", "TYPE_TOP");
            return TYPE_TOP;
        } else {
            //Log.d("DetailInfoAdapter", "TYPE_BOTTOM");
            return position;
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Log.d("DetailInfoAdapter", "뷰홀더 생성시작");
        if (viewType == TYPE_TOP) {
//            Log.d("DetailInfoAdapter", "TYPE_TOP 뷰홀더 생성");
//            View topView = inflate(parent.getContext(), R.layout.item_detail_info_top, null); //이방식으로 뷰홀더를 생성해버리면 뷰홀더가 가지는 폭,높이 속성을 컨트롤할 수 없음
            View topView = LayoutInflater.from(context).inflate(R.layout.item_detail_info_top, parent, false);
            return new TopViewHolder(topView);
        } else {
//            Log.d("DetailInfoAdapter", "TYPE_COMMENT 뷰홀더 생성");
//            View commentView = View.inflate(parent.getContext(), R.layout.item_detail_info_comment, null); //이방식으로 뷰홀더를 생성해버리면 뷰홀더가 가지는 폭,높이 속성을 컨트롤할 수 없음
            View commentView = LayoutInflater.from(context).inflate(R.layout.item_detail_info_comment, parent, false);
            return new CommentViewHolder(commentView);
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
//        Log.d("DetailInfoAdapter", "바인드 뷰홀더");
        if (holder instanceof TopViewHolder) { //TopViewHolder가 생성되어 있을 때
//            TopViewHolder topViewHolder = (TopViewHolder) holder;
//            topViewHolder.bindItem(); //상단에 들어가는 물품정보
        } else if (holder instanceof CommentViewHolder) { //CommentViewHolder가 생성되어 있을 때
            DetailInfoComment detailinfo = answer_data.get(position-1); //상단 뷰페이저를 생성하고 난 후의 포지션이기 때문에 -1하여 0번째부터 데이터를 넣기 위함

            CommentViewHolder commentViewHolder = (CommentViewHolder) holder;
            commentViewHolder.bindItem(detailinfo, commentViewHolder); //하단에 들어가는 댓글정보
        }
    }

    @Override
    public int getItemCount() {
        return answer_data.size()+1; // getItemCount의 리턴값이 홀더를 생성하는 횟수이기 때문에, 상단에 들어가는 뷰페어지 홀더를 추가로 생성하기위해 값을 1증가하여 한번 더 생성하게 함
    }

    public class CommentViewHolder extends RecyclerView.ViewHolder {
        @Bind(R.id.answer_reg_date)
        TextView answer_reg_date;
        @Bind(R.id.answer_reg_user)
        TextView answer_reg_user;
        @Bind(R.id.answer_cont)
        TextView answer_cont;
        @Bind(R.id.answer_reg_user_adres)
        TextView answer_reg_user_adres;
        @Bind(R.id.answer_reg_user_img)
        ImageView answer_reg_user_img;

        public CommentViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);

        }


        public void bindItem(DetailInfoComment detailinfo, CommentViewHolder commentViewHolder) {
//            Glide.with(context).load(detailinfo.answerRegUserImg).fitCenter().into(answer_reg_user_img);
//            Drawable drawable = ContextCompat.getDrawable(context, detailinfo.answerRegUserImg);

            commentViewHolder.answer_reg_user.setText(detailinfo.answerRegUser);
            commentViewHolder.answer_reg_date.setText(detailinfo.answerRegDate);
            commentViewHolder.answer_cont.setText(detailinfo.answerCont);
            commentViewHolder.answer_reg_user_adres.setText(detailinfo.answerRegUserAdres);
            commentViewHolder.answer_reg_user_img.setScaleType(ImageView.ScaleType.CENTER_CROP);
//            commentViewHolder.answer_reg_user_img.setBackground(drawable);
            commentViewHolder.answer_reg_user_img.setImageResource(detailinfo.answerRegUserImg);
        }

    }

    class TopViewHolder extends RecyclerView.ViewHolder {

        public TopViewHolder(View itemView) {
            super(itemView);
        }
    }
}

 

그다음은 뷰페이저 어댑터입니다.

public class DetailInfoViewPagerAdapter extends PagerAdapter {
    public String TAG = getClass().getSimpleName();
    LayoutInflater inflater;

    public DetailInfoViewPagerAdapter(LayoutInflater inflater) {
        // TODO Auto-generated constructor stub
        //전달 받은 LayoutInflater를 멤버변수로 전달
        this.inflater=inflater;
    }

    //PagerAdapter가 가지고 잇는 View의 개수를 리턴
    //보통 보여줘야하는 이미지 배열 데이터의 길이를 리턴
    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return 10; //이미지 개수 리턴(그림이 10개라서 10을 리턴)
    }

    //ViewPager가 현재 보여질 Item(View객체)를 생성할 필요가 있는 때 자동으로 호출
    //쉽게 말해, 스크롤을 통해 현재 보여져야 하는 View를 만들어냄.
    //첫번째 파라미터 : ViewPager
    //두번째 파라미터 : ViewPager가 보여줄 View의 위치(가장 처음부터 0,1,2,3...)
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        // TODO Auto-generated method stub
        View view=null;

        //새로운 View 객체를 Layoutinflater를 이용해서 생성
        //만들어질 View의 설계는 res폴더>>layout폴더>>viewpater_childview.xml 레이아웃 파일 사용
        view= inflater.inflate(R.layout.viewpager_childview, null);

        //만들어진 View안에 있는 ImageView 객체 참조
        //위에서 inflated 되어 만들어진 view로부터 findViewById()를 해야 하는 것에 주의.
        ImageView img= (ImageView)view.findViewById(R.id.img_viewpager_childimage);

        //ImageView에 현재 position 번째에 해당하는 이미지를 보여주기 위한 작업
        //현재 position에 해당하는 이미지를 setting
        img.setImageResource(R.drawable.sample1+position);
        img.setScaleType(ImageView.ScaleType.FIT_XY);

        //ViewPager에 만들어 낸 View 추가
        container.addView(view);

        //Image가 세팅된 View를 리턴
        return view;
    }

    //화면에 보이지 않은 View는파쾨를 해서 메모리를 관리함.
    //첫번째 파라미터 : ViewPager
    //두번째 파라미터 : 파괴될 View의 인덱스(가장 처음부터 0,1,2,3...)
    //세번째 파라미터 : 파괴될 객체(더 이상 보이지 않은 View 객체)
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        // TODO Auto-generated method stub
        //ViewPager에서 보이지 않는 View는 제거
        //세번째 파라미터가 View 객체 이지만 데이터 타입이 Object여서 형변환 실시
        container.removeView((View)object);
    }

    //instantiateItem() 메소드에서 리턴된 Ojbect가 View가  맞는지 확인하는 메소드
    @Override
    public boolean isViewFromObject(View v, Object obj) {
        // TODO Auto-generated method stub
        return v==obj;
    }

}

 

입문자입니당 (240 포인트) 님이 2017년 6월 15일 질문

1개의 답변

0 추천
PagerAdapter로 FragmentStatePagerAdapter를 사용해 보세요.. Fragment가 화면에 보이지 않아도 메모리에는 상주를 하기 때문에 발생하는 문제로 보입니다. FragmentStatePagerAdapter는 Fragment를 save하고 restore하는 과정으로 메모리 사용을 절약합니다.
mcsong (44,040 포인트) 님이 2017년 6월 16일 답변
...