Android’de RecyclerView ve CardView Kullanımı

Android’de RecyclerView ve CardView Kullanımı

Herkese Merhaba. Bu yazıda bir android projesi içerisinde RecyclerView ve CardView toollarının kullanımını anlatmaya çalışacağım.

CardView dediğimiz tool, aslında günlük hayatta kullandığımız neredeyse tüm uygulamaların içerisinde bulunan bir view. CardView aracılığıyla uygulama içerisinde kart şeklinde dizaynlar oluşturup bunları listeleyebiliriz.

RecyclerView toolu ise oluşturduğumuz CardView’i elimizdeki veri dizaynına ve sayısına göre alt alta, yan yana ya da daha farklı şekillerde dizmemizi sağlar. Bu araçları kullanmaya başladığım ilk zamanlarda o kadar beğenmiştim ki, resmen “vovovovovovovov” demiştim diyebilirim. Ve bu nedenle de bu yazıyı yazma ihtiyacı hissettim. Hazırsak başlayalım…

Her şeyden önce projemizin app düzeyindeki gradle dosyasına aşağıdaki iki satırı eklemeliyiz. Çünkü bahsi geçen toolları kullanmamız için gereken kütüphaneleri projemize import etmemiz gerekiyor.

implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support:cardview-v7:28.0.0'

Daha sonra, RecyclerView’i kullanacağımız activitynin layout dosyası içerisine RecyclerView viewini ekliyoruz.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="match_parent"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/example_recyler_view"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:listitem="@layout/recycler_view_list_item" />

</android.support.constraint.ConstraintLayout>

Sonrasında ise CardView dizaynını oluşturuyoruz. Bu dizayn için layout klasörü içerisinde bir layout dosyası oluşturabilirsiniz. Bu layout, RecyclerView toolu içerisinde görünecek bir satırın dizaynı olduğu için isim verirken ona göre bir isim verirseniz (list_item, row vb.) işiniz kolaylaşacaktır. Aşağıda örnek dizaynı görebilirsiniz.

<?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"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:tag="cards main container">

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

        <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/txtNameSurname"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="8dp"
                android:layout_marginTop="8dp"
                android:layout_marginEnd="8dp"
                android:text="Çağrı Aldemir"
                android:textColor="#ff5c25"
                android:textSize="24sp"
                app:layout_constraintEnd_toStartOf="@+id/txtBirthTime"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="8dp"
                android:orientation="horizontal"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="@+id/txtNameSurname"
                app:layout_constraintTop_toBottomOf="@+id/txtNameSurname">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="Doğum Tarihi:" />

                <TextView
                    android:id="@+id/txtDateOfBirth"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="8dp"
                    android:layout_weight="1"
                    android:text="31.08.1995" />
            </LinearLayout>

            <TextView
                android:id="@+id/txtBirthTime"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:layout_marginEnd="8dp"
                android:layout_marginBottom="8dp"
                android:text="14:45"
                android:textColor="#05dcad"
                android:textSize="36sp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
        </android.support.constraint.ConstraintLayout>

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

</LinearLayout>

Dizaynları bitirdiğimize göre artık kodlara geçebiliriz.

Öncelikle bir model classı oluşturalım ve içerisine değişken olarak bir string ve bir date ekleyelim. Bu değişkenler üzerinde tutacağımız örnek verileri CardView’ler içerisinde göstereceğimiz için bu kısım önemli. Ancak elbette gerçek bir projede bu tür veriler veritabanı ya da herhangi bir API tarafından sağlanacaktır.

Aşağıda oluşturduğum örnek model classını görebilirsiniz.

package tr.com.cagrialdemir.recyclerviewexampleapp;

import java.util.Date;

public class ModelExample {
    private String nameSurname;
    private Date dateOfBirth;

    public ModelExample(String nameSurname, Date dateOfBirth) {
        this.nameSurname = nameSurname;
        this.dateOfBirth = dateOfBirth;
    }

    public String getNameSurname() {
        return nameSurname;
    }

    public void setNameSurname(String nameSurname) {
        this.nameSurname = nameSurname;
    }

    public Date getDateOfBirth() {
        return dateOfBirth;
    }

    public void setDateOfBirth(Date dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
    }

}

Şimdi ise tüm bu işlemlerin gerçekleşeceği asıl classa geldik. Bu class RecyclerView adaptöründen extend edilmiş bir class ve haliyle override edilecek birtakım metodları var.

