Development Tip

ListView 항목 스 와이프 오른쪽에서 왼쪽으로 삭제 버튼 표시

yourdevel 2020. 11. 8. 11:21
반응형

ListView 항목 스 와이프 오른쪽에서 왼쪽으로 삭제 버튼 표시


데이터베이스에서 선택한 단어 목록을 보여주는 사용자 지정 ListView가 있습니다. 이 목록보기 항목을 스 와이프하면 아래 이미지와 같은 삭제 버튼을 표시하고 싶습니다. 그리고 그 버튼을 누르면 데이터베이스에서 삭제되고 목록보기를 새로 고칩니다. 미디엄목록보기 항목 스 와이프

이 샘플 코드는 이미 여기 에서 살펴 보았습니다 . 그러나 여전히 작동하지 않습니다.


편집 : 다른 옵션 사이에 문제를 해결할 수있는 멋진 라이브러리가 있습니다 : https://github.com/daimajia/AndroidSwipeLayout


나는 Google을 많이 검색했으며 가장 적합한 프로젝트는 github 의 swipmenulistview https://github.com/baoyongzhang/SwipeMenuListView 입니다.


나는 그것을 할 좋은 라이브러리를 찾는 것과 같은 문제를 겪었습니다. 결국 그렇게 할 수있는 라이브러리를 만들었습니다. SwipeRevealLayout

gradle 파일에서 :

dependencies {
    compile 'com.chauthai.swipereveallayout:swipe-reveal-layout:1.4.0'
}

XML 파일에서 :

<com.chauthai.swipereveallayout.SwipeRevealLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:mode="same_level"
    app:dragEdge="left">

    <!-- Your secondary layout here -->
    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent" />

    <!-- Your main layout here -->
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</com.chauthai.swipereveallayout.SwipeRevealLayout>

그런 다음 어댑터 파일에서 :

public class Adapter extends RecyclerView.Adapter {
  // This object helps you save/restore the open/close state of each view
  private final ViewBinderHelper viewBinderHelper = new ViewBinderHelper();

  @Override
  public void onBindViewHolder(ViewHolder holder, int position) {
    // get your data object first.
    YourDataObject dataObject = mDataSet.get(position); 

    // Save/restore the open/close state.
    // You need to provide a String id which uniquely defines the data object.
    viewBinderHelper.bind(holder.swipeRevealLayout, dataObject.getId()); 

    // do your regular binding stuff here
  }
}

오른쪽에서 왼쪽으로 스 와이프하면 삭제 버튼이 표시되는 github에 데모를 만들었습니다. 그러면 ListView에서 항목을 삭제하고 ListView를 업데이트 할 수 있습니다.


방금 ListItem에서 ViewSwitcher를 사용하여 작업했습니다.

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="horizontal" >

<ViewSwitcher
    android:id="@+id/list_switcher"
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    android:inAnimation="@android:anim/slide_in_left"
    android:outAnimation="@android:anim/slide_out_right"
    android:measureAllChildren="false" >
    <TextView
        android:id="@+id/tv_item_name"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_gravity="center_vertical"
        android:maxHeight="50dp"
        android:paddingLeft="10dp" />

    <LinearLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:clickable="false"
        android:gravity="center"
        >

    <Button
        android:id="@+id/b_edit_in_list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Edit"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"

         />
    <Button
        android:id="@+id/b_delete_in_list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Delete"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:background="@android:color/holo_red_dark"
        />
    </LinearLayout>
</ViewSwitcher>

