반응형

RecyclerView #4 - 아이템 클릭 처리


RecyclerView #1- 구조및 기본 사용법

RecyclerView #2 - 구분선 추가, 아이템간 간격 조절

RecyclerView #3 - 컨텍스트 메뉴 처리

RecyclerView #4 - 아이템 클릭 처리

RecyclerView #5 - 아이템 선택 처리하기

RecyclerView #6 - ItemView를 클래스화 하기

RecyclerView #7 - ViewType 동적변경


RecyclerView에 표시된 아이템을 클릭했을때의 처리 방법


#3에서 RecyclerView에 Context Menu를 연결하는 방법을 간단히 기술했습니다.

그외에도 아이템을 클릭했을때, 특정한 처리를 수행하는 것 또한

RecyclerView의 일반적인 사용패턴이므로 일단 정리해 봅니다.


ViewHolder의 ItemView에 onClick Listener 설치

ViewHolder가 보관하는 것은 결국 View Class(혹은 상속받은 객체)이기 때문에, onClick Listener 를 설치해서 처리하면 됩니다.

ViewHolder의 itemView에 onClick Listener를 설치하면 되는 것이죠.



onClick Listener를 설치 할만한 위치로는 어디가 좋을까요?


1. Apdater의 onBindViewHolder

생성된 ViewHolder에 대해 데이터가 바인딩 될때마다 호출되는 Methord로  굳이 여기서 반복해서 등록할 이유는 없습니다.

public void onBindViewHolder(@NonNull StdViewHolder holder, int position) {
holder.textView.setText(mdata.get(position));
}


2. Adapter의 onCreateViewHolder

ViewHolder를 생성하는 부분으로, inflate된 view에 Listener를 설치할 수 있겠습니다.

public StdViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflate = LayoutInflater.from(mContext);
View view = inflate.inflate(R.layout.list_item, parent, false);

StdViewHolder vh = new StdViewHolder(view);
return vh;
}

3. ViewHolder의 생성자

인자로 전달된 itemView에 Listener를 등록하는 방법도 유효하겠네요.

public StdViewHolder(@NonNull View itemView) {
super(itemView);
this.textView = itemView.findViewById(R.id.textView);


사실 onClick listener를 등록하는 위치는 2번(onCreateViewHolder)이나 3번(ViewHolder 생성자) 모두 유효해 보입니다만.


개인적으로는 View를 관리하는 ViewHolder쪽에서 처리하는 것이 좀 더 깔끔해 보입니다.

onClick이벤트 처리기 내에서 클릭된 Item의 위치를 가져올수 있는 방법을 

ViewHolder가 제공(ViewHolder.getAdapterPosition)하고 있다는 것도 또 하나의 이유일 수도 있겠습니다.

public class StdViewHolder extends RecyclerView.ViewHolder {
public TextView textView;

public StdViewHolder(@NonNull View itemView) {
super(itemView);
this.textView = itemView.findViewById(R.id.textView);

itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

Log.d("Recyclerview", "position = "+ getAdapterPosition());
}
});

itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Log.d("Recyclerview", "position = "+ getAdapterPosition());
return false;
}
});
}
}


RecyclerView의 Item Click을 Activity나 Fragment에 전달하기

위에서 설명한 것처럼 ViewHolder에서 onClickListener를 통해 아이템 클릭 처리를 수행할 수 있습니다만,

개발을 하다보니 RecyclerView를 포함하고 있는 Activity나 Fragment에서 '아이템 클릭'에 대한 처리를 수행하는것이

수월할 경우가 많이 있습니다.


여러가지 방법이 있겠지만,

 Activity에서 Adapter에 Listenner를 전달하고 

ViewHolder의 onClickListener에서 Activity의 Listener를 호출하는 방식으로

다음과 같이 적용해 볼수 있습니다.


1. Adapter & ViewHolder