package tr.com.cagrialdemir.recyclerviewexampleapp;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.text.SimpleDateFormat;
import java.util.ArrayList;

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

    private ArrayList<ModelExample> mModelExamples;
    private LayoutInflater inflater;
    Context mContext;

    public RecyclerViewAdapter(Context context, ArrayList<ModelExample> mModelExamples) {
        inflater = LayoutInflater.from(context);
        this.mModelExamples = mModelExamples;
        this.mContext = context;
    }


    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.recycler_view_list_item, parent, false);
        MyViewHolder holder = new MyViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        ModelExample selectedModelExample = mModelExamples.get(position);
        holder.setData(selectedModelExample, position);

    }

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


    class MyViewHolder extends RecyclerView.ViewHolder {

        private TextView txtNameSurname;
        private TextView txtDateOfBirth;
        private TextView txtBirthTime;

        public MyViewHolder(View itemView) {
            super(itemView);


            txtNameSurname = itemView.findViewById(R.id.txtNameSurname);
            txtDateOfBirth = itemView.findViewById(R.id.txtDateOfBirth);
            txtBirthTime = itemView.findViewById(R.id.txtBirthTime);
        }

        public void setData(ModelExample selectedModelExample, int position) {

            String nameSurname = selectedModelExample.getNameSurname();
            String dateOfBirth = new SimpleDateFormat("dd.MM.yyyy").format(selectedModelExample.getDateOfBirth());
            String birthTime = new SimpleDateFormat("HH:mm").format(selectedModelExample.getDateOfBirth());

            txtNameSurname.setText(nameSurname);
            txtDateOfBirth.setText(dateOfBirth);
            txtBirthTime.setText(birthTime);

        }
    }
}

Yukarıdaki classa baktığımızda, öncelikle model classımızın arrayini tutması için bir array list, yapının doğru çalışması için bir gereksinim olan LayoutInflater objesi ve ihtiyaç halinde kullanmak üzere (Toast mesajları göstermek vb.) bir context objesi oluşturuyoruz. Constructor bölümünde ise bu objeleri gelen parametrelerin pointerlarına eşitliyoruz.

onCreateViewHolder bölümünde ise RecyclerView içerisinde gösterilecek CardView dizaynını gösteriyoruz ve holderimizi return ediyoruz.

onBindViewHolder bölümünde her bir satır için yapılması gereken işlemleri gerçekleştiriyoruz. Bu işlemler için ise classın aşağısında yazdığımız MyViewHolder isimli subclassı kullanıyoruz.

getItemCount metodu ise yukarıda bahsettiğim bind metodunun kaç kez çalışması gerektiğini değer olarak döndürüyor. Elbette biz de oluşturduğumuz ArrayList’in uzunluğunu döndürüyoruz.

Subclass içerisine geldiğimizde ise görüldüğü gibi her şeyden önce CardView içerisinde oluşturduğumuz viewleri tanımlıyoruz ve daha sonra layout içerisindeki viewlerle match ediyoruz.

Son olarak setData metodunda ise CardView içerisinde yapmamız gereken işlemleri gerçekleştiriyoruz, ben bu örnekte oluşturduğum model classının içerisinde tutulan verileri viewlerin içerisine doldurdum.

Her şey hazır olduğuna göre artık MainActivity içerisine geçip tüm yazdıklarımızı birleştirebiliriz. Ancak ondan önce stackoverflow üzerinden bulduğum ve işime oldukça çok yarayan bir classı daha projemize eklemeliyiz. Bu class aracılığıyla herhangi bir CardView’e tıklandığında bu click triggerini yakalayabiliyoruz ve ona göre istediğimiz işlemleri gerçekleştirebiliyoruz. (Aşağıdaki classı projenize direkt olarak import edebilirsiniz.)

package tr.com.cagrialdemir.recyclerviewexampleapp;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;


public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
    private OnItemClickListener mListener;

    public interface OnItemClickListener {
        public void onItemClick(View view, int position);

        public void onLongItemClick(View view, int position);
    }

    GestureDetector mGestureDetector;

    public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) {
        mListener = listener;
        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return true;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
                if (child != null && mListener != null) {
                    mListener.onLongItemClick(child, recyclerView.getChildAdapterPosition(child));
                }
            }
        });
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
        View childView = view.findChildViewUnder(e.getX(), e.getY());
        if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
            mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
            return true;
        }
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    }
}

Sona yaklaştık, artık MainActivity içerisine bakalım isterseniz;

package tr.com.cagrialdemir.recyclerviewexampleapp;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Toast;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Random;

public class MainActivity extends AppCompatActivity {

    private RecyclerView exampleRecyclerView;
    private RecyclerViewAdapter mRecyclerViewAdapter;
    private ArrayList<ModelExample> mModelExamples;

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

        exampleRecyclerView = findViewById(R.id.example_recyler_view);

        mModelExamples = new ArrayList<>();
        addExampleDatas();

        mRecyclerViewAdapter = new RecyclerViewAdapter(MainActivity.this, mModelExamples);

        exampleRecyclerView.setAdapter(mRecyclerViewAdapter);

        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        exampleRecyclerView.setLayoutManager(linearLayoutManager);