ListAdapter에서 : getView () 메서드의 편집 및 삭제 버튼에 대한 OnclickListeners를 구현합니다. 여기서 잡는 것은 onClick 메서드 내에서 클릭 된 ListItem의 위치를 ​​가져 오는 것입니다. 이를 위해 setTag () 및 getTag () 메소드가 사용됩니다.

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub
    final ViewHolder viewHolder;
    if (convertView == null) {
        viewHolder = new ViewHolder();
        convertView = mInflater.inflate(R.layout.list_item, null);
        viewHolder.viewSwitcher=(ViewSwitcher)convertView.findViewById(R.id.list_switcher);
        viewHolder.itemName = (TextView) convertView
                .findViewById(R.id.tv_item_name);
        viewHolder.deleteitem=(Button)convertView.findViewById(R.id.b_delete_in_list);
        viewHolder.deleteItem.setTag(position);
        viewHolder.editItem=(Button)convertView.findViewById(R.id.b_edit_in_list);
        viewHolder.editItem.setTag(position);
        viewHolder.deleteItem.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                fragment.deleteItemList((Integer)v.getTag());
            }
        });

        viewHolder.editItem.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                fragment.editItemList(position);
            }
        });
        convertView.setTag(viewHolder);
} else {
        viewHolder = (ViewHolder) convertView.getTag();
    }

    viewHolder.itemName.setText(itemlist[position]);
return convertView;  
}

조각에서 제스처 리스너를 추가하여 플링 제스처를 감지합니다.

public class MyGestureListener extends SimpleOnGestureListener {
    private ListView list;

    public MyGestureListener(ListView list) {
        this.list = list;
    }

    // CONDITIONS ARE TYPICALLY VELOCITY OR DISTANCE

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
            float velocityY) {
        // if (INSERT_CONDITIONS_HERE)

        ltor=(e2.getX()-e1.getX()>DELTA_X);
        if (showDeleteButton(e1))
        {
            return true;
        }
        return super.onFling(e1, e2, velocityX, velocityY);
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2,
            float distanceX, float distanceY) {
        // TODO Auto-generated method stub
        return super.onScroll(e1, e2, distanceX, distanceY);
    }

    private boolean showDeleteButton(MotionEvent e1) {
        int pos = list.pointToPosition((int) e1.getX(), (int) e1.getY());
        return showDeleteButton(pos);
    }

    private boolean showDeleteButton(int pos) {

        View child = list.getChildAt(pos);
        if (child != null) {
            Button delete = (Button) child
                    .findViewById(R.id.b_edit_in_list);
            ViewSwitcher viewSwitcher = (ViewSwitcher) child
                    .findViewById(R.id.host_list_switcher);
            TextView hostName = (TextView) child
                    .findViewById(R.id.tv_host_name);
            if (delete != null) {
                    viewSwitcher.setInAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.slide_in_left));
                    viewSwitcher.setOutAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.slide_out_right));
                }

                viewSwitcher.showNext();
                // frameLayout.setVisibility(View.VISIBLE);
            }
            return true;
        }
        return false;
    }
}

Fragment의 onCreateView 메서드에서

GestureDetector gestureDetector = new GestureDetector(getActivity(),
            new MyGestureListener(hostList));
    hostList.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // TODO Auto-generated method stub
            if (gestureDetector.onTouchEvent(event)) {
                return true;
            } else {
                return false;
            }

        }
    });

이것은 나를 위해 일했습니다. 더 다듬어야합니다.


링크는 매우 멋지고 간단했습니다. 그것의 잘 작동 ... u는 잘 작동하는 라이브러리를 원하지 않습니다. 여기를 클릭 하세요

OnTouchListener gestureListener = new View.OnTouchListener() {
    private int padding = 0;
    private int initialx = 0;
    private int currentx = 0;
    private  ViewHolder viewHolder;

    public boolean onTouch(View v, MotionEvent event) {
        if ( event.getAction() == MotionEvent.ACTION_DOWN) {
            padding = 0;
            initialx = (int) event.getX();
            currentx = (int) event.getX();
            viewHolder = ((ViewHolder) v.getTag());
        }
        if ( event.getAction() == MotionEvent.ACTION_MOVE) {
            currentx = (int) event.getX();
            padding = currentx - initialx;
        }       
        if ( event.getAction() == MotionEvent.ACTION_UP || 
                     event.getAction() == MotionEvent.ACTION_CANCEL) {
            padding = 0;
            initialx = 0;
            currentx = 0;
        }
        if(viewHolder != null) {
            if(padding == 0) {
                v.setBackgroundColor(0xFF000000 );  
                if(viewHolder.running)
                    v.setBackgroundColor(0xFF058805);
            }
            if(padding > 75) {
                viewHolder.running = true;
                v.setBackgroundColor(0xFF00FF00 );  
                viewHolder.icon.setImageResource(R.drawable.clock_running);
            }
            if(padding < -75) {
                viewHolder.running = false;
                v.setBackgroundColor(0xFFFF0000 );  
            }

            v.setPadding(padding, 0,0, 0);
        }

        return true;
    }
};

