EMDI는 지금도 개발중

Android : RecyclerView 활용 데이터 추가 본문

네이티브/Android

Android : RecyclerView 활용 데이터 추가

EMDI 2020. 5. 6. 16:46

1. RecyclerView란?

ListView의 기능을 보완하기 위해서 만들어진 뷰. 기존의 ListView에서 레이아웃 매니저를 추가하여 리스트 타입을 쉽게 변경할 수 있습니다. 또한 ListView와 다르게 Viewholder의 사용이 필수적입니다.

 

2. RecyclerView활용하기

1) build.gradle (Module: app) 또는 activity_main.xml에서 recyclerView추가하기

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}

RecyclerView를 사용하기 위해서는 build.gradle에 implementation 'androidx.recyclerview:recyclerview:1.1.0'이 들어가 있어야 합니다. 위와 같이 dependencies에 직접 입력하셔도 되고, activity_main.xml 디자인에서 RecyclerView 다운로드 버튼을 클릭하면 자동으로 dependencies에 추가되니 둘 중 원하시는 방법으로 추가해주세요.

 

activity_main.xml에서 RecyclerView를 추가하는 방법은 res > layout > activity_main.xml 경로로 들어가신 다음 Palette - Common안에 있는 RecyclerView를 추가해주시면 됩니다. 

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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/rvCertList"
        android:layout_width="475dp"
        android:layout_height="245dp"
        android:layout_marginStart="1dp"
        android:layout_marginLeft="1dp"
        android:layout_marginTop="1dp"
        android:layout_marginEnd="1dp"
        android:layout_marginRight="1dp"
        android:layout_marginBottom="11dp" />

    <EditText
        android:id="@+id/txtPassword"
        android:layout_width="241dp"
        android:layout_height="wrap_content"
        android:layout_below="@+id/rvCertList"
        android:layout_marginStart="31dp"
        android:layout_marginLeft="31dp"
        android:layout_marginTop="22dp"
        android:layout_marginEnd="17dp"
        android:layout_marginRight="17dp"
        android:ems="10"
        android:inputType="textPassword" />

    <Button
        android:id="@+id/btnOK"
        android:layout_width="wrap_content"
        android:layout_height="62dp"
        android:layout_below="@+id/rvCertList"
        android:layout_marginStart="5dp"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="32dp"
        android:layout_marginEnd="29dp"
        android:layout_marginRight="29dp"
        android:layout_marginBottom="403dp"
        android:layout_toEndOf="@+id/txtPassword"
        android:layout_toRightOf="@+id/txtPassword"
        android:text="확인" />

</RelativeLayout>

위의 소스는 activity_main.xml의 소스입니다. 소스를 보시면 알 수 있듯이 RecyclerView뿐만 아니라 테스트를 하기 위해 필요한 Button과 TextBox도 생성하였습니다.

  • RecyclerView id : rvCertList
  • EditText id : txtPassword
  • Button id : btnOk

 

2) RecyclerView에 반복적으로 뿌릴 List! activity_list.xml 생성하기

 

activity_list.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content"
    tools:context=".MainActivity">


    <TextView
        android:id="@+id/certNm"
        android:layout_width="80dp"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:text="발급대상"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/certRegorg"
        android:layout_width="70dp"
        android:layout_height="wrap_content"
        android:layout_margin="3dp"
        android:layout_marginLeft="24dp"
        android:padding="5dp"
        android:text="발급자"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_weight="2"
        app:layout_constraintLeft_toRightOf="@id/certNm"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.004" />

    <TextView
        android:id="@+id/certType"
        android:layout_width="150dp"
        android:layout_height="wrap_content"
        android:layout_margin="3dp"
        android:layout_marginLeft="24dp"
        android:padding="5dp"
        android:text="구분"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_weight="3"
        app:layout_constraintLeft_toRightOf="@id/certRegorg"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.008" />

    <TextView
        android:id="@+id/certExpdate"
        android:layout_width="80dp"
        android:layout_height="wrap_content"
        android:layout_margin="3dp"
        android:layout_marginRight="24dp"
        android:padding="5dp"
        android:text="만료일자"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_weight="3"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

