본문 바로가기

안드로이드/코드

[안드로이드] 리싸이클러뷰 아이템 정보 수정, 스와이프 이벤트, 커스텀 다이얼로그 - RecyclerView, ItemTouchHelper, Dialog

반응형

 

 

 

리싸이클러뷰3_1

 

 

리싸이클러뷰의 아이템을 스와이프 했을때 이벤트를 만드는 예제를 만들어보겠습니다.

 

이전에 포스팅했던 리싸이클러뷰와 ItemTouchHelper를 활용해서 만들었습니다.

링크를 참고하시면 이해하시는데 더 도움이 되실 것 같습니다.

 

 

 

1. list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="wrap_content"
    android:orientation="horizontal">
    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:cardBackgroundColor="#FFFFFF"
        app:cardUseCompatPadding="true">

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

            <ImageView
                android:id="@+id/list_image"
                android:layout_width="70dp"
                android:layout_height="70dp"
                android:padding="5dp"
                app:srcCompat="@mipmap/ic_launcher" />

            <TableLayout
                android:gravity="center"
                android:stretchColumns="1"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_weight="1">

                <TableRow>

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="left"
                        android:layout_marginLeft="10dp"
                        android:textColor="@android:color/black"
                        android:text="이름"
                        android:textSize="20sp" />

                    <TextView
                        android:id="@+id/list_name"
                        android:textColor="@android:color/black"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center"
                        android:textSize="20sp" />

                </TableRow>


                <TableRow>

                    <TextView
                        android:textColor="@android:color/black"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="left"
                        android:layout_marginLeft="10dp"
                        android:text="나이"
                        android:textSize="20sp" />

                    <TextView
                        android:id="@+id/list_age"
                        android:textColor="@android:color/black"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center"
                        android:textSize="20sp" />
                </TableRow>


            </TableLayout>


        </LinearLayout>

    </androidx.cardview.widget.CardView>



</FrameLayout>

 

 

2. customdialog.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="#FFFFFF"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_margin="10dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:gravity="center"
            android:textColor="@android:color/black"
            android:textSize="20sp"
            android:text="이름"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="3"/>
        <EditText
            android:id="@+id/mod_name"
            android:selectAllOnFocus="true"
            android:gravity="center"
            android:imeOptions="actionNext"
            android:inputType="text"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="7"/>

    </LinearLayout>
    <LinearLayout
        android:layout_margin="10dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:gravity="center"
            android:textColor="@android:color/black"
            android:textSize="20sp"
            android:text="나이"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="3"/>
        <EditText
            android:id="@+id/mod_age"
            android:selectAllOnFocus="true"
            android:gravity="center"
            android:inputType="number"
            android:imeOptions="actionDone"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="7"/>

    </LinearLayout>

    <Button
        android:id="@+id/mod_bt"
        android:layout_margin="10dp"
        android:text="수정"
        android:textSize="20sp"
        android:textColor="@android:color/black"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />


</LinearLayout>

 

참고

android:selectAllOnFocus="true"

true로 설정을 해주면 EditText를 선택했을때 EditText의 텍스트가 모두 선택이 된 상태가됩니다.

 

리싸이클러뷰3_2

 

3. activity.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">


    <androidx.recyclerview.widget.RecyclerView
        android:layout_marginTop="10dp"
        android:id="@+id/rv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>



</LinearLayout>

 

4. Person.java

public class Person {
    int image;
    String name;
    int age;

    public Person(int image, String name, int age){
        this.image = image;
        this.name = name;
        this.age = age;
    }

    public int getImage() {
        return image;
    }

    public void setImage(int image) {
        this.image = image;
    }



    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

 

 

5. ItemTouchHelperListener.interface

import androidx.recyclerview.widget.RecyclerView;

public interface ItemTouchHelperListener {
    boolean onItemMove(int from_position, int to_position);
    void onItemSwipe(int position);
    void onLeftClick(int position, RecyclerView.ViewHolder viewHolder);
    void onRightClick(int position, RecyclerView.ViewHolder viewHolder);
}

 

 

6. ItemTouchHelperCallback.java

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;


enum ButtonsState{
    GONE,
    LEFT_VISIBLE,
    RIGHT_VISIBLE
}

public class ItemTouchHelperCallback extends ItemTouchHelper.Callback {


