2019/04/14 - [Android] - 안드로이드 리사이클러뷰(RecyclerView) 사용법 (1)

 

지난 글에 이어서 리사이클러뷰의 아이템부터 알아보겠다.

 

 

/* item.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"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:orientation="horizontal">


    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        app:srcCompat="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:text="제목" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:text="내용" />
</LinearLayout>

 

- [layout] -> [new] -> [Layout Resource File] 로 .xml 레이아웃 파일을 하나 생성한다.

 

- 아이템은 간단한 예시로 이미지뷰 1개, 텍스트뷰 2개로 구성하였다. 여기서 리스트뷰에 표시할 한 아이템의 레이아웃을 만들면 된다. 

 

 

 

 

 

 

/* RecyclerViewA.java */

 


package com.tistory.godog.test2;

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.ImageView;
import android.widget.TextView;
import android.widget.Toast;

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

    private String[] title = {"제목1", "제목2", "제목3", "제목4",
            "제목5", "제목6", "제목7", "제목8", "제목9", "제목10", };

    private String[] content = {"내용1", "내용2", "내용3", "내용4",
            "내용5", "내용6", "내용7", "내용8", "내용9", "내용10", };

    public class ViewHolder extends  RecyclerView.ViewHolder {
        public TextView textView;
        public TextView textView2;
        public ImageView imageView;

        public ViewHolder(View view) {
            super(view);
            this.textView = view.findViewById(R.id.textView);
            this.textView2 = view.findViewById(R.id.textView2);
            this.imageView = view.findViewById(R.id.imageView);
        }
    }

    @Override
    public RecyclerViewA.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
        RecyclerViewA.ViewHolder viewHolder = new ViewHolder(view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(RecyclerViewA.ViewHolder holder, final int position) {
        holder.textView.setText(title[position]);
        holder.textView2.setText(content[position]);

        holder.imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(v.getContext(), position+"번 째 이미지!", Toast.LENGTH_SHORT).show();
            }
        });

        holder.textView2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(v.getContext(), "Test!", Toast.LENGTH_SHORT).show();
            }
        });

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(v.getContext(), position+"번 째!", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        return title.length;
    }
}

 

1. 자바(.java) 파일을 추가합니다.

 

2. RecyclerView.Adapter<RecyclerView.ViewHolder>를 상속 받습니다.

 

3. [Ctrl]+[I] 3개의 메소드를 추가해줍니다.

 

4. 19라인의 RecyclerView.ViewHolder를 상속받은 클래스를 선언한 후 item에서 뷰객체 변수를 선언한 후 연결해 줍니다.

 

5. 2번에서 상속하였던 <RecyclerView.ViewHolder> 부분을 <자바파일명.4번의 선언한 클래스명>으로 수정해줍니다.

 

 

여기까지 진행하였다면 전반적인 구조는 끝이난다.

 

 

onCreateViewHolder() : 이곳은 LayoutManager에 의해 새 아이템을 만들 때 호출하며, 이 아이템뷰의 ViewHolder를 만들어준다.

 

onBindViewHolder() : LayoutManager에 의해 ViewHolder에 position에 맞는 데이터를 이용하여 리스트를 채우게 된다.

 

getItemCount() : 보통 메소드 이름처럼 리사이클러뷰의 표시할 데이터(아이템)의 개수를 리턴 한다.

 

 

 

- holder에서 연결한 item의 뷰 객체들은 onBindViewHolder() 메소드내에서 onClick()등 액션에 대한 실행을 지정할 수 있습니다.

 

아래 이미지는 이미지 뷰, 텍스트 뷰, 아이템에 따라 클릭하였을 때 다른 결과를 확인 할 수 있습니다.

 

이렇듯 리사이클러뷰를 사용하면 간단하게 각 아이템마다 액션을 설정하거나 아이템 내 각각의 뷰마다 액션을 간단히 설정 할 수도 있다.

 

T리사이클러뷰를 이용하여 커스텀 리스트를 만들어 본다.

 

실행 화면

- 리사이클러뷰(RecyclerView)는 기존의 사용하던 리스트뷰(ListView)의 단점들을 보완한 레이아웃이다.

 