그 다음은 RecyclerView에 반복적으로 뿌려줄 아이템 레이아웃을 생성합니다. 보통 activity_main.xml은 프로젝트를 생성하면 기본적으로 갖춰져있는데 이 activity_list.xml은 제가 따로 생성한 파일입니다. 만드는 방법은 res > layout 폴더에 우클릭한다음 New > Layout resource file을 선택!

저는 이제 인증서목록을 RecyclerView에 뿌리는 테스트를 하고 싶어서 각 TextView의 이름들을 인증서 정보에 맞게 설정하였습니다.

 

3) MainActivity.java에 코드 추가

RecyclerView는 기본적으로 RecylcerView, Adapter, LayoutManager 총 3개로 구성되어있습니다. View와 View-data를 binding 해주는 Adapter, 그리고 그 배치 형태를 잡아주는 LayoutManager.

MainActivity.java

public class MainActivity extends AppCompatActivity {
 
    RecyclerView recyclerView;
    RecyclerAdapter adapter;
    RecyclerView.LayoutManager layoutManager;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

우선 위의 소스와 같이 3개를 추가해줍니다. 아마 다른것과 다르게 RecyclerAdapter는 빨간줄이 뜰겁니다. 이 오류는 나중에 사라지는 오류이니 너무 신경쓰지 마세요.

제가 만들려고 하는 결과물입니다. 보시다시피 Row에 있는 하나 하나가 RecyclerView에 해당하는 아이템입니다. 이 아이템에 관련된 클래스의 이름은 저는 ListActivity로 지정하였습니다. 

 

ListActivity.java

package com.myapp.certlist;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class ListActivity {

    public String path ;
    public String cn1;
    public String cn;
    public String ou;
    public String o;
    public String c;
    public String date;

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getCn1() {
        return cn1;
    }

    public void setCn1(String cn1) {
        this.cn1 = cn1;
    }

    public String getCn() {
        return cn;
    }

    public void setCn(String cn) {
        this.cn = cn;
    }

    public String getOu() {
        return ou;
    }

    public void setOu(String ou) {
        this.ou = ou;
    }

    public String getO() {
        return o;
    }

    public void setO(String o) {
        this.o = o;
    }

    public String getC() {
        return c;
    }

    public void setC(String c) {
        this.c = c;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public ListActivity(String certName, String certReg, String certType, String certExpDate) {
        this.cn = certName;
        this.o = certReg;
        this.ou = certType;
        this.date = certExpDate;

    }
    
    @Override
    public String toString() {
        return path;
    }

}

아이템의 클래스도 생성하였으니 이제 다시 MainActivity로 돌아가서 해당 ListActivity와 연관된 리스트를 만들어줍니다.

 

MainActivity.java

public class MainActivity extends AppCompatActivity {
 
    // 추가사항
    private ArrayList<ListActivity> listBundle = new ArrayList<>();
    
    private RecyclerView recyclerView;
    private RecyclerAdapter adapter;
    private RecyclerView.LayoutManager layoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
 	//추가사항
        recyclerView = (RecyclerView) findViewById(R.id.rvCertList);
        recyclerView.setHasFixedSize(true);
        
        layoutManager = new LinearLayoutManager(this);
        ((LinearLayoutManager) layoutManager).setReverseLayout(true);
        ((LinearLayoutManager) layoutManager).setStackFromEnd(true);
        
        recyclerView.setLayoutManager(layoutManager);
        adapter = new RecyclerAdapter();
        recyclerView.setAdapter(adapter);
    }
}

 

4) RecyclerView Adapter 생성하기

그 다음은 RecylcerAdapter를 생성해보도록 하겠습니다. adapter의 파일이름은 RecyclerAdapter로, 아까 MainActivity에 미리 지정했던 빨간줄 뜨는 adapter변수의 클래스명으로 지정해야합니다.

 

public class ViewHolder extends RecyclerView.ViewHolder{
    public ViewHolder(@NonNull View view) {
        super(view);
    }
}