        exampleRecyclerView.addOnItemTouchListener(
                new RecyclerItemClickListener(this, exampleRecyclerView, new RecyclerItemClickListener.OnItemClickListener() {
                    @Override
                    public void onItemClick(View view, int position) {
                        Toast.makeText(MainActivity.this, mModelExamples.get(position).getNameSurname(), Toast.LENGTH_LONG).show();
                    }

                    @Override
                    public void onLongItemClick(View view, int position) {

                    }
                })
        );
    }

    private void addExampleDatas() {
        for (int i = 0; i < 30; i++) {
            mModelExamples.add(new ModelExample(getRandomNameSurname(), createRandomDate()));
        }
    }

    //region Random NameSurname and DateTime Creator Methods

    private String getRandomNameSurname() {

        String[] nameList = {
                "Rachelle Midkiff",
                "Rufus Morneau",
                "Alaine Asay",
                "Brook Whitwell",
                "Ellamae Steen",
                "Phil Layfield",
                "Bobbi Charbonneau",
                "Ernest Gameros",
                "Lyn Lyda",
                "Candy Sorrell",
                "Kermit Moseley",
                "Vonnie Miles",
                "Ellis Kraker",
                "James Taing",
                "Shea Ancona",
                "Leonie Rushing",
                "Sharonda Corrales",
                "Luciana Mcmasters",
                "Francene Dehoyos",
                "Stephen Izzard",
                "Lesli Rohman",
                "Ja Edson",
                "Gale Stokley",
                "Sally Rafter",
                "Denis Pettigrew",
                "Kum Hoffman",
                "Maura Danforth",
                "Terrance Stroupe",
                "Nelida Frieden",
                "Allegra Mcgowen"
        };

        return nameList[new Random().nextInt(nameList.length)];

    }

    private Date createRandomDate() {
        SimpleDateFormat dfDateTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss", Locale.getDefault());
        int year = randBetween(1900, 2013);// Here you can set Range of years you need
        int month = randBetween(0, 11);
        int hour = randBetween(9, 22); //Hours will be displayed in between 9 to 22
        int min = randBetween(0, 59);
        int sec = randBetween(0, 59);

        GregorianCalendar gc = new GregorianCalendar(year, month, 1);
        int day = randBetween(1, gc.getActualMaximum(gc.DAY_OF_MONTH));
        gc.set(year, month, day, hour, min, sec);
        return gc.getTime();
    }

    private int randBetween(int start, int end) {
        return start + (int) Math.round(Math.random() * (end - start));
    }

    //endregion
}

Activity içerisinde öncelikle bir RecyclerView objesi, bir RecyclerViewAdapter objesi (yukarıda yazdığımız class) ve dataları tutacağımız, aynı zamanda adapter içerisine pointerini göndereceğimiz ArrayList objesi oluşturuyoruz.

Daha sonra RecyclerView objesini ve ArrayList’i initialize ediyoruz.

addExampleDatas metodunda oluşturduğumuz örnek verileri ArrayList içerisine ekliyoruz. (Bu metod konuyla ilgili olmadığı için ayrıntılı olarak anlatmayacağım, zaten hazır olarak internet üzerinden bulup kendime göre düzenledim diyebilirim. Ancak kodlara bakarak yapılan işlemi kolayca anlayabilirsiniz. Ayrıca bu metod, içerisinde üç farklı metod daha kullanmakta ancak dediğim gibi bakarak anlayabileceğiniz basit yapılar olduğu için üzerinde durmuyorum.)

RecyclerView adapterimizi initialize ediyoruz ve parametre olarak bulunduğumuz contexti ve oluşturduğumuz ArrayList’i gönderiyoruz. Sonrasında ise RecyclerViewin adapterini oluşturduğumuz adapter olarak set ediyoruz. Bu satırlar birlikte viewi ve adapteri birbirine bağlamış olduk.

Sonrasında gelen 3 satırlık kod bloğu aracılığı ile de viewin layout yapısını set ediyoruz.

Ve son olarak da RecyclerView için click listenerlerimizi oluşturuyoruz. Bu listenerlardan birisi normal click, diğeri ise long click durumunda tetikleniyor. Ben ise normal click bölümünde tıkladığım CardView’in içerisinde bulunan nameSurname değişkenini toast mesajı olarak ekrana yazdırdım.

Bu noktaya kadar her şeyi eksiksiz bir şekilde yaptıysanız projenize RecyclerView yapısını import ettiniz demektir. Uygulama ise şu şekilde görünecektir.

Ancak eğer sorunlarla karşılaştıysanız, hemen aşağıdan proje dosyalarını indirip inceleyebilirsiniz.

Anlatacaklarım bu kadar, umarım yeterli düzeyde anlatabilmişimdir ve işinize yarayacaktır.

Bir sonraki içerikte görüşmek üzere...