- 단점들을 보완하여 만들어진만큼 기존의 커스텀 리스트뷰를 만들 때에 복잡함을 줄여 리스트를 만들 수 있다. ( 데이터를 관리하는 것과 UI를 표현하는 것이 분리가 되어있으므로 번거로운 기존의 수정 방식을 피할 수 있고 에러도 줄일 수 있다.)

 

 

 

 

리사이클러뷰(RecyclerView)

- 이름 그대로 리사이클(재활용)하므로 많은 양의 데이터를 표시하여도 메모리가 부족하게 되어 프로그램이 강제로 종료되는 상황을 막을 수 있다.

(이는 가끔 앱들을 사용하면서 화면을 내릴 때 화면이 버벅거리는 현상들을 발생시킨다.)

 

 

- 재활용이란 위 2개의 이미지를 보면 이해하기 편한데 첫 번째 이미지를 보면 1번 뷰와 같이 스크롤을 내려 보이지 않는 뷰도 메모리를 계속 사용하고 있다.

 

하지만 리사이클 뷰는 두 번째 이미지처럼 지나친 1번 뷰를 다시 아래로 재사용함으로써 메모리 문제를 해결하도록 만들어 준다.

 

 

 

 

 

 

 리사이클뷰를 사용하기 위해서는 우선 라이브러리를 추가해주어야 한다.

 

implementation 'com.android.support:recyclerview-v7:27.1.1'

 

직접 추가해주어도 상관없지만 아래 글을 참조하여 추가해주면 더욱 좋을 것이다.

 

2019/04/03 - [안드로이드] - 안드로이드 스튜디오 라이브러리 추가

 

 

 

 

 

 그러면 먼저 프로젝트 생성 시 만들어지는 메인 레이아웃과 메인 자바 파일을 살펴보겠다.

 

 

 

/* 메인 레이아웃( 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="wrap_content"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <RelativeLayout
        android:id="@+id/rel"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingBottom="40dp">

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

        <Button
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@+id/recycler_view"
            android:text="hoxy" />
    </RelativeLayout>
</LinearLayout>

 

- android.support.v7.widget.RecyclerView로 리사이클러뷰를 추가하였다. Design 탭의 Palette의 검색하여서 추가할 수도 있다.

 

- 위 코드를 보면 굳이 RelativeLayout 속에 리사이클러뷰와 버튼을 추가하였다. 이는 리사이클러뷰 밑에 버튼 추가 시 보이지 않는 문제를 해결하기 위해서이다.

( 아마도 버튼이 리사이클러뷰 아래로 가서 보이지 않는 것 같다. 버튼을 먼저 삽입하고 리사이클러뷰를 삽입하면 버튼이 보인다.)

 

- 리사이클러뷰에서 paddingBottom을 40dp 준 이유도 마찬가지이다. 이를 한번 빼보고 실행해보는 것도 좋을 것이다.

 

 

 

 

 

 

/* 메인 자바 코드 ( MainActivity.java ) */

package com.tistory.godog.test2;


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


public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    public RecyclerView.LayoutManager layoutManager;
    private RecyclerView.Adapter adapter;

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

        recyclerView = findViewById(R.id.recycler_view);
        recyclerView.setHasFixedSize(true);

        //layoutManager = new GridLayoutManager(this, 1);
        layoutManager = new LinearLayoutManager(this);
     

        recyclerView.setLayoutManager(layoutManager);

        adapter = new RecyclerViewA();
        recyclerView.setAdapter(adapter);
    }
}

 

- 변수 선언 

 12라인 : 리사이클러뷰 변수를 선언

 13라인 : 리사이클러뷰 변수와 연결하여 관리할 레이아웃 매니저 변수 선언

 14라인 : 리사이클러뷰 어뎁터 선언

 