우선 public class ViewHolder extends RecyclerView.ViewHolder{ 를 입력하고 Alt + Enter를 누르면 Create constructor matching super라는 알림이 뜨는데 해당 constructor를 클릭해서 위의 소스처럼 생성자를 만들어주세요.

 

그 다음에는 방금 만들었던 ViewHolder를 상위 클래스에 extends해주세요. 만약 위와 같이 소스를 추가했으면 빨간줄이 뜨는데 Alt + Enter를 누르시면 이번에는 implement method를 생성하라는 문구가 뜹니다. 추가해주세요.

 

추가한 내용은 onCtreateViewHolder(), onBindViewHolder(), getItemCount() 총 3개입니다.

onCtreateViewHolder()는 RecyclerAdapter와 연결하는 RecyclerView에 추가할 item의 레이아웃과 item Data를 Bind하는거에 쓰입니다. onBindViewHolder()는 RecyclerView 자체와 item 데이터셋을 서로 연결시켜주는 과정을 의미합니다. getItemCount()은 데이터셋의 데이터개수를 의미합니다.

RecyclerAdapter.java

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder>{

    @NonNull
    @Override
    // adapter와 연결하는 recyclerView에 추가할 item레이아웃과 item Data를 bind함
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    // recyclerView 자체와 item 데이터셋을 서로 연결해주는 과정
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

    }

    @Override
    // 데이터셋의 데이터 개수이다.
    public int getItemCount() {
        return 0;
    }
    
    public class ViewHolder extends RecyclerView.ViewHolder{
        public ViewHolder(@NonNull View view) {
            super(view);
        }
    }
}

여기까지 오셨으면 위의 소스와 같이 만들어진 모습을 보실 수 있습니다. 이제는 ListActivity에 추가한 데이터셋을 연결해야합니다.

 

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder>{
    
    // 생성자를 통해 액티비티의 데이터셋과 해당 데이터셋을 얕은 복사를 통해 binding 해준다.
    ArrayList<ListActivity> listBundle = new ArrayList<>();
    Context mContext;

    public RecyclerAdapter(ArrayList<ListActivity> bundle){
        this.listBundle = bundle;
    }
    
    ... 여태까지 한 내용

RecyclerAdapter 클래스 안에 생성자를 만들어서 Bind. 그 외 나중에 필요한 Context도 미리 선언을 해두었습니다.

@NonNull
@Override
// adapter와 연결하는 recyclerView에 추가할 item레이아웃과 item Data를 bind함
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

    Context mContext  = parent.getContext();
    LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View view = inflater.inflate(R.layout.activity_list, parent, false);
    ViewHolder holder = new ViewHolder(view);

return holder;
}

@Override
// 데이터셋의 데이터 개수이다.
public int getItemCount() {
  	return listBundle.size();
}

그 다음은 onCreateViewHolder()에 위와 같이 써줍니다. R.layout.아까만든데이터셋아이템레이아웃 

 

5) Item Layout 완성하기

activity_list.xml

다시 activity_list.xml로 가서 아무것도 설정하지 않았던 빈 View에 본인이 넣을 TextView를 추가로 합니다. 저같은 경우에는 인증서정보를 보여줄 것이라 발급대상, 발급자, 구분, 만료일자 TextView를 생성하였습니다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content"
    tools:context=".MainActivity">


    <TextView
        android:id="@+id/certNm"
        android:layout_width="80dp"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:text="발급대상"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/certRegorg"
        android:layout_width="70dp"
        android:layout_height="wrap_content"
        android:layout_margin="3dp"
        android:layout_marginLeft="24dp"
        android:padding="5dp"
        android:text="발급자"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_weight="2"
        app:layout_constraintLeft_toRightOf="@id/certNm"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.004" />

    <TextView
        android:id="@+id/certType"
        android:layout_width="150dp"
        android:layout_height="wrap_content"
        android:layout_margin="3dp"
        android:layout_marginLeft="24dp"
        android:padding="5dp"
        android:text="구분"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_weight="3"
        app:layout_constraintLeft_toRightOf="@id/certRegorg"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.008" />

    <TextView
        android:id="@+id/certExpdate"
        android:layout_width="80dp"
        android:layout_height="wrap_content"
        android:layout_margin="3dp"
        android:layout_marginRight="24dp"
        android:padding="5dp"
        android:text="만료일자"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_weight="3"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