    private ItemTouchHelperListener listener;
    private boolean swipeBack = false;
    private ButtonsState buttonsShowedState = ButtonsState.GONE;
    private static final float buttonWidth = 115;
    private RectF buttonInstance = null;
    private RecyclerView.ViewHolder currenrtItemViewHolder = null;

    public ItemTouchHelperCallback(ItemTouchHelperListener listener) {
        this.listener = listener;
    }


    @Override
    public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        int drag_flags = ItemTouchHelper.UP|ItemTouchHelper.DOWN;
        int swipe_flags = ItemTouchHelper.START|ItemTouchHelper.END;
        return makeMovementFlags(drag_flags,swipe_flags);
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {

        return listener.onItemMove(viewHolder.getAdapterPosition(),target.getAdapterPosition());
    }

    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
        listener.onItemSwipe(viewHolder.getAdapterPosition());
    }

    //아이템을 터치하거나 스와이프하거나 뷰에 변화가 생길경우 불러오는 함수
    @Override
    public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        //아이템이 스와이프 됐을경우 버튼을 그려주기 위해서 스와이프가 됐는지 확인
        if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE){
            if(buttonsShowedState != ButtonsState.GONE){
                if(buttonsShowedState == ButtonsState.LEFT_VISIBLE) dX = Math.max(dX, buttonWidth);
                if(buttonsShowedState == ButtonsState.RIGHT_VISIBLE) dX = Math.min(dX, -buttonWidth);
                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);

            }else{
                setTouchListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            }

            if(buttonsShowedState == ButtonsState.GONE){
                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            }

        }
        currenrtItemViewHolder = viewHolder;

        //버튼을 그려주는 함수
        drawButtons(c, currenrtItemViewHolder);


    }

    private void drawButtons(Canvas c, RecyclerView.ViewHolder viewHolder){

        float buttonWidthWithOutPadding = buttonWidth - 10;
        float corners = 5;

        View itemView = viewHolder.itemView;
        Paint p = new Paint();

        buttonInstance = null;


        //오른쪽으로 스와이프 했을때 (왼쪽에 버튼이 보여지게 될 경우)
        if(buttonsShowedState == ButtonsState.LEFT_VISIBLE){
            RectF leftButton = new RectF(itemView.getLeft() + 10, itemView.getTop() + 10, itemView.getLeft() + buttonWidthWithOutPadding,
                    itemView.getBottom() - 10);
            p.setColor(Color.BLUE);
            c.drawRoundRect(leftButton, corners, corners, p);
            drawText("수정", c, leftButton, p);
            buttonInstance = leftButton;

        //왼쪽으로 스와이프 했을때 (오른쪽에 버튼이 보여지게 될 경우)
        }else if(buttonsShowedState == ButtonsState.RIGHT_VISIBLE){
            RectF rightButton = new RectF(itemView.getRight() - buttonWidthWithOutPadding, itemView.getTop() + 10, itemView.getRight() -10,
                    itemView.getBottom() - 10);
            p.setColor(Color.RED);
            c.drawRoundRect(rightButton, corners, corners, p);
            drawText("삭제", c, rightButton, p);

            buttonInstance = rightButton;
        }

    }

    //버튼의 텍스트 그려주기
    private void drawText(String text, Canvas c, RectF button, Paint p){
        float textSize = 25;
        p.setColor(Color.WHITE);
        p.setAntiAlias(true);
        p.setTextSize(textSize);

        float textWidth = p.measureText(text);
        c.drawText(text, button.centerX() - (textWidth/2), button.centerY() + (textSize/2), p);
    }

    @Override
    public int convertToAbsoluteDirection(int flags, int layoutDirection) {
        if(swipeBack){
            swipeBack = false;
            return 0;
        }
        return super.convertToAbsoluteDirection(flags, layoutDirection);
    }

    
    private void setTouchListener(final Canvas c, final RecyclerView recyclerView,
                                  final RecyclerView.ViewHolder viewHolder,
                                  final float dX, final float dY, final int actionState,
                                  final boolean isCurrentlyActive){
        recyclerView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                swipeBack = event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP;
                if(swipeBack){
                    if(dX < -buttonWidth) buttonsShowedState = ButtonsState.RIGHT_VISIBLE;

                    else if(dX > buttonWidth) buttonsShowedState = ButtonsState.LEFT_VISIBLE;




                    if(buttonsShowedState != ButtonsState.GONE){
                        setTouchDownListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
                        setItemsClickable(recyclerView, false);
                    }
                }
                return false;
            }
        });
    }

    private void setTouchDownListener(final Canvas c, final RecyclerView recyclerView
            , final RecyclerView.ViewHolder viewHolder, final float dX, final float dY
            , final int actionState, final boolean isCurrentlyActive){
        recyclerView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if(event.getAction() == MotionEvent.ACTION_DOWN){
                    setTouchUpListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
                }
                return false;
            }
        });
    }

    private void setTouchUpListener(final Canvas c, final RecyclerView recyclerView
            , final RecyclerView.ViewHolder viewHolder, final float dX, final float dY
            , final int actionState, final boolean isCurrentlyActive){

        recyclerView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                ItemTouchHelperCallback.super.onChildDraw(c, recyclerView, viewHolder, 0F, dY, actionState, isCurrentlyActive);
                recyclerView.setOnTouchListener(new View.OnTouchListener() {
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        return false;
                    }
                });

                setItemsClickable(recyclerView, true);
                swipeBack = false;

                if(listener != null && buttonInstance != null && buttonInstance.contains(event.getX(), event.getY())){
                    if(buttonsShowedState == ButtonsState.LEFT_VISIBLE){
                        listener.onLeftClick(viewHolder.getAdapterPosition(), viewHolder);
                    }else if(buttonsShowedState == ButtonsState.RIGHT_VISIBLE){
                        listener.onRightClick(viewHolder.getAdapterPosition(), viewHolder);
                    }
                }

                buttonsShowedState = ButtonsState.GONE;
                currenrtItemViewHolder = null;
                return false;
            }
        });
    }

    private void setItemsClickable(RecyclerView recyclerView, boolean isClickable){
        for(int i = 0; i < recyclerView.getChildCount(); i++){
            recyclerView.getChildAt(i).setClickable(isClickable);
        }
    }

}

 

