안드로이드 RecyclerView, 안드로이드 CardView 예제 튜토리얼

안드로이드 RecyclerView안드로이드 CardView안드로이드 롤리팝에서 소개되었으며 머티리얼 디자인을 채택했습니다. 머티리얼 디자인은 안드로이드 5.0부터 소개된 UI 위젯의 포괄적인 가이드이며 앱의 시각적인 매력을 높입니다.

안드로이드 RecyclerView

안드로이드 RecyclerView는 ListView의 더욱 고급화된, 강력하고 유연한 버전입니다. 안드로이드 RecyclerView는 ListView와 유사하지만 ListView에서는 강제로 RecyclerView.ViewHolder 클래스를 사용하여 요소를 보관해야 하는 반면, 안드로이드 RecyclerView에서는 그러한 강제성이 없습니다. 이름에서 알 수 있듯이 안드로이드 RecyclerView는 스크롤을 위해 셀을 재사용하여 목록의 항목을 리사이클합니다. RecyclerView의 또 다른 개선점은 ListView와 달리 레이아웃 매니저를 런타임에서 동적으로 설정할 수 있다는 것입니다. RecyclerView는 다음과 같은 유형의 레이아웃을 런타임에서 설정할 수 있습니다.

  • LinearLayoutManager: 수직 및 수평 목록을 모두 지원합니다.
  • StaggeredLayoutManager : 이는 스태거드 리스트를 지원합니다.
  • GridLayoutManager : 이는 갤러리뷰에서 볼 수 있는 그리드를 표시하는 데에 사용됩니다.

안드로이드 RecyclerView 클래스

  • RecyclerView.ItemAnimator 클래스는 ListView와는 다르게 뷰를 애니메이션화하는 데에 더 나은 지원을 제공합니다.
  • RecyclerView.ItemDecorator 클래스는 테두리와 구분선을 추가하는 데에 더 나은 지원을 제공하여 우리에게 큰 제어력을 부여합니다.

따라서 RecyclerView는 ListView와 비교할 때 더 많은 사용자 정의가 가능하며 사용자에게 더 큰 제어력을 제공합니다. RecyclerView는 support 라이브러리에 포함되어 있습니다. 따라서 다음 종속성을 추가하기 위해 gradle 스크립트를 수정해야 합니다.

dependencies {
       compile 'com.android.support:recyclerview-v7:21.0.0-rc1'
 }

안드로이드 CardView

안드로이드 CardView UI 컴포넌트는 카드 안에 정보를 표시합니다. 이 컴포넌트는 일반적으로 연락처 정보를 표시하는 데에 사용됩니다. 이 컴포넌트는 다른 support 라이브러리에 포함되어 있으므로 해당 종속성도 추가해야 합니다.

dependencies {
        compile 'com.android.support:cardview-v7:21.0.0-rc1'
        compile 'com.android.support:recyclerview-v7:21.0.0-rc1'
 }

Android CardView 위젯을 사용하면 배경색, 그림자, 모서리 반지름, 표고 등을 제어할 수 있습니다. XML에서 사용자 정의 속성을 사용하려면 부모 레이아웃에 다음과 같은 네임스페이스 선언을 추가해야 합니다. 다음은 프로젝트에서 일부 속성을 포함한 네임스페이스 선언입니다.

<android.support.v7.widget.CardView
        android:id="@+id/card_view"
        xmlns:card_view="https://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardBackgroundColor="@color/grey_300"
        card_view:cardCornerRadius="10dp"
        card_view:cardElevation="5dp"
        card_view:cardUseCompatPadding="true">

위에서 사용된 중요한 속성은 다음과 같습니다:

  • card_view:cardCornerRadius : 레이아웃에서 모서리 반지름을 설정하는 데 사용됩니다.
  • card_view:cardBackgroundColor : 뷰의 배경색을 설정하는 데 사용됩니다.

예제 프로젝트에서는 Android 버전 이름과 번호와 샘플 로고가 포함된 CardView 목록을 표시하기 위해 RecyclerView를 추가합니다. CardView의 onclick은 해당 Card를 목록에서 제거하는 방식으로 프로그래밍되어 있습니다. 우리는 ActionBar에 제거한 카드를 순서대로 다시 추가하는 메뉴 옵션을 추가했습니다. 참고: 로고 이미지는 Google에서 무작위로 가져온 것이므로 크기가 다를 수 있습니다.

Android RecyclerView와 CardView 예제

프로젝트는 MainActivity를 포함하고 있으며 RecyclerView를 표시합니다. CardView는 CustomAdapter 클래스에서 RecyclerView에 추가됩니다. DataModel은 각 CardView에 대한 데이터를 가져오기 위해 getter를 사용합니다. MyData 클래스는 텍스트 뷰와 드로어블의 배열과 ID를 보유합니다.

