Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
Tags
- backgroundTint
- RestAPI
- 리눅스
- prolificinteractive/material-calendarview
- livedata
- DialogFragment
- viewmodel
- cocoapod
- editText
- PostgreSQL
- Dialog
- calendar
- Button
- addTextChangedListner
- android
- springboot
- podinit
Archives
- Today
- Total
코코딩딩
[안드로이드/android] recyclerview 접기,펼치기(아코디언) 본문
고객센터 기능을 하는 뷰를 구현하기 위해 다른 어플을 확인한 결과 카카오톡의 공지사항과 같은 뷰를 구현 하기로 결정하였다. recyclerview 형태의 리스트에 제목과 날짜가 출력되어 있고 각 리스트를 클릭하면 해당 리스트에 맞는 내용이 펼쳐지면서 출력되는 형태로 구현하고자 한다.
item.xml 만들기
예전해 했던 것과 마찬가지로 recyclerview의 각 리스트에 해당하는 레이아웃을 만드는데 이번에는 펼친 후 표시될 레이아웃 까지 한번에 만들어준다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/linearlayout"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="15dp"
android:background="@drawable/bg_round"
android:orientation="vertical">
<TextView
android:id="@+id/qna_insert_date"
android:layout_width="match_parent"
android:layout_height="29dp"
android:text="2022.04.29 10:09"
android:textSize="16sp"
/>
<GridLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/qna_title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="문의내역의 제목이 표시됨"
android:textColor="@color/black"
android:textSize="16sp" />
</GridLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/qna_detail_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:visibility="visible"
android:background="@drawable/bg_round"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="29dp"
android:textColor="@color/black"
android:text="문의내용"
android:textSize="16sp"
/>
<TextView
android:id="@+id/question_content_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="유저가 입력한 문의 내용 들어가는 곳"
android:textSize="14sp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="29dp"
android:textColor="@color/black"
android:text="답변내용"
android:textSize="16sp"
/>
<TextView
android:id="@+id/answer_content_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="답변 내용이 들어가는 곳"
android:textSize="14sp"
/>
</LinearLayout>
</LinearLayout>
위와 같은 코드로 레이아웃을 구성하면 아래와 같이 나오고 처음에는 접힌 상태가 되어야 하므로 visibility="gone" 으로 변경해준다.
DTO 만들기
이전과는 다르게 리스트에 들어갈 각 item을 dto로 관리하기 위해 dto를 만들어준다.
package com.example.recycletest;
public class ItemDTO {
String title;
String date;
String qContent;
String aContent;
public ItemDTO(String title, String date, String qContent, String aContent) {
this.title = title;
this.date = date;
this.qContent = qContent;
this.aContent = aContent;
}
@Override
public String toString() {
return "ItemDTO{" +
"title='" + title + '\'' +
", date='" + date + '\'' +
", qContent='" + qContent + '\'' +
", aContent='" + aContent + '\'' +
'}';
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getqContent() {
return qContent;
}
public void setqContent(String qContent) {
this.qContent = qContent;
}
public String getaContent() {
return aContent;
}
public void setaContent(String aContent) {
this.aContent = aContent;
}
}
clickListener 만들기
자바 클래스를 새로 만들어 interface로 listener를 만들어준다.
public interface OnViewHolderItemClickListener {
void onViewHolderItemClick();
}
adapter 만들기
이전에 했던 예제 에서는 adapter와 viewholder가 한 클래스에 있었지만 이번에는 따로 분리가 되었다.
import android.annotation.SuppressLint;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class QnaRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
// item 리스트 array
private ArrayList<ItemDTO> listData = new ArrayList<>();
// item의 클릭 상태 저장 array
private SparseBooleanArray selectedItems = new SparseBooleanArray();
// 직전에 클릭됐던 item의 position
private int prePosition = -1;
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//자신이 만든 리스트의 layout 설정
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_qna_list, parent, false);
//뷰홀더
return new QnaViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, @SuppressLint("RecyclerView") final int position) {
QnaViewHolder qnaViewHolder = (QnaViewHolder)holder;
qnaViewHolder.onBind(listData.get(position),position, selectedItems);
//클릭리스너
qnaViewHolder.setOnViewHolderItemClickListener(new OnViewHolderItemClickListener() {
@Override
public void onViewHolderItemClick() {
if (selectedItems.get(position)) {
// 펼쳐진 Item을 클릭 시
selectedItems.delete(position);
} else {
// 직전의 클릭됐던 Item의 클릭상태를 지움
selectedItems.delete(prePosition);
// 클릭한 Item의 position을 저장
selectedItems.put(position, true);
}
// 해당 포지션의 변화를 알림
if (prePosition != -1) notifyItemChanged(prePosition);
notifyItemChanged(position);
// 클릭된 position 저장
prePosition = position;
}
});
}
@Override
public int getItemCount() {
return listData.size();
}
// dto의 값들을 array에 추가해주는 메서드
void addItem(ItemDTO itemData) {
listData.add(itemData);
}
}
ViewHolder 만들기
package com.example.recycletest;
import android.animation.ValueAnimator;
import android.util.SparseBooleanArray;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
public class QnaViewHolder extends RecyclerView.ViewHolder {
// 자신이 만든 레이아웃 요소들 선언
TextView date;
TextView title;
TextView qContent;
TextView aContent;
//레이아웃 전체
LinearLayout linearlayout;
// 클릭하면 나올 숨겨진 레이아웃
LinearLayout detailLayout;
OnViewHolderItemClickListener onViewHolderItemClickListener;
public QnaViewHolder(@NonNull View itemView) {
super(itemView);
date = itemView.findViewById(R.id.qna_insert_date);
qContent = itemView.findViewById(R.id.question_content_tv);
aContent = itemView.findViewById(R.id.answer_content_tv);
title = itemView.findViewById(R.id.qna_title_tv);
detailLayout = itemView.findViewById(R.id.qna_detail_layout);
linearlayout = itemView.findViewById(R.id.linearlayout);
//레이아웃 전체를 클릭시 리스너 호출
linearlayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onViewHolderItemClickListener.onViewHolderItemClick();
}
});
}
public void onBind(ItemDTO itemData, int position, SparseBooleanArray selectedItems){
//dto에 있는 값을 뷰에 set
title.setText(itemData.getTitle());
date.setText(itemData.getDate());
qContent.setText(itemData.getqContent());
aContent.setText(itemData.getaContent());
changeVisibility(selectedItems.get(position));
}
private void changeVisibility(final boolean isExpanded) {
// ValueAnimator.ofInt(int... values)는 View가 변할 값을 지정, 인자는 int 배열
ValueAnimator va = isExpanded ? ValueAnimator.ofInt(0, 600) : ValueAnimator.ofInt(600, 0);
// Animation이 실행되는 시간, n/1000초
va.setDuration(500);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// imageView의 높이 변경
detailLayout.getLayoutParams().height = (int) animation.getAnimatedValue();
detailLayout.requestLayout();
// imageView가 실제로 사라지게하는 부분
detailLayout.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
}
});
// Animation start
va.start();
}
public void setOnViewHolderItemClickListener(OnViewHolderItemClickListener onViewHolderItemClickListener) {
this.onViewHolderItemClickListener = onViewHolderItemClickListener;
}
}
MainActivity
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
QnaRecyclerAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
addDTO();
}
private void init(){
RecyclerView recyclerView = findViewById(R.id.recyclerView);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
adapter = new QnaRecyclerAdapter();
recyclerView.setAdapter(adapter);
}
//임의로 dto 10개 생성해서 adapter에 있는 array에 추가
private void addDTO(){
for(int i=0;i<10;i++){
ItemDTO itemData = new ItemDTO("문의내용", "2022/04/29 14:00","asd","asdasd");
adapter.addItem(itemData);
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
화면
자신이 원하는 형태로 변환 하려면 item 레이아웃을 변환하고 레이아웃요소들의 id를 ViewHolder에 알맞게 선언해주어야 한다.
코드 출처
'일단기록 > 매일기록' 카테고리의 다른 글
[안드로이드 / android] 달력 라이브러리( prolificinteractive/material-calendarview ) (0) | 2022.05.03 |
---|---|
[스프링/spring] restapi 실수한 부분 정리 (0) | 2022.05.02 |
[안드로이드/android] recyclerView (0) | 2022.04.28 |
[안드로이드/android] 카카오api 카카오링크(v2-link) 메세지 전송 (1) | 2022.04.27 |
[안드로이드/android] customdialog, timepicker (0) | 2022.04.26 |