public class StdRecyclerAdapter extends RecyclerView.Adapter<StdRecyclerAdapter.StdViewHolder> {

public interface OnListItemLongSelectedInterface {
void onItemLongSelected(View v, int position);
}

public interface OnListItemSelectedInterface {
void onItemSelected(View v, int position);
}

private OnListItemSelectedInterface mListener;
private OnListItemLongSelectedInterface mLongListener;


Context mContext;
List<String> mdata;
RecyclerView recyclerView;

public StdRecyclerAdapter(Context context
, OnListItemSelectedInterface listener
, OnListItemLongSelectedInterface longListener) {
this.mContext = context;
this.mListener = listener;
this.mLongListener = longListener;
}


public class StdViewHolder extends RecyclerView.ViewHolder {
public TextView textView;

public StdViewHolder(@NonNull View itemView) {
super(itemView);

itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = getAdapterPosition();
mListener.onItemSelected(v, getAdapterPosition());

Log.d("test", "position = "+ getAdapterPosition());
}
});

itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
mLongListener.onItemLongSelected(v, getAdapterPosition());
return false;
}
});
}
}
}

2. MainActivity

public class MainActivity extends AppCompatActivity implements StdRecyclerAdapter.OnListItemLongSelectedInterface
, StdRecyclerAdapter.OnListItemSelectedInterface{


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


StdRecyclerAdapter mAdapter = new StdRecyclerAdapter(this, this,this);
recyclerView.setAdapter(mAdapter);
mAdapter.setData(dataSet);

}

@Override
public void onItemSelected(View v, int position) {
StdRecyclerAdapter.StdViewHolder viewHolder = (StdRecyclerAdapter.StdViewHolder)recyclerView.findViewHolderForAdapterPosition(position);
Toast.makeText(this, viewHolder.textView.getText().toString(), Toast.LENGTH_SHORT).show();
}

@Override
public void onItemLongSelected(View v, int position) {
Toast.makeText(this, position + " long clicked", Toast.LENGTH_SHORT).show();
}


 


반응형
반응형

RecyclerView #3 - 컨텍스트 메뉴 처리


RecyclerView #1- 구조및 기본 사용법

RecyclerView #2 - 구분선 추가, 아이템간 간격 조절

RecyclerView #3 - 컨텍스트 메뉴 처리

RecyclerView #4 - 아이템 클릭 처리

RecyclerView #5 - 아이템 선택 처리하기

RecyclerView #6 - ItemView를 클래스화 하기

RecyclerView #7 - ViewType 동적변경


CreateContextMenu 리스너 설정


public static class StdViewHolder extends RecyclerView.ViewHolder
implements View.OnCreateContextMenuListener{
}
public StdViewHolder(@NonNull View itemView) {
super(itemView);
this.textView = itemView.findViewById(R.id.textView);

itemView.setOnCreateContextMenuListener(this);
}


메뉴 구성

아래와 같이 동적으로 메뉴를 구성하거나, 리소스를 통해 메뉴를 inflate시키면 된다.
다만 메뉴를 Inflate 하기 위해 필요한 Context는 어딘가 에서 넘겨받거나 구해와야....

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
MenuItem Edit = menu.add(Menu.NONE, R.id.add_menu, 1, "add");
MenuItem Delete = menu.add(Menu.NONE, R.id.delete_menu, 2, "delete");
Edit.setOnMenuItemClickListener(onMenuItemClickListener);
Delete.setOnMenuItemClickListener(onMenuItemClickListener);

}

메뉴 선택 처리

선택된 아이템에 대해 처리한다.
private final MenuItem.OnMenuItemClickListener onMenuItemClickListener = new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.add_menu:
return true;

case R.id.delete_menu:
return true;
}
return false;
}
};

전체코드

public class StdRecyclerAdapter extends RecyclerView.Adapter<StdRecyclerAdapter.StdViewHolder> {


Context mContext;
List<String> mdata;
RecyclerView recyclerView;

public StdRecyclerAdapter(Context context) {
this.mContext = context;
}

public void setData(List<String> data) {
mdata = data;
notifyDataSetChanged();
}

@NonNull
@Override
public StdViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflate = LayoutInflater.from(mContext);
View view = inflate.inflate(R.layout.list_item, parent, false);

StdViewHolder vh = new StdViewHolder(view);
return vh;
}

@Override
public void onBindViewHolder(@NonNull StdViewHolder holder, int position) {
holder.textView.setText(mdata.get(position));
}

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