버튼을 띄워주는 이벤트는 단시간에 이해하기 힘들기 때문에 링크를 통해서 확인하시고 천천히 직접 하나씩 코딩해보시면서 이해하시면 도움이 되실것 같습니다.

 

 

 

7. OnDialogListener.interface

public interface OnDialogListener {
    void onFinish(int position, Person person);
}

 

다이얼로그가 종료될때 다이얼로그의 EditText에 입력한 값을 받아오기 위한 인터페이스입니다.

 

 

8. CustomDialog.java

import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;

public class CustomDialog extends Dialog {
    private OnDialogListener listener;
    private Context context;
    private Button mod_bt;
    private EditText mod_name, mod_age;
    private String name;
    private int image,age;
    public CustomDialog(Context context,final int position, Person person){
        super(context);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        setContentView(R.layout.customdialog);

        name = person.getName();
        age = person.getAge();
        image = person.getImage();

        //이름, 나이 EditText에 값 채우기
        mod_name = findViewById(R.id.mod_name);
        mod_name.setText(name);

        mod_age = findViewById(R.id.mod_age);
        mod_age.setText(String.valueOf(age));

        mod_bt = findViewById(R.id.mod_bt);
        mod_bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(listener!=null){
                    //EditText의 수정된 값 가져오기
                    String name = mod_name.getText().toString();
                    int age = Integer.parseInt(mod_age.getText().toString());
                    Person person = new Person(image,name, age);

                    //Listener를 통해서 person객체 전달
                    listener.onFinish(position, person);

                    //다이얼로그 종료
                    dismiss();
                }
            }
        });
    }

    public void setDialogListener(OnDialogListener listener){
        this.listener = listener;
    }



}

 

 

9. ListAdapter.java

import android.app.AlertDialog;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;

