(java, android) 리사이클러뷰 footer가 보이지 않습니다ㅜ

조회수 535회

이미지

위 사진은 예시의 사진입니다.

사진처럼 초기의 빈화면에 저런 footer가 있고 저런 버튼을 통해 점점 아이템을 추가해 가는

그런기능을 만들고 싶은데요

지금 footer가 아무리 해도 보이질 않습니다.. 여러 샘플을 보았지만 어디가 문제인지 잘모르겠습니다.

기존에 액티비티에서 버튼을 생성해서 기능을 다 구현했지만 마음이 바뀌어 footer로 바꾸려고

다시 하려는데 보이질 않습니다ㅠ사진처럼 footer 버튼을 눌러서 루틴을 추가하고 footer는

아이템의 마지막에 붙어서 계속 붙어 따라내려가는 그런 형태를 구현하려 합니다.

참고로 리사이클러뷰는 멀티타입 리사이클러뷰입니다(2개 타입)

루틴 아이템과 루틴 상세를 표현하는 아이템 두가지입니다.

중첩리사이클러뷰처럼 보일수 있으나 List 에 두가지 타입을 저장하여

리사이클러뷰하나 어댑터하나로 표현합니다.

전혀 상관없는 코드 같은 것은 일부는 지웠습니다.

더 필요하신 코드있으시면 말씀해주세요

RoutineAdapter.java

public class RoutineAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
    final static int TYPE_ROUTINE = 1;
    final static int TYPE_ROUTINE_DETAIL = 2;
    final static int TYPE_ROUTINE_FOOTER = 3;

    private Context context;
    private List<Object> mItems = new ArrayList<>();
    OnRoutineItemClickListener routinelistener;
    OnRoutineAddClickListener routineAddListener;

    public void updateRoutineList(List<Object> newRoutineList) {
        final RoutineDiffUtil diffCallback = new RoutineDiffUtil(this.mItems, newRoutineList);
        final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);

        this.mItems.clear();
        this.mItems.addAll(newRoutineList);
        diffResult.dispatchUpdatesTo(this);
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        context = parent.getContext();
//        View itemView;
        if(viewType == TYPE_ROUTINE) {
            View itemView = LayoutInflater.from(context).inflate(R.layout.routine_item, parent, false);
                return new RoutineViewHolder(itemView);
        }
        else if(viewType == TYPE_ROUTINE_DETAIL) {
            View itemView = LayoutInflater.from(context).inflate(R.layout.routine_detail_item, parent, false);
                return new RoutineDetailViewHolder(itemView);
        }
        else {
            View itemView = LayoutInflater.from(context).inflate(R.layout.routine_item_add, parent, false);
            return new RoutineAddFooterViewHolder(itemView);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        Object object = mItems.get(position);
        if(holder instanceof RoutineAddFooterViewHolder) {
            RoutineAddFooterViewHolder footerVH =  (RoutineAddFooterViewHolder) holder;
        }
        else if(object instanceof RoutineModel && holder instanceof RoutineViewHolder)
            setRoutineData((RoutineViewHolder) holder, (RoutineModel) object, position);
        else {
            RoutineDetailModel item = (RoutineDetailModel) object;
            ((RoutineDetailViewHolder) holder).setDetailItem(item);
        }
    }

    private void setRoutineData(RoutineViewHolder holder, RoutineModel routineItem, int position){
        holder.routine.setText(routineItem.getRoutine());

        holder.addSet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(routinelistener != null) routinelistener.OnAddBtnClick(position);
            }
        });

        holder.deleteSet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(routinelistener != null) routinelistener.OnDeleteBtnClick(position);
            }
        });
    }

    public Object getRoutineItem(int position) {
        if(mItems == null || position < 0 || position >= mItems.size())
            return null;
        return mItems.get(position);
    }

    @Override
    public int getItemCount() {
        if(mItems == null)
            return -1;
        return mItems.size();
    }

    @Override
    public int getItemViewType(int position) {
        Object obj = mItems.get(position);
        if(position == mItems.size())
            return TYPE_ROUTINE_FOOTER;
        else if(obj instanceof  RoutineModel)
            return TYPE_ROUTINE;
        else
            return TYPE_ROUTINE_DETAIL;
    }

    // 루틴 추가인터페이스
    public interface OnRoutineAddClickListener {
        public void OnAddRoutineClick();
    }

    public void setOnAddRoutineClickListener(OnRoutineAddClickListener listener) {
        this.routineAddListener = listener;
    }

    // 상세 추가/삭제 인터페이스
    public interface OnRoutineItemClickListener {
        public void OnAddBtnClick(int curRoutinePos);
        public void OnDeleteBtnClick(int curRoutinePos);
    }

    public void setOnRoutineClickListener(OnRoutineItemClickListener listener) {
        this.routinelistener = listener;
    }

    public class RoutineViewHolder extends RecyclerView.ViewHolder {
        public TextView routine;
        public Button addSet;
        public Button deleteSet;

        public RoutineViewHolder(@NonNull View itemView) {
            super(itemView);

            routine = itemView.findViewById(R.id.routine);
            addSet = itemView.findViewById(R.id.add_set);
            deleteSet = itemView.findViewById(R.id.delete_set);

        }
    }

    public class RoutineDetailViewHolder extends RecyclerView.ViewHolder {
        public TextView set;
        public TextView weight;

        public RoutineDetailViewHolder(@NonNull View itemView) {
            super(itemView);

            set = itemView.findViewById(R.id.set);
            weight = itemView.findViewById(R.id.weight);
        }

        public void setDetailItem(RoutineDetailModel item) {
            set.setText(item.getSet().toString() + "세트");
        }
    }

    // footer 뷰홀더
    public class RoutineAddFooterViewHolder extends RecyclerView.ViewHolder {
        TextView textView;
        public RoutineAddFooterViewHolder(@NonNull View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.add_text);
            ConstraintLayout regionForClick = itemView.findViewById(R.id.clickable_layout);
            regionForClick.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (routineAddListener != null) {
                        routineAddListener.OnAddRoutineClick();
                    }
                }
            });
        }

    }
}