Android RecyclerView와 CardView 예제 코드

activity_main.xml은 아래와 같이 RecyclerView를 RelativeLayout 내에 보유합니다. activity_main.xml 코드:

<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity"
    android:background="@color/grey_300"
    >

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        />

</RelativeLayout>

Android CardView 레이아웃은 아래와 같이 정의됩니다. cards_layout.xml 코드:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:tag="cards main container">

    <android.support.v7.widget.CardView
        android:id="@+id/card_view"
        xmlns:card_view="https://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardBackgroundColor="@color/color_white"
        card_view:cardCornerRadius="10dp"
        card_view:cardElevation="5dp"
        card_view:cardUseCompatPadding="true">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            >

            <ImageView
                android:id="@+id/imageView"
                android:tag="image_tag"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_margin="5dp"
                android:layout_weight="1"
                android:src="@drawable/ic_launcher"/>

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="12dp"
                android:layout_weight="2"
                android:orientation="vertical"
                >

                <TextView
                    android:id="@+id/textViewName"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="10dp"
                    android:text="Android Name"
                    android:textAppearance="?android:attr/textAppearanceLarge"/>

                <TextView
                    android:id="@+id/textViewVersion"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="10dp"

                    android:text="Android Version"
                    android:textAppearance="?android:attr/textAppearanceMedium"/>

            </LinearLayout>
        </LinearLayout>

    </android.support.v7.widget.CardView>

</LinearLayout>

Android CardView는 중첩된 선형 레이아웃에서 ImageView와 두 개의 TextView를 보유합니다. menu_main.xml은 제거된 카드를 다시 추가하기 위한 단일 항목을 포함합니다. menu_main.xml 코드:

<menu xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    tools:context=".MainActivity">
    <item android:id="@+id/add_item"
        android:title="Add"
        android:orderInCategory="100"
        app:showAsAction="always"/>
</menu>

MainActivity.java 클래스는 아래와 같이 정의됩니다:

package com.journaldev.recyclerviewcardview;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private static RecyclerView.Adapter adapter;
    private RecyclerView.LayoutManager layoutManager;
    private static RecyclerView recyclerView;
    private static ArrayList data;
    static View.OnClickListener myOnClickListener;
    private static ArrayList removedItems;

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

        myOnClickListener = new MyOnClickListener(this);

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

        layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());

        data = new ArrayList();
        for (int i = 0; i < MyData.nameArray.length; i++) {
            data.add(new DataModel(
                    MyData.nameArray[i],
                    MyData.versionArray[i],
                    MyData.id_[i],
                    MyData.drawableArray[i]
            ));
        }

        removedItems = new ArrayList();

        adapter = new CustomAdapter(data);
        recyclerView.setAdapter(adapter);
    }


    private static class MyOnClickListener implements View.OnClickListener {

        private final Context context;

        private MyOnClickListener(Context context) {
            this.context = context;
        }

        @Override
        public void onClick(View v) {
            removeItem(v);
        }

        private void removeItem(View v) {
            int selectedItemPosition = recyclerView.getChildPosition(v);
            RecyclerView.ViewHolder viewHolder
                    = recyclerView.findViewHolderForPosition(selectedItemPosition);
            TextView textViewName
                    = (TextView) viewHolder.itemView.findViewById(R.id.textViewName);
            String selectedName = (String) textViewName.getText();
            int selectedItemId = -1;
            for (int i = 0; i < MyData.nameArray.length; i++) {
                if (selectedName.equals(MyData.nameArray[i])) {
                    selectedItemId = MyData.id_[i];
                }
            }
            removedItems.add(selectedItemId);
            data.remove(selectedItemPosition);
            adapter.notifyItemRemoved(selectedItemPosition);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        if (item.getItemId() == R.id.add_item) {
           //추가할 항목이 있는지 확인하기
            if (removedItems.size() != 0) {
                addRemovedItemToList();
            } else {
                Toast.makeText(this, "Nothing to add", Toast.LENGTH_SHORT).show();
            }
        }
        return true;
    }

    private void addRemovedItemToList() {
        int addItemAtListPosition = 3;
        data.add(addItemAtListPosition, new DataModel(
                MyData.nameArray[removedItems.get(0)],
                MyData.versionArray[removedItems.get(0)],
                MyData.id_[removedItems.get(0)],
                MyData.drawableArray[removedItems.get(0)]
        ));
        adapter.notifyItemInserted(addItemAtListPosition);
        removedItems.remove(0);
    }
}

