RecyclerView의 행 내부에서 버튼 클릭 처리
행 클릭을 처리하기 위해 다음 코드를 사용하고 있습니다. ( 출처 )
static class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {
private GestureDetector gestureDetector;
private ClickListener clickListener;
public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
this.clickListener = clickListener;
gestureDetector = 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 && clickListener != null) {
clickListener.onLongClick(child, recyclerView.getChildPosition(child));
}
}
});
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
clickListener.onClick(child, rv.getChildPosition(child));
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
}
그러나 이것은 각 행에 삭제 버튼을 말하고 싶다면 작동합니다. 나는 이것을 구현하는 방법을 잘 모르겠습니다.
OnClick 리스너를 연결하여 작동하는 버튼을 삭제했지만 (행 삭제) 전체 행에서 onclick도 발생합니다.
누구든지 단일 버튼을 클릭하면 전체 행 클릭을 피하는 방법에 대해 나를 도울 수 있습니까?
감사.
이것이 recyclerView 내에서 여러 onClick 이벤트를 처리하는 방법입니다.
편집 : 콜백을 포함하도록 업데이트되었습니다 (다른 의견에서 언급했듯이). 내가 사용하고 WeakReference
의를 ViewHolder
메모리 누수의 가능성을 제거 할 수 있습니다.
인터페이스 정의 :
public interface ClickListener {
void onPositionClicked(int position);
void onLongClicked(int position);
}
그런 다음 어댑터 :
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private final ClickListener listener;
private final List<MyItems> itemsList;
public MyAdapter(List<MyItems> itemsList, ClickListener listener) {
this.listener = listener;
this.itemsList = itemsList;
}
@Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.my_row_layout), parent, false), listener);
}
@Override public void onBindViewHolder(MyViewHolder holder, int position) {
// bind layout and data etc..
}
@Override public int getItemCount() {
return itemsList.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
private ImageView iconImageView;
private TextView iconTextView;
private WeakReference<ClickListener> listenerRef;
public MyViewHolder(final View itemView, ClickListener listener) {
super(itemView);
listenerRef = new WeakReference<>(listener);
iconImageView = (ImageView) itemView.findViewById(R.id.myRecyclerImageView);
iconTextView = (TextView) itemView.findViewById(R.id.myRecyclerTextView);
itemView.setOnClickListener(this);
iconTextView.setOnClickListener(this);
iconImageView.setOnLongClickListener(this);
}
// onClick Listener for view
@Override
public void onClick(View v) {
if (v.getId() == iconTextView.getId()) {
Toast.makeText(v.getContext(), "ITEM PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(v.getContext(), "ROW PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
}
listenerRef.get().onPositionClicked(getAdapterPosition());
}
//onLongClickListener for view
@Override
public boolean onLongClick(View v) {
final AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
builder.setTitle("Hello Dialog")
.setMessage("LONG CLICK DIALOG WINDOW FOR ICON " + String.valueOf(getAdapterPosition()))
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.create().show();
listenerRef.get().onLongClicked(getAdapterPosition());
return true;
}
}
}
그런 다음 활동 / 조각에서-구현할 수있는 것은 무엇이든 : Clicklistener
-또는 원하는 경우 익명 클래스 :
MyAdapter adapter = new MyAdapter(myItems, new ClickListener() {
@Override public void onPositionClicked(int position) {
// callback performed on click
}
@Override public void onLongClicked(int position) {
// callback performed on click
}
});
클릭 된 항목을 확인하려면 뷰 ID ievgetId () == whateverItem.getId ()와 일치합니다.
이 방법이 도움이되기를 바랍니다.
일반적으로 다음과 같습니다.
- 여러 개의 버튼이 있기 때문에 여러 리스너를 사용해야합니다.
- 내 로직이 어댑터 나 뷰 홀더가 아닌 액티비티에 있기를 원합니다.
따라서 @ mark-keen의 대답은 잘 작동하지만 인터페이스가 있으면 더 많은 유연성을 제공합니다.
public static class MyViewHolder extends RecyclerView.ViewHolder {
public ImageView iconImageView;
public TextView iconTextView;
public MyViewHolder(final View itemView) {
super(itemView);
iconImageView = (ImageView) itemView.findViewById(R.id.myRecyclerImageView);
iconTextView = (TextView) itemView.findViewById(R.id.myRecyclerTextView);
iconTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onClickListener.iconTextViewOnClick(v, getAdapterPosition());
}
});
iconImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onClickListener.iconImageViewOnClick(v, getAdapterPosition());
}
});
}
}
onClickListener가 어댑터에 정의 된 위치 :
public MyAdapterListener onClickListener;
public interface MyAdapterListener {
void iconTextViewOnClick(View v, int position);
void iconImageViewOnClick(View v, int position);
}
그리고 아마도 생성자를 통해 설정됩니다.
public MyAdapter(ArrayList<MyListItems> newRows, MyAdapterListener listener) {
rows = newRows;
onClickListener = listener;
}
그런 다음 Activity 또는 RecyclerView가 사용되는 모든 곳에서 이벤트를 처리 할 수 있습니다.
mAdapter = new MyAdapter(mRows, new MyAdapter.MyAdapterListener() {
@Override
public void iconTextViewOnClick(View v, int position) {
Log.d(TAG, "iconTextViewOnClick at position "+position);
}
@Override
public void iconImageViewOnClick(View v, int position) {
Log.d(TAG, "iconImageViewOnClick at position "+position);
}
});
mRecycler.setAdapter(mAdapter);
나중에 가비지 수집해야하는 추가 개체 (예 : 리스너)를 생성 하지 않고 어댑터 클래스 내부에 뷰 홀더를 중첩 할 필요 가없는 솔루션을 원했습니다 .
에서 ViewHolder
클래스
private static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private final TextView ....// declare the fields in your view
private ClickHandler ClickHandler;
public MyHolder(final View itemView) {
super(itemView);
nameField = (TextView) itemView.findViewById(R.id.name);
//find other fields here...
Button myButton = (Button) itemView.findViewById(R.id.my_button);
myButton.setOnClickListener(this);
}
...
@Override
public void onClick(final View view) {
if (clickHandler != null) {
clickHandler.onMyButtonClicked(getAdapterPosition());
}
}
주의 할 점 : ClickHandler
인터페이스가 정의되었지만 여기서 초기화되지 않았으므로 onClick
메서드가 초기화되었다는 가정이 없습니다 .
ClickHandler
인터페이스는 다음과 같습니다 :
private interface ClickHandler {
void onMyButtonClicked(final int position);
}
어댑터에서 생성자에 'ClickHandler'의 인스턴스를 설정하고을 재정 onBindViewHolder
의하여 뷰 홀더에서 'clickHandler'를 초기화합니다.
private class MyAdapter extends ...{
private final ClickHandler clickHandler;
public MyAdapter(final ClickHandler clickHandler) {
super(...);
this.clickHandler = clickHandler;
}
@Override
public void onBindViewHolder(final MyViewHolder viewHolder, final int position) {
super.onBindViewHolder(viewHolder, position);
viewHolder.clickHandler = this.clickHandler;
}
참고 : viewHolder.clickHandler가 잠재적으로 똑같은 값으로 여러 번 설정된다는 것을 알고 있지만 이것은 null 및 분기를 확인하는 것보다 저렴하며 메모리 비용이 없으며 추가 명령 만 있습니다.
마지막으로 어댑터를 만들 때 다음 ClickHandler
과 같이 인스턴스를 생성자 에 전달해야합니다 .
adapter = new MyAdapter(new ClickHandler() {
@Override
public void onMyButtonClicked(final int position) {
final MyModel model = adapter.getItem(position);
//do something with the model where the button was clicked
}
});
참고 adapter
멤버 변수는 여기가 아닌 지역 변수
이미 리사이클 러 터치 리스너가 있고 뷰 홀더에서 버튼 터치 이벤트를 개별적으로 처리하는 대신 모든 터치 이벤트를 처리하려는 경우 다른 솔루션을 추가하고 싶었습니다. 이 조정 된 버전의 클래스가 수행하는 핵심 작업은 항목 컨테이너가 아닌 탭할 때 onItemClick () 콜백에서 버튼보기를 반환하는 것입니다. 그런 다음 뷰가 버튼인지 테스트하고 다른 작업을 수행 할 수 있습니다. 버튼을 길게 두드리는 것은 전체 행을 길게 두드리는 것으로 해석됩니다.
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener
{
public static interface OnItemClickListener
{
public void onItemClick(View view, int position);
public void onItemLongClick(View view, int position);
}
private OnItemClickListener mListener;
private 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)
{
// Important: x and y are translated coordinates here
final ViewGroup childViewGroup = (ViewGroup) recyclerView.findChildViewUnder(e.getX(), e.getY());
if (childViewGroup != null && mListener != null) {
final List<View> viewHierarchy = new ArrayList<View>();
// Important: x and y are raw screen coordinates here
getViewHierarchyUnderChild(childViewGroup, e.getRawX(), e.getRawY(), viewHierarchy);
View touchedView = childViewGroup;
if (viewHierarchy.size() > 0) {
touchedView = viewHierarchy.get(0);
}
mListener.onItemClick(touchedView, recyclerView.getChildPosition(childViewGroup));
return true;
}
return false;
}
@Override
public void onLongPress(MotionEvent e)
{
View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
if(childView != null && mListener != null)
{
mListener.onItemLongClick(childView, recyclerView.getChildPosition(childView));
}
}
});
}
public void getViewHierarchyUnderChild(ViewGroup root, float x, float y, List<View> viewHierarchy) {
int[] location = new int[2];
final int childCount = root.getChildCount();
for (int i = 0; i < childCount; ++i) {
final View child = root.getChildAt(i);
child.getLocationOnScreen(location);
final int childLeft = location[0], childRight = childLeft + child.getWidth();
final int childTop = location[1], childBottom = childTop + child.getHeight();
if (child.isShown() && x >= childLeft && x <= childRight && y >= childTop && y <= childBottom) {
viewHierarchy.add(0, child);
}
if (child instanceof ViewGroup) {
getViewHierarchyUnderChild((ViewGroup) child, x, y, viewHierarchy);
}
}
}
@Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e)
{
mGestureDetector.onTouchEvent(e);
return false;
}
@Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent){}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
그런 다음 활동 / 조각에서 사용 :
recyclerView.addOnItemTouchListener(createItemClickListener(recyclerView));
public RecyclerItemClickListener createItemClickListener(final RecyclerView recyclerView) {
return new RecyclerItemClickListener (context, recyclerView, new RecyclerItemClickListener.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
if (view instanceof AppCompatButton) {
// ... tapped on the button, so go do something
} else {
// ... tapped on the item container (row), so do something different
}
}
@Override
public void onItemLongClick(View view, int position) {
}
});
}
onInterceptTouchEvent()
클릭 이벤트를 처리 할 때 내부에서 true를 반환해야합니다 .
먼저 비슷한 항목이 있는지 확인하고 크기가 0 인 컬렉션을 가져 오면 새 쿼리를 시작하여 저장할 수 있습니다.
또는
더 전문적이고 빠른 방법. 클라우드 트리거 생성 (저장 전)
check out this answer https://stackoverflow.com/a/35194514/1388852
Just put an override method named getItemId Get it by right click>generate>override methods>getItemId Put this method in the Adapter class
참고URL : https://stackoverflow.com/questions/30284067/handle-button-click-inside-a-row-in-recyclerview
'developer tip' 카테고리의 다른 글
MD5는 128 비트인데 왜 32 자입니까? (0) | 2020.10.16 |
---|---|
자바 스크립트를 사용하여 인라인 스타일 추가 (0) | 2020.10.16 |
JavaScript : 문자열에서 마지막 텍스트 교체 (0) | 2020.10.16 |
System.Windows.Media 네임 스페이스를 찾을 수 없습니까? (0) | 2020.10.16 |
여러 언어로 된 정규식에 대한 단위 테스트는 어디에서 찾을 수 있습니까? (0) | 2020.10.15 |