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



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

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

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

RecyclerView #4 - 아이템 클릭 처리

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

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

RecyclerView #7 - ViewType 동적변경




RecyclerView는 선택된 아이템의 선택상태나 선택된 아이템 리스트 관리등의 기능을 제공하지 않습니다.


아이템이 선택되었을때 배경색을 변경하거나, 

RecyclerView의 외부에서 선택 혹은 해제 처리를 하는 방법에 대해 정리해 보겠습니다.



특정 아이템을 클릭했을때 배경색상 변경하기

우선 생각해 볼수 있는 방법은 아이템이 클릭되었을때 onClick() 리스너r에서 itemView의 배경색을 변경하는 방법입니다.

ViewHolder가 보관하는 itemView에 onClick 리스너를 설정했으므로, 

onClick()리스너에서 넘어오는 View는 ViewHolder.itemView 객체입니다.

아래와 같이 배경색을 변경해 줄수 있습니다.

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) {

v.setBackgroundColor(Color.BLUE);

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

선택 해제 처리는?

onClick 이벤트 발생시, 클릭된 아이템의 position( ViewHolder.getAdapterPosition() )과 선택상태를 저장해 놓고 토글 시키면 될것 같습니다.

position별 선택상태를 저장하는 구조는 SparseBooleanArray를 사용하겠습니다.

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

private SparseBooleanArray mSelectedItems = new SparseBooleanArray(0);

....


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) {
int position = getAdapterPosition();

if ( mSelectedItems.get(position, false) ){
mSelectedItems.put(position, false);
v.setBackgroundColor(Color.WHITE);
} else {
mSelectedItems.put(position, true);
v.setBackgroundColor(Color.BLUE);
}

}
});


의도대로 잘 동작하는 것 같습니다만, 스크롤 해보면 이상한(혹은 당연한) 현상이 발생합니다.


 

"RecyclerView #1- 구조및 기본 사용법" 포스트에 첨부했던 이미지 인데요.

다시 한번만 정리하고 가겠습니다.


RecyclerView의 Adapter는 전체 아이템의 개수에 기반하여

적정수( 한화면에 표시되는 아이템 개수 + 스크롤시 사용할 여분의 아이템 개수)의 ViewHolder( View )를 미리 생성하고

화면상에 표시되지 않는 View를 재사용하는 구조입니다.


클릭할 당시의 View가 계속해서 동일한 데이터를 표시한다고 보장할 수 없다는 결론이죠.

즉 아이템의 선택상태는 position기반으로 관리하되, 실제 선택 상태를 표시하는 것은 ViewHolder에 데이터가

반영되는 시점에 처리 해주면 될것 같습니다.

* ViewHolder의 getAdapterPosition()은 ViewHolder의 재사용 여부와 관계 없이 아이템 배치순서상의 position( 0 base)을 리턴합니다.

DataSet내의 index값 이라고 생각해도 무방합니다.


그렇다면 

1. 아이템 클릭시 선택 상태 저장 및 선택 상태 표시

2. 아이템 바인딩 시에 선택 상태 표시

의 방법으로 구현해 보면 될것 같습니다.

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

if ( mSelectedItems.get(position, false) ){
mSelectedItems.put(position, false);
v.setBackgroundColor(Color.WHITE);
} else {
mSelectedItems.put(position, true);
v.setBackgroundColor(Color.BLUE);
}

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

if ( mSelectedItems.get(position, false) ){
holder.itemView.setBackgroundColor(Color.BLUE);
} else {
holder.itemView.setBackgroundColor(Color.WHITE);
}
}


이제 의도한 대로 동작하는 것 같습니다.


하지만 RecyclerView 외부에서 특정 아이템을 선택하는 경우나,

선택된 아이템 전체를 해제(Clear)하는 것은 현재 구조로는 어려워 보이네요.

그리고 선택된 아이템의 배경색 변경도 한곳으로 몰고 싶어집니다.



아이템의 선택상태가 변경되는 시점( 아이템의 클릭이벤트가 발생하거나 아이템 선택/해제 Methord가 호출되었을때)에 선택 상태를 저장(갱신)하고

데이터가 View에 바인딩되는 시점인 onBindViewHolder() 에서 배경색을 변경하도록 처리하면 될것 같습니다.