public static class StdViewHolder extends RecyclerView.ViewHolder
implements View.OnCreateContextMenuListener{
public TextView textView;

public StdViewHolder(@NonNull View itemView) {
super(itemView);
this.textView = itemView.findViewById(R.id.textView);

itemView.setOnCreateContextMenuListener(this);
}

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
MenuItem Edit = menu.add(Menu.NONE, R.id.add_menu, 1, "add");
MenuItem Delete = menu.add(Menu.NONE, R.id.delete_menu, 2, "delete");
Edit.setOnMenuItemClickListener(onMenuItemClickListener);
Delete.setOnMenuItemClickListener(onMenuItemClickListener);
}

private final MenuItem.OnMenuItemClickListener onMenuItemClickListener = new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.add_menu:
return true;

case R.id.delete_menu:
return true;
}
return false;
}
};
}
}


반응형
반응형

RecyclerView #2 - 구분선 추가, 아이템간 간격 조절


RecyclerView #1- 구조및 기본 사용법

RecyclerView #2 - 구분선 추가, 아이템간 간격 조절

RecyclerView #3 - 컨텍스트 메뉴 처리

RecyclerView #4 - 아이템 클릭 처리

RecyclerView #5 - 아이템 선택 처리하기

RecyclerView #6 - ItemView를 클래스화 하기

RecyclerView #7 - ViewType 동적변경


RecyclerView.ItemDecoration


기본 구분선(DividerItemDecoration) 추가

 // 기본 구분선 추가
DividerItemDecoration dividerItemDecoration =
new DividerItemDecoration(recyclerView.getContext(),new LinearLayoutManager(this).getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);


아아템 사이의 간격 조절

RecyclerDecoration.java


package com.thirteenbrains.unodir.stdrecyclerview;

import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.View;

public class RecyclerDecoration extends RecyclerView.ItemDecoration {

private final int divHeight;


public RecyclerDecoration(int divHeight) {
this.divHeight = divHeight;
}

@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1)

outRect.bottom = divHeight;

}
}

MainActivity.java

RecyclerDecoration spaceDecoration = new RecyclerDecoration(20);
recyclerView.addItemDecoration(spaceDecoration);


반응형
반응형

Java 언어로 배우는 디자인 패턴 입문

YUKI HIROSHI

영진닷컴



저자가 이야기하는 대로 GoF의 디자인 패턴을 좀더 이해하기 쉽게 예제와 곁들여 설명한 책..


나도 GoF의 디자인 패턴 책이 있기는 한데, 정말로 읽기가 쉽지 않았던 기억이 있다.


상대적으로 패턴에 대한 설명도 이해하기 수월하며, 

각 패턴을 구현한 예제들도 '패턴'을 이해하는데 도움을 주도록 구성되어 있다.


새로운 여러가지를 동시에 접하고 익히려다 보니, 

우선은 그냥 한번 훑어본 수준이지만

다시 한번 천천히 볼만한 가치가 있는 책이라고 생각된다.



반응형
반응형

서른과 마흔사이 나를 되돌아볼 시간

미리암 프리스

박지희 역

비즈니스북스



제목만 보고, 30~40 즈음에 생각해 봐야하는 것들에 대한 책인줄 알았지만


'생의 전반기'의 경험에 따라 생성되는 '거짓자아'의 존재와 부작용, 

그리고 그것을 극복하는 방법에 대해 이야기 하고 심리학 적(?) 도서.


어떠한 상황에 당면하거나 어떠한 행동을 해야 할때 알게 모르게 떠오르던 과거의 기억들을

'거짓 자아'라는 개념으로 구체화 시켜 설명하며 이러한 것들을 마주하고 이해, 용서, 극복하여

거짓 자아에서 벗어나야 함을 역설하고 있다.


대부분의 심리학 책들이 그렇듯 술술 읽히지는 않지만, 

공감이 가는 내용들이 중간 중간 나와 결국은 다 읽게 되었다.


나의 본성 과 거짓자아 에 대해 조금만 시간을 내어 떠올려보는

연습이 필요할 것 같다.



반응형
반응형

 

RecyclerView #1- 구조및 기본 사용법

 

RecyclerView #1- 구조 및 기본 사용법

RecyclerView #2 - 구분선 추가, 아이템간 간격 조절