6) RecyclerView Adapter 마무리짓기

activity_list.xml에 생성하였던 TextView를 이제 연결짓도록 합시다. 다시 RecyclerAdapter.java로 돌아와서 레이아웃에 추가했던 item들을 소스에 추가해주세요.

RecyclerAdapter.java

public class ViewHolder extends RecyclerView.ViewHolder{

    TextView certNameView;
    TextView certRegView;
    TextView certTypeView;
    TextView certExpDateView;

    public ViewHolder(@NonNull View view) {
        super(view);
        certNameView = view.findViewById(R.id.certNm);
        certRegView = view.findViewById(R.id.certRegorg);
        certTypeView = view.findViewById(R.id.certType);
        certExpDateView = view.findViewById(R.id.certExpdate);
    }
}

@Override
// recyclerView 자체와 item 데이터셋을 서로 연결해주는 과정
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

    ListActivity list = listBundle.get(position);
    holder.certNameView.setText(list.getCn());
    holder.certRegView.setText(list.getO());
    holder.certTypeView.setText(list.getOu());
    holder.certExpDateView.setText(list.getDate());

}

 

7) MainActivity 클래스 완성시키기

이제 RecylcerAdapter를 활용할 수 있게되었습니다. 아까 MainActivity에서 생성만 했던 RecyclerAdapter를 바꿔주도록 합시다.

private RecyclerAdapter adapter = new RecyclerAdapter(listBundle);

@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    recyclerView = (RecyclerView) findViewById(R.id.rvCertList);
    recyclerView.setHasFixedSize(true);

    layoutManager = new LinearLayoutManager(this);
    ((LinearLayoutManager) layoutManager).setReverseLayout(true);
    ((LinearLayoutManager) layoutManager).setStackFromEnd(true);

    recyclerView.setLayoutManager(layoutManager);
    adapter = new RecyclerAdapter(listBundle);
    recyclerView.setAdapter(adapter);

    //------------------------------------------------------------
    // 인증서 목록 뿌리기
    final Collection<? extends ListActivity> collection = getCerts();
    if(collection == null || collection.size() == 0) return;

    for (int i=0; i<collection.size(); i++)
    {
        listBundle.add(((ListActivity) ((ArrayList) collection).get(i)));
        adapter.notifyDataSetChanged();
    }
    layoutManager.scrollToPosition(listBundle.size()-1);

    //------------------------------------------------------------

    final Button btnOk = (Button) findViewById(R.id.btnOK);
    btnOk.setOnClickListener(new View.OnClickListener() {

    	@RequiresApi(api = Build.VERSION_CODES.KITKAT)
        @Override
        public void onClick(View v) {

            count++;

        }
    });
}

저는 버튼은 다른 용도로 사용할 예정이라 그냥 화면을 로드할 때 바로 인증서목록이 뿌려지는 것으로 설정했습니다. 이렇게 하니 정상적으로 목록이 보이는걸 확인할 수 있네요!!

for (int i=0; i<collection.size(); i++)
{
    listBundle.add(((ListActivity) ((ArrayList) collection).get(i)));
    adapter.notifyDataSetChanged();
}

단, 제 소스는 100% 공개한 소스가 아닌 인증서정보를 가져오는 소스는 제외한 글입니다. 만약 정상적으로 연결되는지 테스트가 하고싶은 분들은 이 부분을 수정하시면 정상적으로 뜨실겁니다.

제가 참고로 많이 도움이 되었던 블로그의 링크고 첨부하겠습니다. 사실 이 블로그의 내용을 토대로 만든거라 제가 작성한글도 매우 유사할겁니다.

 

Android RecyclerView에 대한 모든 것

안드로이드에서는 똑같은 class형태의 객체들을 나열하는 데에 RecyclerView라는 형식을 많이 사용한다. ListView와 같은 기존 방식보다는 효율적이고 빠르다고 할 수 있다. 쉽게 스크롤할 수 있으며, �

shyunku.tistory.com

 

Comments