다만 onBindViewHolder()가 호출되는 시점은 View에 Data를 바인드해야 하는 상황이므로,

Adapter에게 데이터를 다시 바인딩 해야 함을 알려주어야 합니다.

우리가 작성한 코드에서는 setData() 호출을 통해 데이터 셋을 설정하는 코드입니다.


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


RecyclerView Adapter는  표시되는 데이터를 다시 바인딩해야 하는 상황에서 

Adapter에게 알려줄수 있도록 notify~ 류의 메소드를 제공합니다.




데이터가 변경된건 아니지만, 강제로 데이터를 다시 그리도록 처리하는 방법입니다.

일종의 트릭이겠죠?


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


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

if (isItemSelected(position)) {
holder.itemView.setBackgroundColor(Color.BLUE);
} else {
holder.itemView.setBackgroundColor(Color.WHITE);
}
}



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) {
int position = getAdapterPosition();
toggleItemSelected(position);
}
});
}
}


아이템의 선택 상태 관리는 별도의 메소드로 분리했습니다.

private void toggleItemSelected(int position) {

if (mSelectedItems.get(position, false) == true) {
mSelectedItems.delete(position);
notifyItemChanged(position);
} else {
mSelectedItems.put(position, true);
notifyItemChanged(position);
}
}

private boolean isItemSelected(int position) {
return mSelectedItems.get(position, false);
}

public void clearSelectedItem() {
int position;

for (int i = 0; i < mSelectedItems.size(); i++) {
position = mSelectedItems.keyAt(i);
mSelectedItems.put(position, false);
notifyItemChanged(position);
}

mSelectedItems.clear();
}


전체 선택 해제 버튼도 하나 추가했구요.

의도한 대로 잘 동작하는 것 같습니다.




마지막으로 

BackgroundColor 를 변경하는 부분을 XML을 통해 처리 할 수 있습니다.

Xml selector에 select된 상태와 select되지 않은 상태에 적용할 색상 혹은 이미지를 지정하고,

대상 위젯의 BackGround속성으로 해당 selector를 할당 하는 방식입니다.


적용하는 방법은 아래와 같습니다.


"drawable" 폴더 하위에 선택 상태에 따라 표시할 색상을 지정하는 item_selector.xml을 작성합니다.

( 이때 xml 파일이 drawable-hdpi, drawable-hdpi, drawable-v24 등과 같은 사이즈 종속적인 폴더에 들어가지 않도록 주의 해야 합니다.

샘플 작업시, 해당 파일을 drawable-v24 폴더에 생성하는 바람에 테스트한 실제 장비에서는 정상적으로 동작하였지만,

Emulater에서 해당 selector를 적용한 Layout에 대해 아래와 같은 inflate 에러 메시지가 발생하여  앱이 종료된바 있습니다.)

android.view.InflateException: Binary XML file line #14: Binary XML file line #14: Error inflating class <unknown>


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="false">
<color android:color="@color/normal_item_bg" />
</item>
<item android:state_selected="true">
<color android:color="@color/selected_item_bg" />
</item>
</selector>


사용되는 배경색으로 사용되는 색상은 color.xml에 정의 되어 있어야 합니다.

<?xml version="1.0" encoding="utf-8"?>
<resources>

<color name="normal_item_bg">#FFFFFFFF</color>
<color name="selected_item_bg">#FF0000FF</color>

</resources>



아이템을 표시할 Layout의 Root Layout에 Background를 다음과 같이 지정합니다.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/item_selector">



onBindViewHolder의 코드를 다음과 같이 수정하면 선택 상태에 따라 자동으로 배경색이 변경되게 됩니다.

( onCreateViewHolder에서 Inflate된 View와 layout xml의 root LinearLayout이 같은 지는 모르겠으나,

적어도 Layout간에는 상위 Layout의 선택 상태가 하위 Layout에도 반영되는 것으로 보입니다.)

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

holder.itemView.setSelected(isItemSelected(position));
}





롱클릭 이후 데이터를 선택하게 하거나, 단일 선택등의 처리는 조금 응용하면 가능할 것으로 생각됩니다.


<< 전체코드 >


StdRecyclerAdapter.java

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;


private SparseBooleanArray mSelectedItems = new SparseBooleanArray(0);

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

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

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));