RecyclerView #3 - 컨텍스트 메뉴 처리

RecyclerView #4 - 아이템 클릭 처리

RecyclerView #5 - 아이템 선택 처리하기

RecyclerView #6 - ItemView를 클래스화 하기

RecyclerView #7 - ViewType 동적변경

 

RecyclerView는 ListView 의 향상되고 유연한 버전이라고 설명되어 있습니다.

실제로 안드로이드 디벨로퍼 사이트의 ListView에 대한 설명 초기에 

목록을 표시하는보다 현대적이고 유연하며 효율적인 방법을 사용하려면 android.support.v7.widget.RecyclerView를 사용하라고. 언급되어 있습니다.

'ListView 호환 상위 위젯으로 받아들여도 무방할 것 같습니다.

 

RecyclerView는 ListView와 달리 "Item을 표시하는 각각의 View를 효율적으로 재사용할 수 있도록 구성되어" 있습니다.

다만 RecyclerView는 표시되는 각 Item의 selection을 기본 지원하지 않기 때문에 귀찮은 처리가 일부 동반됩니다.

 

#1

 

RecyclerView 구성요소 및 동작개념

 

ViewHolder

RecyclerView는 ViewHolder를 통해 각 항목(Item)을 표시할 View를 관리하도록 하는 구조로 설계되어 있습니다. 

ViewHolder는 RecyclerView.ViewHolder 를 상속받아 구현하게 되며, ViewHolder를 통해 item을 표시할 View를 보관하고 필요에 따라 재사용 할수 있게 됩니다. 

 RecyclerView 는 화면에 표시되는 개수만큼의 ViewHolder와 여분의 ViewHolder를 기본으로 생성하여 스크롤에 대비하며, 

스크롤등의 동작으로 인해 "사라지는 아이템"을 표시하고 있는 ViewHolder를 "새로 표시되는 아이템"을 그리는데 재사용함으로써,

대용량의 데이터라도 소수의 View를 이용하여 표현할 수 있습니다.

 

아래의 이미지는 총 50개의 데이터를 RecyclerView에 삽입하고, 특정 아이템을 클릭할시 배경색을 파란색으로 변경하도록 작성한

샘플 프로그램입니다.( 아이템을 선택할 때를 제외하고는 배경색을 변경하는 코드는 없습니다.)

클릭한 첫번째 아이템뷰는 화면 스크롤에 따라 13번째 아이템을 표시하기위해 재사용되었고, 

스크롤을 계속하게되면 12번 간격으로 파란색으로 선택된 View가 재 사용됨을 볼수 있습니다.

 

 

 

 

 

아래의 이미지를 통해 좀더 확실히 살펴보면,

이 예제 프로그램에서는 한 화면에 표시되는 ViewHoler 8개 + 여분의 ViewHoler 4개가 생성되어 재활용 되고 있음을 알 수 있습니다.

 

 

 

 

Adapter

 위에서 설명한 ViewHolder 객체는  RecyclerView Adapter에 의해 생성 및 관리 되며,

Adapter는 RecyclerView.Adapter 를 상속받아 작성하게 됩니다.

Adapter는 ViewHolder를 position(위치) 기반으로 할당하고, 

Data를 ViewHoler에 반영할 수 있도록  onBindViewHolder() 콜백을 호출하게 됩니다.

다시말해 onBindViewHolder 콜백 호출을 받았을때, 위치(position) 기반의 데이터를 할당된 ViewHolder에 표시할 수 있습니다.

 

 

RecyclerView 사용 예제 구현

 

dependecy

RecyclerView를 사용하기 위해서는 build.gradle(app)에 다음과 같은 dependency 추가가 필요합니다만.

dependencies {
    ...
    implementation 'com.android.support:recyclerview-v7:28.0.0'
}

Layout 편집기 상에 RecyclerView를 Drag&Drop하면 다음과 같은 메시지 박스가 팝업되며 [OK]를 누르면 자동 추가 됩니다.

 

MainActivity Layout

가장 단순한 형태를 사용합니다.

LinearLayout 안에 RecyclerView만 포함된 형태입니다.



<?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" tools:context=".MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v7.widget.RecyclerView> </LinearLayout>

 

 

Adapter & ViewHolder구현

