코리아 IT아카데미/android
11일차 | RecyclerView, Observer(callback)
Sharon kim
2022. 2. 28. 18:15
옵저버 패턴
콜백메서드 와 유사, 리스너를 만들어냄
객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴
obserber
IButtonListener.interface
package com.example.myrecyclerview.observer;
public interface IButtonListener {
void clickEvent(String event);//추상 메서드
}
Button.java
package com.example.myrecyclerview.observer;
public class Button {
private String name;
private IButtonListener iButtonListener;
public Button(String name){
this.name = name;
}
public void click(String message){
iButtonListener.clickEvent(message);
}
public void addListner(IButtonListener listener){
this.iButtonListener = listener;
}
}
MainTest.java
package com.example.myrecyclerview.observer;
public class MainTest {
public static void main(String[] args) {
Button myButton = new Button("우리가 만든 버튼");
myButton.addListner(new IButtonListener() {
@Override
public void clickEvent(String event) {
System.out.println("동작을 확인합니다 !!! " + event);
}
});
myButton.click("안녕하세요~~~");
myButton.click("hi");
myButton.click("hello");
myButton.click("abcd");
}
}
RecyclerView
리사이클러 뷰는 리스트 뷰에 비해 상당히 기능적 커스터마이징에 중점을 두고 있다. 리스트 뷰에 비해 조금 어려울 수 있지만, 사용자에게 앱에 대한 연결성을 유지시켜주는 머터리얼 디자인에 대한 요구를 충족시키는 복잡한 그리드나 리스트에 아주 유용하게 쓰일 수 있다.
주요 클래스
- Adapter – 기존의 ListView에서 사용하는 Adapter와 같은 개념으로 데이터와 아이템에 대한 View생성
- ViewHolder – 재활용 View에 대한 모든 서브 뷰를 보유
- LayoutManager – 아이템의 항목을 배치
- ItemDecoration – 아이템 항목에서 서브뷰에 대한 처리
- ItemAnimation – 아이템 항목이 추가, 제거되거나 정렬될때 애니메이션 처리
출처: https://itmining.tistory.com/12 [IT 마이닝]
코드추가
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
build.gradle(Module:~)
dependencies {
//glide
implementation 'com.github.bumptech.glide:glide:4.13.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.13.0'
}
adapter
> FoodListAdapter.interface
package com.example.myrecyclerview.adapter;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.RoundedCorner;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.example.myrecyclerview.DetailActivity;
import com.example.myrecyclerview.Interfaces.OnFoodListItemListener;
import com.example.myrecyclerview.MainActivity;
import com.example.myrecyclerview.R;
import com.example.myrecyclerview.models.Food;
import java.util.ArrayList;
//어뎁터 만들때
//1. 상속 RecyclerView.Adapter<> <---
//2. 뷰홀더 무조건 만들기
public class FoodListAdapter extends RecyclerView.Adapter<FoodListAdapter.ViewHolder>{
ArrayList<Food> list;
Context context;
OnFoodListItemListener onFoodListItemListener;
//메서드로 연결하는 방법
// public void setOnFoodListItemListener(OnFoodListItemListener onFoodListItemListener){
// this.onFoodListItemListener = onFoodListItemListener;
// }
public FoodListAdapter(ArrayList<Food> list, Context context, OnFoodListItemListener listener){
this.list = list;
this.context = context;
this.onFoodListItemListener = listener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_food, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
//holder.thumbnailImageView.
Food food = list.get(position);
holder.titleTextView.setText(food.getTitle());
holder.subTitleTextView.setText(food.getSubTitle());
holder.detailTextView.setText(food.getDetail());
Glide.with(context)
.load(food.getThumbnail())
.centerCrop()
.transform(new CenterCrop(), new RoundedCorners(30))
.into(holder.thumbnailImageView);
}
@Override
public int getItemCount() {
return list.size();
}
//내부 클래스
public class ViewHolder extends RecyclerView.ViewHolder{
ImageView thumbnailImageView;
TextView titleTextView;
TextView subTitleTextView;
TextView detailTextView;
public ViewHolder(@NonNull View itemView) {
super(itemView);
thumbnailImageView = itemView.findViewById(R.id.thumbnailImageView);
titleTextView = itemView.findViewById(R.id.titleTextView);
subTitleTextView = itemView.findViewById(R.id.subTitleTextView);
detailTextView = itemView.findViewById(R.id.detailTextView);
detailTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onFoodListItemListener.OnItemClick(getLayoutPosition());
// Log.d("TAG", "클릭됨" + getLayoutPosition());
// Intent intent = new Intent(context, DetailActivity.class);
// context.startActivity(intent);
}
});
}
}
}
models
> Food.java
package com.example.myrecyclerview.models;
import java.util.ArrayList;
public class Food {
private String thumbnail;
private String title;
private String subTitle;
private String detail;
public Food(String thumbnail, String title, String subTitle, String detail) {
this.thumbnail = thumbnail;
this.title = title;
this.subTitle = subTitle;
this.detail = detail;
}
public String getThumbnail() {
return thumbnail;
}
public String getTitle() {
return title;
}
public String getSubTitle() {
return subTitle;
}
public String getDetail() {
return detail;
}
public static ArrayList<Food> getData() {
ArrayList<Food> foods = new ArrayList<>();
foods.add(new Food("https://cdn.pixabay.com/photo/2014/11/05/15/57/salmon-518032__340.jpg", "Food1", "SubTitle", "detail"));
foods.add(new Food("https://cdn.pixabay.com/photo/2017/02/15/10/39/salad-2068220__340.jpg", "Food1", "SubTitle", "detail"));
foods.add(new Food("https://cdn.pixabay.com/photo/2017/06/06/22/46/mediterranean-cuisine-2378758__340.jpg", "Food1", "SubTitle", "detail"));
foods.add(new Food("https://cdn.pixabay.com/photo/2014/11/05/15/57/salmon-518032__340.jpg", "Food1", "SubTitle", "detail"));
foods.add(new Food("https://cdn.pixabay.com/photo/2016/03/05/19/02/hamburger-1238246__340.jpg", "Food1", "SubTitle", "detail"));
foods.add(new Food("https://cdn.pixabay.com/photo/2014/11/05/15/57/salmon-518032__340.jpg", "Food1", "SubTitle", "detail"));
foods.add(new Food("https://cdn.pixabay.com/photo/2015/12/09/17/11/vegetables-1085063__340.jpg", "Food1", "SubTitle", "detail"));
foods.add(new Food("https://cdn.pixabay.com/photo/2014/11/05/15/57/salmon-518032__340.jpg", "Food1", "SubTitle", "detail"));
foods.add(new Food("https://cdn.pixabay.com/photo/2014/11/05/15/57/salmon-518032__340.jpg", "Food1", "SubTitle", "detail"));
foods.add(new Food("https://cdn.pixabay.com/photo/2014/11/05/15/57/salmon-518032__340.jpg", "Food1", "SubTitle", "detail"));
foods.add(new Food("https://cdn.pixabay.com/photo/2015/12/09/17/11/vegetables-1085063__340.jpg", "Food1", "SubTitle", "detail"));
foods.add(new Food("https://cdn.pixabay.com/photo/2014/11/05/15/57/salmon-518032__340.jpg", "Food1", "SubTitle", "detail"));
foods.add(new Food("https://cdn.pixabay.com/photo/2014/11/05/15/57/salmon-518032__340.jpg", "Food1", "SubTitle", "detail"));
foods.add(new Food("https://cdn.pixabay.com/photo/2014/11/05/15/57/salmon-518032__340.jpg", "Food1", "SubTitle", "detail"));
foods.add(new Food("https://cdn.pixabay.com/photo/2015/04/08/13/13/food-712665__340.jpg", "Food1", "SubTitle", "detail"));
return foods;
}
}
MainActivity.java
package com.example.myrecyclerview;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.LinearLayout;
import com.example.myrecyclerview.Interfaces.OnFoodListItemListener;
import com.example.myrecyclerview.adapter.FoodListAdapter;
import com.example.myrecyclerview.models.Food;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
// 준비물
// 샘플데이터 (통신)
// adapter
// layout manager
/*
* RecyclerView
* 장점
* -ListView 개선판
* -ViewHolder (강제)
* -유연하다
* -LayoutManager (수평으로 리스트를 만들기 쉽다)
* -Linear
* -Grid
* -StaggeredGrid
* */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<Food> foods = Food.getData();
RecyclerView recyclerView = findViewById(R.id.RecyclerVIew);
FoodListAdapter adapter = new FoodListAdapter(foods, this, new OnFoodListItemListener() {
@Override
public void OnItemClick(int position) {
Log.d("TAG","position " + position);
Intent intent = new Intent(getApplicationContext(), DetailActivity.class);
intent.putExtra("position", position);
String title = foods.get(position).getTitle();
Log.d("TAG", "position");
startActivity(intent);
}
});
recyclerView.setAdapter(adapter);
//레이아웃 매니저 생성
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
// GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
// recyclerView.setLayoutManager(gridLayoutManager);
//devider 생성해보기
// DividerItemDecoration dividerItemDecoration
// = new DividerItemDecoration(this, linearLayoutManager.getOrientation());
// recyclerView.addItemDecoration(dividerItemDecoration);
}
}
item_food.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"
android:padding="16dp">
<ImageView
android:id="@+id/thumbnailImageView"
android:layout_width="120dp"
android:layout_height="120dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/purple_200" />
<TextView
android:id="@+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="F0001"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@id/subTitleTextView"
app:layout_constraintStart_toEndOf="@id/thumbnailImageView"
app:layout_constraintTop_toTopOf="@id/thumbnailImageView"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/subTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="sub Title"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/thumbnailImageView"
app:layout_constraintStart_toStartOf="@id/titleTextView"
app:layout_constraintTop_toBottomOf="@id/titleTextView" />
<TextView
android:id="@+id/detailTextView"
android:layout_width="100dp"
android:layout_height="100dp"
android:gravity="center"
android:text="detail"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="@id/thumbnailImageView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/thumbnailImageView" />
<!-- <View-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="2dp"-->
<!-- android:background="@color/purple_200"-->
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintStart_toStartOf="parent" />-->
</androidx.constraintlayout.widget.ConstraintLayout>
activity_main.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="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/RecyclerVIew"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
tools:listitem="@layout/item_food">
</androidx.recyclerview.widget.RecyclerView>
</androidx.constraintlayout.widget.ConstraintLayout>
옵저버 more
interfaces
> OnFoodListItemListener.interface
package com.example.myrecyclerview.Interfaces;
public interface OnFoodListItemListener {
void OnItemClick(int position);
}
DetailActivity.java
package com.example.myrecyclerview;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class DetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
}
}
adapter
> FoodListAdapter
package com.example.myrecyclerview.adapter;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.RoundedCorner;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.example.myrecyclerview.DetailActivity;
import com.example.myrecyclerview.Interfaces.OnFoodListItemListener;
import com.example.myrecyclerview.MainActivity;
import com.example.myrecyclerview.R;
import com.example.myrecyclerview.models.Food;
import java.util.ArrayList;
//어뎁터 만들때
//1. 상속 RecyclerView.Adapter<> <---
//2. 뷰홀더 무조건 만들기
public class FoodListAdapter extends RecyclerView.Adapter<FoodListAdapter.ViewHolder>{
ArrayList<Food> list;
Context context;
OnFoodListItemListener onFoodListItemListener;
//메서드로 연결하는 방법
// public void setOnFoodListItemListener(OnFoodListItemListener onFoodListItemListener){
// this.onFoodListItemListener = onFoodListItemListener;
// }
public FoodListAdapter(ArrayList<Food> list, Context context, OnFoodListItemListener listener){
this.list = list;
this.context = context;
this.onFoodListItemListener = listener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_food, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
//holder.thumbnailImageView.
Food food = list.get(position);
holder.titleTextView.setText(food.getTitle());
holder.subTitleTextView.setText(food.getSubTitle());
holder.detailTextView.setText(food.getDetail());
Glide.with(context)
.load(food.getThumbnail())
.centerCrop()
.transform(new CenterCrop(), new RoundedCorners(30))
.into(holder.thumbnailImageView);
}
@Override
public int getItemCount() {
return list.size();
}
//내부 클래스
public class ViewHolder extends RecyclerView.ViewHolder{
ImageView thumbnailImageView;
TextView titleTextView;
TextView subTitleTextView;
TextView detailTextView;
public ViewHolder(@NonNull View itemView) {
super(itemView);
thumbnailImageView = itemView.findViewById(R.id.thumbnailImageView);
titleTextView = itemView.findViewById(R.id.titleTextView);
subTitleTextView = itemView.findViewById(R.id.subTitleTextView);
detailTextView = itemView.findViewById(R.id.detailTextView);
detailTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onFoodListItemListener.OnItemClick(getLayoutPosition());
// Log.d("TAG", "클릭됨" + getLayoutPosition());
// Intent intent = new Intent(context, DetailActivity.class);
// context.startActivity(intent);
}
});
}
}
}
MainActivity.java
package com.example.myrecyclerview;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.LinearLayout;
import com.example.myrecyclerview.Interfaces.OnFoodListItemListener;
import com.example.myrecyclerview.adapter.FoodListAdapter;
import com.example.myrecyclerview.models.Food;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
// 준비물
// 샘플데이터 (통신)
// adapter
// layout manager
/*
* RecyclerView
* 장점
* -ListView 개선판
* -ViewHolder (강제)
* -유연하다
* -LayoutManager (수평으로 리스트를 만들기 쉽다)
* -Linear
* -Grid
* -StaggeredGrid
* */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<Food> foods = Food.getData();
RecyclerView recyclerView = findViewById(R.id.RecyclerVIew);
FoodListAdapter adapter = new FoodListAdapter(foods, this, new OnFoodListItemListener() {
@Override
public void OnItemClick(int position) {
Log.d("TAG","position " + position);
Intent intent = new Intent(getApplicationContext(), DetailActivity.class);
intent.putExtra("position", position);
String title = foods.get(position).getTitle();
Log.d("TAG", "position");
startActivity(intent);
}
});
recyclerView.setAdapter(adapter);
//레이아웃 매니저 생성
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
// GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
// recyclerView.setLayoutManager(gridLayoutManager);
//devider 생성해보기
// DividerItemDecoration dividerItemDecoration
// = new DividerItemDecoration(this, linearLayoutManager.getOrientation());
// recyclerView.addItemDecoration(dividerItemDecoration);
}
}
activity_detail.xml >>생성만
설명 : detail 버튼을 누르면 화면 전환과 동시에 Logcat 처럼 된다.
▶ Image에도 다음과 같은 작업 하기◀