- 설정

 22라인 : .setHasFixedSize(true) 메서드는 리사이클러뷰 안 아이템들의 크기를 가변적으로 바꿀지 아니면 일정한 크기를 사용할지를 지정한다.

 이를 false로 준다면 매번 아이템들의 크기를 계산해야 하므로 성능 저하에 영향을 미칠 수 있다. 그리고 디자인적으로도 크기가 다 같은 것이 좋으니 true를 주는 게 좋다.

 

 

 24/25라인 : 레이아웃 매니저의 타입을 지정해준다. 24라인 주석처리가 된

  new GridLayoutManager(this, 1)는 그리드형으로 보여준다. 그리드형이라고 한다면 쉽게 말해서

  m x n 형태이다. 여기서 2번 째로 주는 인수열의 개수이다.

 

 25라인 : new LinearLayoutManager(this)는 우리가 생각하는 그 리스트형으로 설정에 따라서 가로 혹은 세로로 표시할 수 있다.

 

 

 28라인  : 리사이클러뷰와 레이아웃 매니저를 연결해준다.

 

 

 30라인 : 여기서는 어뎁터 객체를 생성하는데, 이는 추후의 추가할 어뎁터 자바 파일명이다.

 

 31라인 : 리사이클러뷰의 어뎁터를 연결해준다.

 

 

 

 

다음 글에서 이어집니다 

 

2019/04/14 - [Android] - 안드로이드 리사이클러뷰(RecyclerView) (2)

 

안드로이드 스튜디오 라이브러리를 찾아서 추가

 

 

 

 

 

 

 

우선 라이브러리 등을 확인하려면 built.gradle( Module: app) -> dependencies {} 항목들을 보면 알 수 있다.

 

이 곳에 직접 라이브러리를 입력하여서 추가할 수도 있지만 보통 예전의 작성된 글이나 코드들을 참조하여 추가하기 때문에 버전 같은 것이 다를 경우가 많다.

 

그래서 직접 입력하기보다는 찾아서 추가해주는 것이 좋다.

 

 

 

 

 

라이브러리를 찾아서 추가하기 위해서는 우선

 

<File> --> <Project Structure...>로 들어간다.

 

단축키로는 Ctrl + Alt + Shift + S 이므로 각자 편한 방법을 사용하여 실행하면 된다.

 

 

 

 

 

실행되었다면 이제는

 

<app>의 Dependencies 탭으로 들어간다.

 

 

 

 

 

그러면 오른쪽에 있는 녹색 +를 클릭하여 라이브러리를 추가하기 위해서 Library를 클릭해 준다.

 

 

 

 

 

 

 

그러면 검색창이 뜨고 일부만 검색해도 그 단어를 포함하고 있는 것들을 다 보여주기 때문에 이곳에서 추가하기 원하는 라이브러리를 찾아서 추가해주면 된다.

 

 

 

 위에서도 말했듯이 기존의 작성된 글을 참조하는 경우가 많으므로 추가하는 라이브러리의 버전이 다를 경우가 종종 발생한다.

 

 그러므로 라이브러리를 추가할 때는 직접 입력하기보다는 이렇게 찾아서 추가해주는 것이 좋다.

 

 

 

 

안드로이드(자바)에서 년, 월, 일, 시간, 분, 초를 구해서 변수에 저장

 

 

 

실행 결과

 

 

 

 

 

 

 

/* 레이아웃 */

<?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"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">


    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="날짜 받기"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

 

버튼을 클릭하면 텍스트뷰에 날짜와 시간을 표시하도록 한다.

 

 

 

/* 자바 코드 */

package com.tistory.godog.test3;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.util.Calendar;

public class MainActivity extends AppCompatActivity {

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

        textView = findViewById(R.id.textView);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getDT();
            }
        });
    }

    public void getDT() {
        Calendar cal = Calendar.getInstance();
        int y=0, m=0, d=0, h=0, mi=0, s=0;

        y = cal.get(Calendar.YEAR);
        m = cal.get(Calendar.MONTH) +1;
        d = cal.get(Calendar.DAY_OF_MONTH);
        h = cal.get(Calendar.HOUR);
        mi = cal.get(Calendar.MINUTE);
        s = cal.get(Calendar.SECOND);

        textView.setText(y+"/"+m+"/"+d+" "+h+":"+mi+":"+s);
    }
}

 

- Calendar 클래스가 추상 클래스이여서 new로 인스턴스를 생성할 수 없다.

 