holder.itemView.setSelected(isItemSelected(position));
}

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

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) {
int position = getAdapterPosition();
toggleItemSelected(position);

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

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

private void toggleItemSelected(int position) {

if (mSelectedItems.get(position, false) == true) {
mSelectedItems.delete(position);
notifyItemChanged(position);
} else {
mSelectedItems.put(position, true);
notifyItemChanged(position);
}
}

private boolean isItemSelected(int position) {
return mSelectedItems.get(position, false);
}

public void clearSelectedItem() {
int position;

for (int i = 0; i < mSelectedItems.size(); i++) {
position = mSelectedItems.keyAt(i);
mSelectedItems.put(position, false);
notifyItemChanged(position);
}

mSelectedItems.clear();
}
}


MainActivity.java

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

RecyclerView recyclerView;

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

init();
}

private void init(){
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);

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

List<String> dataSet = new ArrayList<String>();

int row = 1;
for ( int i = 0; i < 10 ; i++) {
dataSet.add("<" + row++ + ">" + "C/C++");
dataSet.add("<" + row++ + ">" + "Java");
dataSet.add("<" + row++ + ">" + "Kotlin");
dataSet.add("<" + row++ + ">" + "Python");
dataSet.add("<" + row++ + ">" + "Ruby");
}

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

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

// 아이템간 공백 추가
RecyclerDecoration spaceDecoration = new RecyclerDecoration(20);
recyclerView.addItemDecoration(spaceDecoration);

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

@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();
// Toast.makeText(this, position + " clicked", Toast.LENGTH_SHORT).show();
}

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

}

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="match_parent"
tools:context=".MainActivity">

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

<Button
android:id="@+id/btnClear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Clear Selected Item" />

<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>

</LinearLayout>

list_item.xml

<?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"
android:background="@drawable/item_selector">

<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_margin="8dp"
android:text="TextView" />
</LinearLayout>

item_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="false">
<color android:color="@color/normal_item_bg" />
</item>
<item android:state_selected="true">
<color android:color="@color/selected_item_bg" />
</item>
</selector>

color.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>

<color name="normal_item_bg">#FFFFFFFF</color>
<color name="selected_item_bg">#FF0000FF</color>

</resources>


  1. 감사합니다 2019.05.25 23:11

    ㅜㅜ정말 감사합니다 이틀동안 골머리썩던걸 해결해주셨어요ㅠㅠㅠㅠ 최고세요

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();
}


 


  1. 토닥스 2019.10.31 00:47

    안녕하세요 다름이 아니라 질문이 하나 있습니다!MainActivity에서 new StdRecyclerAdapter(this, this,this);
    에서 세 this가 각각 다른 것을 의미하는 것 같은데 혹시 this의 원리가 무엇인가요!?

    • 좋은향기 2019.10.31 14:33 신고

      1. StdRecyclerAdapter Class의 생성자는 다음과 같이 정의되어 있습니다.

      public StdRecyclerAdapter(Context context
      , OnListItemSelectedInterface listener
      , OnListItemLongSelectedInterface longListener)

      2. MainActivity Class는 다음과 같이 정의되어 있습니다.

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

      3. MainActivity Class는 AppCompatActivity를 상속받고 있으며(AppCompatActivity Class의 상속계층을 따라가 보면 Context를 상속하고 있음을 알 수 있습니다.)
      StdRecyclerAdapter.OnListItemLongSelectedInterface 와 StdRecyclerAdapter.OnListItemSelectedInterface를 구현하고 있습니다.

      4. 결론적으로 아래와 같이 해석됩니다.
      new StdRecyclerAdapter((Context)this
      , (StdRecyclerAdapter.OnListItemLongSelectedInterface)this
      , ( StdRecyclerAdapter.OnListItemSelectedInterface)this)

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

영진닷컴

samsung | SM-G920S | Normal program | Center-weighted average | 1/10sec | F/1.9 | 0.00 EV | 4.3mm | ISO-200 | Flash did not fire | 2019:03:11 19:14:11



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


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


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

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


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

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

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



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

미리암 프리스

박지희 역

비즈니스북스


samsung | SM-G920S | Normal program | Center-weighted average | 1/20sec | F/1.9 | 0.00 EV | 4.3mm | ISO-200 | Flash did not fire | 2019:03:15 09:57:06


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


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

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


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

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

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


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

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


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

연습이 필요할 것 같다.



+ Recent posts