MainActivity

public class WriteRoutineActivity extends AppCompatActivity {
    Button add_routine_btn;
    TextView title;
    RecyclerView routine_rv;

    LinearLayoutManager routineLayoutManger;
    RoutineAdapter routineAdapter;
    List<RoutineModel> items;
    List<String> titleData;

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

        items = new ArrayList<>();
        routineAdapter = new RoutineAdapter();
        routine_rv.setAdapter(routineAdapter);

        //루틴 추가
        routineAdapter.setOnAddRoutineClickListener(new RoutineAdapter.OnRoutineAddClickListener() {
            @Override
            public void OnAddRoutineClick() {
                WorkoutListDialogFragment routineDialog = new WorkoutListDialogFragment();
                routineDialog.show(getSupportFragmentManager(), "RoutineListDialog");
            }
        });

        // 루틴 상세 추가,삭제
        routineAdapter.setOnRoutineClickListener(new RoutineAdapter.OnRoutineItemClickListener() {
            @Override
            public void OnAddBtnClick(int routinePos) {
                Object obj = routineAdapter.getRoutineItem(routinePos);
                if(obj instanceof RoutineModel) {
                    RoutineModel item = (RoutineModel) obj;
                    item.addDetail(new RoutineDetailModel(((RoutineModel) obj).getDetailItemSize()));
                    routineAdapter.updateRoutineList(getDataToBeDisplayed());
//                    routineAdapter.updateData(getDataToBeDisplayed());
                }
                else
                    Toast.makeText(getApplicationContext(), "This is Wrong Type" ,Toast.LENGTH_SHORT).show();
            }
            @Override
            public void OnDeleteBtnClick(int routinePos) {
                Object item = routineAdapter.getRoutineItem(routinePos);
                if(item instanceof RoutineModel) {
                    RoutineModel routineModel = (RoutineModel) item;
                    if(routineModel.getDetailItemSize() > 1) {
                        try {
                           routineModel.removeDetails(routineModel.getDetailItemSize() - 1);
                        } catch (Exception e) {
                            e.printStackTrace();
                            Toast.makeText(getApplicationContext(), "잘못된 값입니다.", Toast.LENGTH_SHORT).show();
                        }
                    }
                    else { // 상세아이템이 한개 남았을때 루틴까지 삭제
                        items.remove(routineModel);
                    }
                    routineAdapter.updateRoutineList(getDataToBeDisplayed());
                }
            }
        });
    }

    public void addRoutine(String routine) {
        RoutineModel routineModel = new RoutineModel(routine);
        RoutineDetailModel routineDetailModel = new RoutineDetailModel();
        routineModel.addDetail(routineDetailModel);
        items.add(routineModel);
        routineAdapter.updateRoutineList(getDataToBeDisplayed());
    }

    // 보여질 데이터
    private List<Object> getDataToBeDisplayed() {
        List<Object> mixedList = new ArrayList<>();
        for(RoutineModel rm: items){
            mixedList.add(rm);
            if(rm.getDetailItemList() != null && rm.getDetailItemSize() > 0){
                for(RoutineDetailModel rmdetilas: rm.getDetailItemList()){
                    mixedList.add(rmdetilas);
                }
            }
        }
        return mixedList;
    }

}
  • footer 가 포함된 xml 파일도 올려주시면 좋을것같아요 김은기 2021.2.15 13:43

1 답변

  • 좋아요

    1

    싫어요
    채택 취소하기
    @Override
    public int getItemCount() {
        if(mItems == null)
            return -1;
        return mItems.size();
    }
    
    @Override
    public int getItemViewType(int position) {
        Object obj = mItems.get(position);
        if(position == mItems.size())
            return TYPE_ROUTINE_FOOTER;
        else if(obj instanceof  RoutineModel)
            return TYPE_ROUTINE;
        else
            return TYPE_ROUTINE_DETAIL;
    }
    

    items.size() 가 10일 경우 index (position) 은 0~9 까지 이므로 getItemViewType() 에서 if(position == mItems.size()) 조건이 타지 않을 것으로 보이네요.

    • (•́ ✖ •̀)
      알 수 없는 사용자
    • 답변감사합니다. 그런데 mitems 사이즈가 실행했을때 0이면 position도 0이라 처음에는 보여야하는것 아닌가요? codeslave 2021.2.19 04:08
    • mItems 사이즈가 0 이라면 getItemViewType() 은 호출 되지 않습니다 :) 또한 position 이 0 인 경우는 리스트 사이즈가 1일 때 입니다. footer 처리를 위해 여러 방법이 있겠으나 getItemCount() 에서 mItems.size() + 1 로 처리 하거나 mItems.size() 로 그대로두고 데이터셋에 footer 를 위한 더미 데이터를 추가 한 후 데이터셋의 object 로 footer 처리를 구분하는 방식도 있겠네요. 알 수 없는 사용자 2021.2.19 15:23
    • 아.. 아이템 사이즈가 아예 0이니 어댑터에서 데이터가 없다고 판단하고 뷰가 보이지를 않는거군요? 그래서 처음에 footer가 보이려면 말씀대로 getItemCount()에서 사이즈 +1해서 어댑터에서 데이터가 한개 존재하게 한다는거군요.. codeslave 2021.2.20 01:51
    • 네 그렇습니다 :) 알 수 없는 사용자 2021.2.23 11:56

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)