└> 이는 날짜와 시간을 계산하는 방법이 지역, 나라에 따라 다르기 때문이다. 그래서 getInstance() 메소드를 사용하면 운영체제에 설정되어 있는 시간을 기준으로 객체를 얻어온다.

 

 

 

- 월을 구할 때는 0~11에 값을 가지므로 우리가 사용하는 1~12 값의 월을 얻기 위해서는 +1을 해주어야 한다.

 

 

안드로이드 날짜/시간 다이로그 실행하여서 날짜와 시간을 변수에 저장

 

 

실행 화면

설정한 날짜와 시간을 "날짜/시간 확인"버튼을 누르면 토스트 메시지로 확인할 수 있도록 만들었다.

 

 

 

 

/* 레이아웃 */

메인 / 달력 / 시계


<?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"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="달력" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="시간" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="날짜/시간 확인" />


</LinearLayout>

- 달력 버튼을 클릭하면 달력 다이로그가 실행.

- 시간 버튼을 클릭하면 시간 다이로그가 실행.

- 날짜/시간 확인 버튼을 클릭하면 위 다이로그에서 저장한 날짜와 시간을 토스트 메시지로 보여준다.

 

 

 

/* 자바 코드 */


package com.tistory.godog.test2;

import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.TimePicker;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    int y=0, m=0, d=0, h=0, mi=0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showDate();
            }
        });

        Button button1 = findViewById(R.id.button2);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showTime();
            }
        });
        Button button2 = findViewById(R.id.button3);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getApplicationContext(), y+"."+m+"."+d+"\n"+h+":" + mi, Toast.LENGTH_SHORT).show();
            }
        });
    }

    void showDate() {
        DatePickerDialog datePickerDialog = new DatePickerDialog(this, new DatePickerDialog.OnDateSetListener() {
            @Override
            public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
                y = year;
                m = month+1;
                d = dayOfMonth;

            }
        },2019, 1, 11);

        datePickerDialog.setMessage("메시지");
        datePickerDialog.show();
    }

    void showTime() {
        TimePickerDialog timePickerDialog = new TimePickerDialog(this, new TimePickerDialog.OnTimeSetListener() {
            @Override
            public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
                h = hourOfDay;
                mi = minute;

            }
        }, 21, 12, true);
        
        timePickerDialog.setMessage("메시지");
        timePickerDialog.show();
    }
}

 

- DatePickerDialog( Context , 이벤트 리스너, 년, 월, 일) : 여기서 년, 월, 일은 Date다이로그가 처음 보여주는 년, 월, 일이다. 

즉, 다이로그에 초기값 같은 것으로 오늘 날짜로 설정하고 싶다면 Calendar변수를 사용하여 설정해 주면 된다.

 

└> onDateSet 메소드에서 year, month, dayOfMonth를 미리 선언해둔 변수에 저장하여 다른 곳에서 사용할 수 있다.

 

└> 주의할점은 우리가 아는 월은 1~12지만 여기서는 0~11로 되어있기 때문에 초기값을 줄 때에도 값을 가져올 때에도 +1을 해야 생각했던 월의 값을 받을 수 있다. 실제로 위 코드에서 DateDialog의 월 초기값을 1로 주었지만 실행된 Dialog에서는 2월을 보여준다.

 

└> datePickerDialog.setMessage("메시지") 이곳에 메시지를 입력하면 다이로그 위에 보여준다. 인터페이스로서 이용하여도 되지만 생략하여도 무관하다.

 

 

 

- TimePickerDialog( Context, 이벤트 리스터, 시간, 분, 오전/오후 구분) : 여기서 시간과 분은 DatePickerDialog처럼 초기값을 주는 것이다. 마지막 인자로 오전/오후 구분이라고 하였는데 이를 true로 주었을 때는 0~24시간을 선택할 수 있는 뷰를 보여준다.

반대로 false를 주었을 때는 0~12시간만을 선택할 수 있으며, 따로 오전/오후를 선택할수 있는 버튼이 주어진다.

 

└> 마찬가지로 onTimeSet에서 hourOfDay, minute를 변수에 저장하여서 입력받은 시간과 분을 이용할 수 있다.

 

 

+ Recent posts