public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ItemViewHolder>
            implements ItemTouchHelperListener, OnDialogListener{

    ArrayList<Person> items = new ArrayList<>();
    Context context;
    public ListAdapter(Context context){
        this.context = context;
    }


    @NonNull
    @Override
    public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        //LayoutInflater를 이용해서 원하는 레이아웃을 띄워줌
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.list_item, parent, false);
        return new ItemViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ItemViewHolder holder, int position) {
        //ItemViewHolder가 생성되고 넣어야할 코드들을 넣어준다.
        holder.onBind(items.get(position));
    }

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

    public void addItem(Person person){
        items.add(person);
    }



    @Override
    public boolean onItemMove(int from_position, int to_position) {
        //이동할 객체 저장
        Person person = items.get(from_position);
        //이동할 객체 삭제
        items.remove(from_position);
        //이동하고 싶은 position에 추가
        items.add(to_position,person);

        //Adapter에 데이터 이동알림
        notifyItemMoved(from_position,to_position);
        return true;
    }

    @Override
    public void onItemSwipe(int position) {
        items.remove(position);
        notifyItemRemoved(position);
    }


    //왼쪽 버튼 누르면 수정할 다이얼로그 띄우기
    @Override
    public void onLeftClick(int position, RecyclerView.ViewHolder viewHolder) {
        //수정 버튼 클릭시 다이얼로그 생성
        CustomDialog dialog = new CustomDialog(context, position, items.get(position));

        //화면 사이즈 구하기
        DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics();
        int width = dm.widthPixels;
        int height = dm.heightPixels;

        //다이얼로그 사이즈 세팅
        WindowManager.LayoutParams wm = dialog.getWindow().getAttributes();
        wm.copyFrom(dialog.getWindow().getAttributes());
        wm.width = (int) (width * 0.7);
        wm.height = height/2;

        //다이얼로그 Listener 세팅
        dialog.setDialogListener(this);

        //다이얼로그 띄우기
        dialog.show();
    }


    //오른쪽 버튼 누르면 아이템 삭제
    @Override
    public void onRightClick(int position, RecyclerView.ViewHolder viewHolder) {
        items.remove(position);
        notifyItemRemoved(position);
    }

    @Override
    public void onFinish(int position, Person person) {
        items.set(position,person);
        notifyItemChanged(position);
    }


    class ItemViewHolder extends RecyclerView.ViewHolder {

        TextView list_name,list_age;
        ImageView list_image;



        public ItemViewHolder(View itemView) {
            super(itemView);
            list_name = itemView.findViewById(R.id.list_name);
            list_age = itemView.findViewById(R.id.list_age);
            list_image = itemView.findViewById(R.id.list_image);
        }

        public void onBind(Person person) {
            list_name.setText(person.getName());
            list_age.setText(String.valueOf(person.getAge()));
            list_image.setImageResource(person.getImage());
        }
    }


}

 

 

10. MainActivity.java

package com.everyshare.recyclerviewtest;

import android.graphics.Canvas;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

public class MainActivity extends AppCompatActivity {
    RecyclerView rv;
    ListAdapter adapter;
    ItemTouchHelper helper;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);





        rv = findViewById(R.id.rv);
        //RecyclerView의 레이아웃 방식을 지정
        LinearLayoutManager manager = new LinearLayoutManager(this);
        manager.setOrientation(LinearLayoutManager.VERTICAL);
        rv.setLayoutManager(manager);

        //RecyclerView의 Adapter 세팅
        adapter = new ListAdapter(this);
        rv.setAdapter(adapter);

        //ItemTouchHelper 생성
        helper = new ItemTouchHelper(new ItemTouchHelperCallback(adapter));
        //RecyclerView에 ItemTouchHelper 붙이기
        helper.attachToRecyclerView(rv);


        //Adapter에 데이터 추가
        Person person1 = new Person(R.drawable.image1,"파이리",1);
        Person person2 = new Person(R.drawable.image2,"피카츄",2);
        Person person3 = new Person(R.drawable.image3,"꼬부기",3);
        Person person4 = new Person(R.drawable.image4,"이상해씨",4);
        adapter.addItem(person1);
        adapter.addItem(person2);
        adapter.addItem(person3);
        adapter.addItem(person4);
    }

    private void setUpRecyclerView(){
        rv.addItemDecoration(new RecyclerView.ItemDecoration() {
            @Override
            public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
                helper.onDraw(c,parent, state);
            }
        });
    }
}

 

끝까지 보시고 이해가 안되시는 부분이 있으시다면 댓글을 남겨주세요.

명확한 답변을 드릴지는 모르겠지만 아는선에서 도움이 되는 답변을 드리도록 하겠습니다.

감사합니다.

반응형