removeItems() 메서드는 리스너 메서드에서 클릭된 CardView를 제거하기 위해 호출됩니다. 해당 id는 나중에 검색하기 위해 배열에 저장됩니다. 나중에 view를 추가하기 위해 addRemovedItemToList()라는 다른 메서드를 구현했습니다. 이 메서드에서는 그 view를 목록의 미리 정의된 위치에 추가하고 removedItems 배열에서 해당 id를 제거합니다. CustomAdapter는 두 경우 모두에 대해 알립니다. CustomeAdapter.java 클래스는 아래에 정의되어 있습니다 :

package com.journaldev.recyclerviewcardview;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.ArrayList;

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> {

    private ArrayList<DataModel> dataSet;

    public static class MyViewHolder extends RecyclerView.ViewHolder {

        TextView textViewName;
        TextView textViewVersion;
        ImageView imageViewIcon;

        public MyViewHolder(View itemView) {
            super(itemView);
            this.textViewName = (TextView) itemView.findViewById(R.id.textViewName);
            this.textViewVersion = (TextView) itemView.findViewById(R.id.textViewVersion);
            this.imageViewIcon = (ImageView) itemView.findViewById(R.id.imageView);
        }
    }

    public CustomAdapter(ArrayList<DataModel> data) {
        this.dataSet = data;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent,
                                           int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.cards_layout, parent, false);

        view.setOnClickListener(MainActivity.myOnClickListener);

        MyViewHolder myViewHolder = new MyViewHolder(view);
        return myViewHolder;
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int listPosition) {

        TextView textViewName = holder.textViewName;
        TextView textViewVersion = holder.textViewVersion;
        ImageView imageView = holder.imageViewIcon;

        textViewName.setText(dataSet.get(listPosition).getName());
        textViewVersion.setText(dataSet.get(listPosition).getVersion());
        imageView.setImageResource(dataSet.get(listPosition).getImage());
    }

    @Override
    public int getItemCount() {
        return dataSet.size();
    }
}

위의 코드에서는 RecyclerView.ViewHolder를 확장하여 고유한 ViewHolder를 구현했습니다. view는 레이아웃 디렉토리에 정의한 cards_layout.xml에서 인플레이션됩니다. MainActivity의 onClickListener는 아래 스니펫에서 이 view에 연결됩니다.

view.setOnClickListener(MainActivity.myOnClickListener);

ArrayList에는 DataModel 클래스 객체 형태로 모든 데이터가 저장되고 해당 카드에 대해 목록에 추가됩니다. 이 애플리케이션에 특정한 데이터를 포함하는 DataModel.javaMyData.java 클래스는 아래에 나와 있습니다 :

package com.journaldev.recyclerviewcardview;

public class DataModel {

    String name;
    String version;
    int id_;
    int image;

    public DataModel(String name, String version, int id_, int image) {
        this.name = name;
        this.version = version;
        this.id_ = id_;
        this.image=image;
    }

    public String getName() {
        return name;
    }

    public String getVersion() {
        return version;
    }

    public int getImage() {
        return image;
    }

    public int getId() {
        return id_;
    }
}
package com.journaldev.recyclerviewcardview;

public class MyData {

    static String[] nameArray = {"Cupcake", "Donut", "Eclair", "Froyo", "Gingerbread", "Honeycomb", "Ice Cream Sandwich","JellyBean", "Kitkat", "Lollipop", "Marshmallow"};
    static String[] versionArray = {"1.5", "1.6", "2.0-2.1", "2.2-2.2.3", "2.3-2.3.7", "3.0-3.2.6", "4.0-4.0.4", "4.1-4.3.1", "4.4-4.4.4", "5.0-5.1.1","6.0-6.0.1"};

    static Integer[] drawableArray = {R.drawable.cupcake, R.drawable.donut, R.drawable.eclair,
            R.drawable.froyo, R.drawable.gingerbread, R.drawable.honeycomb, R.drawable.ics,
            R.drawable.jellybean, R.drawable.kitkat, R.drawable.lollipop,R.drawable.marsh};

    static Integer[] id_ = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
}

아래는 우리의 안드로이드 RecyclerView 및 CardView 예제 애플리케이션에 의해 생성된 출력입니다. 제거된 항목이 항상 세 번째 인덱스 (목록의 네 번째 위치)에 추가되는 것을 볼 수 있습니다. 이로써 안드로이드 RecyclerView 및 CardView에 대한 자습서를 마칩니다. 아래 링크에서 Android RecyclerView CardView 예제 프로젝트를 다운로드할 수 있습니다.

Android RecyclerView CardView 예제 프로젝트 다운로드

Source:
https://www.digitalocean.com/community/tutorials/android-recyclerview-android-cardview-example-tutorial