스 와이프-삭제 및 드래그를 결합하여 항목을 다시 정렬하는 목록보기를 보여주는 앱을 사용할 수 있습니다. 이 코드는 스 와이프하여 삭제를위한 Chet Haase의 코드와 드래그하여 재정렬하기위한 Daniel Olshansky의 코드를 기반으로합니다.

Chet의 코드는 항목을 즉시 삭제합니다. 스 와이프하면 항목이 삭제되었음을 나타내는 하단보기가 표시되지만 사용자가 삭제를 취소 할 수있는 실행 취소 버튼을 제공하는 Gmail과 더 비슷하게 기능을 개선했습니다. Chet의 코드에도 버그가 있습니다. 목록보기에있는 항목이 목록보기의 높이보다 적고 마지막 항목을 삭제하면 마지막 항목이 삭제되지 않은 것처럼 보입니다. 이것은 내 코드에서 수정되었습니다.

Daniel의 코드는 항목을 길게 눌러야합니다. 많은 사용자는 이것이 숨겨진 기능인 경향이 있기 때문에 직관적이지 않다고 생각합니다. 대신 "이동"버튼을 허용하도록 코드를 수정했습니다. 버튼을 누르고 항목을 드래그하기 만하면됩니다. 이는 뉴스 주제를 재정렬 할 때 Google 뉴스 앱이 작동하는 방식과 더 일치합니다.

데모 앱과 함께 소스 코드는 https://github.com/JohannBlake/ListViewOrderAndSwipe 에서 사용할 수 있습니다.

Chet와 ​​Daniel은 모두 Google에서 왔습니다.

항목 삭제에 대한 Chet의 비디오는 https://www.youtube.com/watch?v=YCHNAi9kJI4 에서 볼 수 있습니다.

품목 재주문에 대한 Daniel의 비디오는 https://www.youtube.com/watch?v=_BZIvjMgH-Q 에서 볼 수 있습니다.

이 모든 것을 하나로 묶어 보이지 않는 UI 경험을 제공하기 위해 상당한 작업을했기 때문에 좋아요 또는 찬성 투표에 감사드립니다. Github에서 프로젝트에 별표를 표시하십시오.


레이아웃 .xml에서 ViewPager를 정의하십시오.

<android.support.v4.view.ViewPager
    android:id="@+id/example_pager"
    android:layout_width="fill_parent"
    android:layout_height="@dimen/abc_action_bar_default_height" />

그런 다음 활동 / 조각에서 사용자 지정 호출기 어댑터를 설정합니다.

활동에서 :

protected void onCreate(Bundle savedInstanceState) {
    PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager());
    ViewPager pager = (ViewPager) findViewById(R.id.example_pager);

    pager.setAdapter(adapter);
    // pager.setOnPageChangeListener(this); // You can set a page listener here
    pager.setCurrentItem(0);
}

조각에서 :

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_layout, container, false);

    if (view != null) {
        PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager());
        ViewPager pager = (ViewPager) view.findViewById(R.id.example_pager);

        pager.setAdapter(adapter);
        // pager.setOnPageChangeListener(this); // You can set a page listener here
        pager.setCurrentItem(0);
    }

    return view;
}

사용자 지정 호출기 클래스를 만듭니다.

// setup your PagerAdapter which extends FragmentPagerAdapter
class PagerAdapter extends FragmentPagerAdapter {
    public static final int NUM_PAGES = 2;
    private CustomFragment[] mFragments = new CustomFragment[NUM_PAGES];
    public PagerAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
    }
    @ Override
    public int getCount() {
        return NUM_PAGES;
    }
    @ Override
    public Fragment getItem(int position) {
        if (mFragments[position] == null) {
               // this calls the newInstance from when you setup the ListFragment
            mFragments[position] = new CustomFragment();
        }
        return mFragments[position];
    }
}