RecyclerView.Adapter

 

위의 개념 설명에서 Adapter는 

RecyclerView.Adapter 를 상속하여 작성하며, 

필요에 따라 ViewHolder를 생성하고 

관리한다고 설명했습니다.

 

public abstract static class Adapter<VH extends RecyclerView.ViewHolder>

 

다음과 같이 3개의 메소드는 필수로 구현하여야 합니다. ( Ctrl + I )

 

메소드 이름을 통해

ViewHolder를 생성하고, 바인드하고, 아이템의 전체 개수를 알려주는 메소드가 필수 구현되어야 함을 유추해 볼수 있습니다.

 

 

RecyclerView.ViewHolder

 

RecyclerView.ViewHolder class의 경우 abstract class일 뿐만 아니라, 

대부분의 경우 원하는 레이아웃을 적용해야 하므로,

RecyclerView.ViewHolder를 상속하는 별도의 Class를 작성해야 합니다. 

 



public class StdRecyclerAdapter extends RecyclerView.Adapter<StdRecyclerAdapter.StdViewHolder> {

@NonNull
@Override
public StdViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return null;
}

@Override
public void onBindViewHolder(@NonNull StdViewHolder holder, int position) {
}

@Override
public int getItemCount() {
}


public static class StdViewHolder extends RecyclerView.ViewHolder{
public StdViewHolder(@NonNull View itemView) {
super(itemView);
}
}
}


 

 

 

이제 필요한 뼈대는 작성이 되었으니, 동작하도록 코드를 추가해 보겠습니다.

 

Adapter's : onCreateViewHoler()

리스트 내의 항목을 표시하기 위한 View를 생성하고, 해당 뷰를 관리(hold)할 ViewHolder를 생성하여 리턴합니다.

 
// Create new views (invoked by the layout manager) @NonNull @Override public .StdViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { // create a new view LayoutInflater inflate = LayoutInflater.from(mContext); View view = inflate.inflate(R.layout.list_item, parent, false); StdViewHolder vh = new StdViewHolder(view); return vh; }

 

list_item.xml layout

은 LinearLayout 안에 TextView를 하나 배치한 형태입니다.

상위 LinearLayout의 layou_height를 match_parent로 하지 않도록 주의하세요.

아이템이 하나밖에 표시되지 않는 것 처럼 보입니다. 실제로 삽질한건 안 비밀...(쿨럭..).



<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="TextView" /> </LinearLayout>

 

 

ViewHolder's : construct()

생성자의 인자로 전달된 View와 관련되 사항을, 필요에 따라 가공하여 보관합니다.

ViewHolder의 의미 그대로 View를 Hold 하는 역할을 수행합니다.

 

 

* StdViewHodlder의 생성자로 넘어온 itemView는 ViewHolder.itemView에 저장되므로
여기서 별도로 저장하지 않더라도, ViewHolder 객체를 통해 접근이 가능합니다.
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public static class StdViewHolder extends RecyclerView.ViewHolder{
// each data item is just a string in this case


public TextView textView;



public StdViewHolder(@NonNull View itemView) {

super(itemView);

this.textView = itemView.findViewById(R.id.textView);
}

}

 

 

Adapter's : onBindViewHolder()

인자를 통해 전달된 ViewHolder 객체에 position에 기반한 데이터를 할당(표시) 합니다.



// Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(@NonNull StdViewHolder holder, int position) { // - get element from your dataset at this position // - replace the contents of the view with that element holder.textView.setText(mdata.get(position)); }

 

 

MainActivity

public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } private void init(){ RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView); // use this setting to improve performance if you know that changes // in content do not change the layout size of the RecyclerView recyclerView.setHasFixedSize(true); LinearLayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); List<String> dataSet = new ArrayList<String>(); dataSet.add("C/C++"); dataSet.add("Java"); dataSet.add("Kotlin"); dataSet.add("Python"); int i = dataSet.size(); StdRecyclerAdapter mAdapter = new StdRecyclerAdapter(this, dataSet); recyclerView.setAdapter(mAdapter); } }

 

오늘은 여기까지만...

 

참고자료

https://developer.android.com/guide/topics/ui/layout/recyclerview

반응형

+ Recent posts