이 기능을 처음부터 구현하는 것은 시간 낭비입니다. SalutonMondo에서 권장하는 라이브러리를 구현했고 매우 만족합니다. 사용이 매우 간단하고 매우 빠릅니다. 원본 라이브러리를 개선하고 항목 클릭에 대한 새 클릭 리스너를 추가했습니다. 또한 멋진 글꼴 라이브러리 ( http://fortawesome.github.io/Font-Awesome/ )를 추가했으며 이제 새 항목 제목을 추가하고 글꼴 awesome에서 아이콘 이름을 지정할 수 있습니다.

다음 은 github 링크입니다.


나는 이것을 달성하기 위해 수많은 타사 라이브러리를 거쳤습니다. 그러나 그들 중 어느 것도 내가 원하는 부드러움과 사용성 경험을 나타내지 않습니다. 그런 다음 직접 작성하기로 결정했습니다. 그리고 그 결과는, 글쎄, 나는 그것을 좋아했습니다. 여기서 코드를 공유하겠습니다. 아마도 앞으로 어떤 리사이클 러 뷰에도 포함될 수있는 라이브러리로 작성할 것입니다. 그러나 지금은 여기에 코드가 있습니다.

참고 : 리사이클 러 뷰와 ViewHolder를 사용하고 있습니다. 일부 값은 하드 코딩되어 있으므로 요구 사항에 따라 변경하십시오.

  • row_layout.xml

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:orientation="horizontal">
        <Button
            android:id="@+id/slide_button_2"
            android:text="Button2"
            android:layout_width="80dp"
            android:layout_height="80dp" />
        <Button
            android:id="@+id/slide_button_1"
            android:text="Button1"
            android:layout_width="80dp"
            android:layout_height="80dp" />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/chat_row_cell"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:orientation="horizontal"
        android:background="@color/white">
        <ImageView
            android:id="@+id/chat_image"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="10dp"
            android:layout_marginBottom="10dp"
            android:layout_marginRight="10dp"
            android:layout_gravity="center"/>
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">
                <TextView
                    android:id="@+id/chat_title"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textColor="@color/md_grey_800"
                    android:textSize="18sp"/>
                <TextView
                    android:id="@+id/chat_subtitle"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textColor="@color/md_grey_600"/>
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>
    

  • ChatAdaptor.java

    공용 클래스 ChatAdaptor extends RecyclerView.Adapter {

    List<MXGroupChatSession> sessions;
    Context context;
    ChatAdaptorInterface listener;
    
    public interface ChatAdaptorInterface{
        void cellClicked(MXGroupChatSession session);
        void utilityButton1Clicked(MXGroupChatSession session);
        void utilityButton2Clicked(MXGroupChatSession session);
    }
    
    public ChatAdaptor(List<MXGroupChatSession> sessions, ChatAdaptorInterface listener, Context context){
        this.sessions=sessions;
        this.context=context;
        this.listener=listener;
    }
    
    @Override
    public ChatViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view=(View)LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_row,null);
        ChatViewHolder chatViewHolder=new ChatViewHolder(view);
        return chatViewHolder;
    }
    
    @Override
    public void onBindViewHolder(ChatViewHolder holder, final int position) {
        MXGroupChatSession session=this.sessions.get(position);
        holder.selectedSession=session;
        holder.titleView.setText(session.getTopic());
        holder.subtitleView.setText(session.getLastFeedContent());
        Picasso.with(context).load(new File(session.getCoverImagePath())).transform(new CircleTransformPicasso()).into(holder.imageView);
    }
    
    @Override
    public int getItemCount() {
        return sessions.size();
    }
    
    public class ChatViewHolder extends RecyclerView.ViewHolder{
        ImageView imageView;
        TextView titleView;
        TextView subtitleView;
        ViewGroup cell;
        ViewGroup cellContainer;
        Button button1;
        Button button2;
    
        MXGroupChatSession selectedSession;
        private GestureDetectorCompat gestureDetector;
    
        float totalx;
        float buttonTotalWidth;
    
        Boolean open=false;
        Boolean isScrolling=false;
    
    
        public ChatViewHolder(View itemView) {
            super(itemView);
            cell=(ViewGroup) itemView.findViewById(R.id.chat_row_cell);
            cellContainer=(ViewGroup) itemView.findViewById(R.id.chat_row_container);
            button1=(Button) itemView.findViewById(R.id.slide_button_1);
            button2=(Button) itemView.findViewById(R.id.slide_button_2);
    
            button1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    listener.utilityButton1Clicked(selectedSession);
                }
            });
    
            button2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    listener.utilityButton2Clicked(selectedSession);
                }
            });
    
            ViewTreeObserver vto = cellContainer.getViewTreeObserver();
            vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    buttonTotalWidth = button1.getWidth()+button2.getWidth();
                }
            });
    
            this.titleView=(TextView)itemView.findViewById(R.id.chat_title);
            subtitleView=(TextView)itemView.findViewById(R.id.chat_subtitle);
            imageView=(ImageView)itemView.findViewById(R.id.chat_image);
            gestureDetector=new GestureDetectorCompat(context,new ChatRowGesture());
    
            cell.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if(gestureDetector.onTouchEvent(event)){
                        return true;
                    }
    
                    if(event.getAction() == MotionEvent.ACTION_UP) {
                        if(isScrolling ) {
                            isScrolling  = false;
                            handleScrollFinished();
                        };
                    }
                    else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                        if(isScrolling ) {
                            isScrolling  = false;
                            handleScrollFinished();
                        };
                    }
                    return false;
                }
            });
        }
    
    
        public class ChatRowGesture extends GestureDetector.SimpleOnGestureListener {
    
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                if (!open){
                    listener.cellClicked(selectedSession);
                }
                return true;
            }
    
            @Override
            public boolean onDown(MotionEvent e) {
                return true;
            }
    
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                    isScrolling=true;
                    totalx=totalx+distanceX;
                    freescroll(totalx);
    
                return true;
            }
        }
    
        void handleScrollFinished(){
            if (open){
                if (totalx>2*buttonTotalWidth/3){
                    slideLeft();
                    totalx=buttonTotalWidth;
                }else{
                    slideRight();
                    totalx=0;
                }
            }else{
                if (totalx>buttonTotalWidth/3){
                    slideLeft();
                    totalx=buttonTotalWidth;
                }else{
                    slideRight();
                    totalx=0;
                }
            }
    
        }
    
        void slideRight(){
            TransitionManager.beginDelayedTransition(cellContainer);
            ViewGroup.MarginLayoutParams params;
            params=(ViewGroup.MarginLayoutParams) cell.getLayoutParams();
            params.setMargins(0,0,0,0);
            cell.setLayoutParams(params);
            open=false;
        }
    
        void slideLeft(){
            TransitionManager.beginDelayedTransition(cellContainer);
            ViewGroup.MarginLayoutParams params;
            params=(ViewGroup.MarginLayoutParams) cell.getLayoutParams();
            params.setMargins(((int)buttonTotalWidth*-1),0,(int)buttonTotalWidth,0);
            cell.setLayoutParams(params);
            open=true;
        }
        void freescroll(float x){
            if (x<buttonTotalWidth && x>0){
                int xint=(int)x;
                ViewGroup.MarginLayoutParams params;
                params=(ViewGroup.MarginLayoutParams) cell.getLayoutParams();
                params.setMargins(params.leftMargin,0,xint,0);
                cell.setLayoutParams(params);
            }
        }
    }
    

이것이 누군가를 돕기를 바랍니다 !!


이 코드를 사용할 수 있습니다

holder.img_close.setOnClickListener(new View.OnClickListener() {
      @Override
        public void onClick(View view) {
            holder.swipeRevealLayout.close(true);
            list.remove(position);
            notifyDataSetChanged();
      }});

참고 URL : https://stackoverflow.com/questions/20797099/swipe-listview-item-from-right-to-left-show-delete-button

반응형