ดูรายการ Android ลากและวางเรียงลำดับ


132

ฉันมีรายการบันทึกในมุมมองรายการที่ฉันต้องการให้ผู้ใช้สามารถจัดเรียงใหม่โดยใช้วิธีการลากและวาง ฉันเคยเห็นสิ่งนี้นำไปใช้ในแอพอื่น ๆ แต่ฉันไม่พบบทช่วยสอนสำหรับมัน มันต้องเป็นสิ่งที่คนอื่นต้องการเช่นกัน ใครช่วยชี้รหัสให้ฉันทำสิ่งนี้ได้ไหม


1
ฉันพบบทช่วยสอนนี้ซึ่งอาจช่วยในการสร้างมุมมองรายการที่จัดเรียงได้ ฉันยังไม่ได้ทดสอบ แต่วิดีโอดูมีแนวโน้มดี
Alex

@Arkde ไม่ล้อเล่นพวกเขายังไม่ยอมรับคำตอบสำหรับคำถามนี้อีกหลายปีต่อมา
ArtOfWarfare

@ArtOfWarfare ฉันเดาว่าควรพิจารณาว่ามีบางอย่างที่โชคร้ายเกิดขึ้นกับผู้ถาม ... ไม่อนุญาตให้ทำกิจกรรมต่อไป
heycosmo

1
@heycosmo เป็นไปได้ ... ตามโปรไฟล์ SO ของพวกเขา miannelle เข้าเยี่ยมชมครั้งล่าสุดเพียงหนึ่งเดือนหลังจากถามคำถาม นอกจากนี้การทำงานที่ยอดเยี่ยมใน DSLV ... ฉันได้ทำการปรับเปลี่ยนเล็กน้อยเพื่อให้สิ่งต่างๆเช่นการแตะสองครั้งเพื่อทำซ้ำรายการและเปลี่ยนเงาของรายการเมื่อลากไปรอบ ๆ (รายการของฉันแต่ละรายการมีหมายเลขแถวอยู่ดังนั้นฉัน ทำให้การอัปเดตหมายเลขแถวสามารถอัปเดตได้ในขณะที่ลาก) พวกเขาเป็นเพียงการแฮ็กอินเท่านั้นที่สง่างามน้อยกว่าทุกอย่างในชั้นเรียนดังนั้นทำไมฉันจึงไม่ส่งการเปลี่ยนแปลงไปยัง GitHub
ArtOfWarfare

github.com/bauerca/drag-sort-listviewฉันกำลังใช้สิ่งนี้และเป็นไปได้ไหมที่จะลาก ROW บน LongClick ของ Listview
Achin

คำตอบ:


85

ฉันทำงานนี้มาระยะหนึ่งแล้ว ยากที่จะทำให้ถูกต้องและฉันไม่ได้อ้างว่าฉันทำ แต่ฉันก็มีความสุขกับมัน รหัสของฉันและการสาธิตต่างๆสามารถพบได้ที่

การใช้งานนั้นคล้ายกับ TouchInterceptor (ซึ่งเป็นรหัสที่ใช้) แม้ว่าจะมีการเปลี่ยนแปลงการใช้งานที่สำคัญ

DragSortListView มีการเลื่อนที่ราบรื่นและคาดเดาได้ในขณะที่ลากและสับรายการ การสับเปลี่ยนรายการมีความสอดคล้องกับตำแหน่งของรายการลาก / ลอยมากขึ้น รองรับรายการที่มีความสูงต่างกัน การลากเลื่อนสามารถปรับแต่งได้ (ฉันสาธิตการลากเลื่อนอย่างรวดเร็วผ่านรายการยาว ๆ - ไม่ใช่ว่าแอปพลิเคชันอยู่ในใจ) เคารพส่วนหัว / ส่วนท้าย ฯลฯ . ?? ลองดูสิ.


3
โซลูชันของคุณทำงานได้อย่างมีเสน่ห์ ดีกว่ามากแล้วคนอื่น ๆ ใบอนุญาตของซอร์สโค้ดของคุณคืออะไร? Apache 2.0?
Dariusz Bacinski

1
@heycosmo ฉันมีปัญหาเล็กน้อยเมื่อสร้างเค้าโครงในแอปของฉันโดยใช้มุมมองที่ให้ไว้ในการสาธิต มีข้อผิดพลาดหลายประการเกี่ยวกับเนมสเปซคุณช่วยทำบล็อกโพสต์เล็ก ๆ เกี่ยวกับวิธีใช้โค้ดที่คุณให้มาได้ไหม
daniel_c05

มีความเป็นไปได้หรือไม่ที่จะแก้ไขโค้ดในลักษณะที่รายการจะถูกจัดเรียงไม่เพียง แต่ในขณะที่คุณลากรายการของคุณไว้ด้านบน
Alex Semeniuk

@heycosmo คุณสามารถทำให้ที่เก็บ GitHub ของคุณเข้าถึงได้หรือไม่? ดูเหมือนจะออฟไลน์ ...
sdasdadas

8
bauerca repo ไม่ได้รับการดูแลอีกต่อไป ส้อมนี้มีการใช้งานมากขึ้น: github.com/JayH5/drag-sort-listview
ecdpalma

21

ตอนนี้ก็สวยง่ายที่จะใช้สำหรับRecyclerViewกับItemTouchHelper เพียงแค่แทนที่onMoveวิธีการจากItemTouchHelper.Callback:

 @Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
    mMovieAdapter.swap(viewHolder.getAdapterPosition(), target.getAdapterPosition());
    return true;
}

บทแนะนำที่ดีงามเกี่ยวกับเรื่องนี้สามารถพบได้ที่ medium.com: ลากและปัดด้วย RecyclerView


18

กำลังเพิ่มคำตอบนี้เพื่อจุดประสงค์ของผู้ที่ google เกี่ยวกับเรื่องนี้ ..

เมื่อเร็ว ๆ นี้มีตอนของ DevBytes ( ListView Cell Dragging and Rearranging ) ซึ่งอธิบายถึงวิธีการทำเช่นนี้

คุณสามารถค้นหาได้ที่นี่ยังมีโค้ดตัวอย่างสามารถใช้ได้ที่นี่

สิ่งที่รหัสนี้ทำโดยทั่วไปคือสร้างdynamic listviewโดยส่วนขยายlistviewที่รองรับการลากและสลับเซลล์ เพื่อให้คุณสามารถใช้ DynamicListViewแทนพื้นฐานของคุณListView และนั่นคือคุณได้ติดตั้ง ListView ด้วยการลากและวาง


1
เมื่อใช้การใช้งาน DevBytes โปรดทราบว่าอะแดปเตอร์และ DynamicListView ของคุณจะใช้อินสแตนซ์เดียวกันหากอาร์เรย์อ็อบเจ็กต์ มิฉะนั้นการใช้งานจะไม่ทำงาน นี่ไม่ใช่ปัญหาสำหรับรายการคงที่ แต่อาจเป็นเรื่องท้าทายสำหรับ Loaders
AAverin

ฉันลองใช้ตัวอย่างบน Android เวอร์ชันปัจจุบัน 5.0 ตอนนี้มีปัญหาบางอย่าง ...
Torsten B

@TCA ใช่ฉันยังมีปัญหาเกี่ยวกับ 5.0 กรุณาช่วย.
Manu

6

DragListView lib ทำสิ่งนี้ได้อย่างเรียบร้อยพร้อมการสนับสนุนที่ดีมากสำหรับภาพเคลื่อนไหวที่กำหนดเองเช่นภาพเคลื่อนไหวระดับความสูง นอกจากนี้ยังคงได้รับการดูแลและปรับปรุงอยู่เป็นประจำ

นี่คือวิธีที่คุณใช้:

1: เพิ่ม lib เพื่อไล่ระดับก่อน

dependencies {
    compile 'com.github.woxthebox:draglistview:1.2.1'
}

2: เพิ่มรายการจาก xml

<com.woxthebox.draglistview.DragListView
    android:id="@+id/draglistview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

3: ตั้งค่าตัวฟังการลาก

mDragListView.setDragListListener(new DragListView.DragListListener() {
    @Override
    public void onItemDragStarted(int position) {
    }

    @Override
    public void onItemDragEnded(int fromPosition, int toPosition) {
    }
});

4: สร้างอะแดปเตอร์แทนที่จาก DragItemAdapter

public class ItemAdapter extends DragItemAdapter<Pair<Long, String>, ItemAdapter.ViewHolder>
    public ItemAdapter(ArrayList<Pair<Long, String>> list, int layoutId, int grabHandleId, boolean dragOnLongPress) {
        super(dragOnLongPress);
        mLayoutId = layoutId;
        mGrabHandleId = grabHandleId;
        setHasStableIds(true);
        setItemList(list);
}

5: ใช้ตัวดูที่ขยายจาก DragItemAdapter.ViewHolder

public class ViewHolder extends DragItemAdapter.ViewHolder {
    public TextView mText;

    public ViewHolder(final View itemView) {
        super(itemView, mGrabHandleId);
        mText = (TextView) itemView.findViewById(R.id.text);
    }

    @Override
    public void onItemClicked(View view) {
    }

    @Override
    public boolean onItemLongClicked(View view) {
        return true;
    }
}

สำหรับข้อมูลเพิ่มเติมไปที่https://github.com/woxblom/DragListView


5

ฉันพบว่าDragSortListViewทำงานได้ดีแม้ว่าการเริ่มต้นใช้งานอาจง่ายกว่า นี่คือบทแนะนำสั้น ๆ เกี่ยวกับการใช้งานใน Android Studio พร้อมรายการในหน่วยความจำ:

  1. เพิ่มสิ่งนี้ในการbuild.gradleอ้างอิงสำหรับแอปของคุณ:

    compile 'asia.ivity.android:drag-sort-listview:1.0' // Corresponds to release 0.6.1
    
  2. สร้างทรัพยากรสำหรับรหัสจุดจับการลากโดยสร้างหรือเพิ่มในvalues/ids.xml:

    <resources>
        ... possibly other resources ...
        <item type="id" name="drag_handle" />
    </resources>
    
  3. สร้างเค้าโครงสำหรับรายการที่มีรูปภาพแฮนเดิลลากที่คุณชื่นชอบและกำหนด ID ให้กับ ID ที่คุณสร้างในขั้นตอนที่ 2 (เช่นdrag_handle)

  4. สร้างเค้าโครง DragSortListView สิ่งนี้:

    <com.mobeta.android.dslv.DragSortListView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:dslv="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        dslv:drag_handle_id="@id/drag_handle"
        dslv:float_background_color="@android:color/background_light"/>
    
  5. ตั้งค่าArrayAdapterอนุพันธ์ด้วยการgetViewแทนที่ที่แสดงผลมุมมองรายการของคุณ

        final ArrayAdapter<MyItem> itemAdapter = new ArrayAdapter<MyItem>(this, R.layout.my_item, R.id.my_item_name, items) { // The third parameter works around ugly Android legacy. http://stackoverflow.com/a/18529511/145173
            @Override public View getView(int position, View convertView, ViewGroup parent) {
                View view = super.getView(position, convertView, parent);
                MyItem item = getItem(position);
                ((TextView) view.findViewById(R.id.my_item_name)).setText(item.getName());
                // ... Fill in other views ...
                return view;
            }
        };
    
        dragSortListView.setAdapter(itemAdapter);
    
  6. ตั้งค่าฟังก์ชั่นดร็อปที่จัดเรียงรายการใหม่เมื่อดรอป

        dragSortListView.setDropListener(new DragSortListView.DropListener() {
            @Override public void drop(int from, int to) {
                MyItem movedItem = items.get(from);
                items.remove(from);
                if (from > to) --from;
                items.add(to, movedItem);
                itemAdapter.notifyDataSetChanged();
            }
        });
    

4

เมื่อเร็ว ๆ นี้ฉันได้สะดุดกับGistที่ยอดเยี่ยมนี้ซึ่งช่วยให้สามารถใช้งานการจัดเรียงแบบลากได้ListViewโดยไม่จำเป็นต้องพึ่งพาภายนอก


โดยทั่วไปจะประกอบด้วยการสร้าง Adapter ที่กำหนดเองของคุณขยายArrayAdapterเป็นคลาสภายในไปยังกิจกรรมที่มีListViewไฟล์. บนอะแดปเตอร์นี้จะตั้งค่าเป็นonTouchListenerรายการของคุณที่จะส่งสัญญาณการเริ่มต้นของการลาก

ใน Gist นั้นพวกเขาตั้งค่า Listener เป็นส่วนเฉพาะของเค้าโครงของ List Item ("ที่จับ" ของรายการ) ดังนั้นจึงไม่มีใครเคลื่อนย้ายโดยการกดส่วนใดส่วนหนึ่งโดยไม่ได้ตั้งใจ โดยส่วนตัวแล้วฉันชอบที่จะไปกับonLongClickListenerแทน แต่ขึ้นอยู่กับคุณที่จะตัดสินใจ นี่คือข้อความที่ตัดตอนมาจากส่วนนั้น:

public class MyArrayAdapter extends ArrayAdapter<String> {

    private ArrayList<String> mStrings = new ArrayList<String>();
    private LayoutInflater mInflater;
    private int mLayout;

    //constructor, clear, remove, add, insert...

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder holder;

        View view = convertView;
        //inflate, etc...

        final String string = mStrings.get(position);
        holder.title.setText(string);

        // Here the listener is set specifically to the handle of the layout
        holder.handle.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                    startDrag(string);
                    return true;
                }
                return false;
            }
        });

        // change color on dragging item and other things...         

        return view;
    }
}

นอกจากนี้ยังเกี่ยวข้องกับการเพิ่ม an onTouchListenerลงในListViewซึ่งตรวจสอบว่ามีการลากรายการหรือไม่จัดการการแลกเปลี่ยนและการยกเลิกการใช้งานและหยุดสถานะการลาก ข้อความที่ตัดตอนมาจากส่วนนั้น:

mListView.setOnTouchListener(new View.OnTouchListener() {
     @Override
     public boolean onTouch(View view, MotionEvent event) {
        if (!mSortable) { return false; }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                // get positions
                int position = mListView.pointToPosition((int) event.getX(), 
                    (int) event.getY());
                if (position < 0) {
                    break;
                }
                // check if it's time to swap
                if (position != mPosition) {
                    mPosition = position;
                    mAdapter.remove(mDragString);
                    mAdapter.insert(mDragString, mPosition);
                }
                return true;
            }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_OUTSIDE: {
                //stop drag state
                stopDrag();
                return true;
            }
        }
        return false;
    }
});

สุดท้ายนี่คือลักษณะstopDragและstartDragวิธีการซึ่งจัดการกับการเปิดใช้งานและปิดใช้งานกระบวนการลาก:

public void startDrag(String string) {
    mPosition = -1;
    mSortable = true;
    mDragString = string;
    mAdapter.notifyDataSetChanged();
}

public void stopDrag() {
    mPosition = -1;
    mSortable = false;
    mDragString = null;
    mAdapter.notifyDataSetChanged();
}

แม้ว่านี่จะเป็นหนึ่งในการใช้งานที่ง่ายที่สุด แต่ก็ดูไม่ดี (แถวที่เปลี่ยนไปมีรูปลักษณ์เป็นศูนย์จริงๆ) รู้สึกไม่ดีและทำให้การเลื่อนดูรายการที่ไม่พอดีกับหน้าจอเป็นไปไม่ได้ (ฉันสลับแถวตลอดเวลาแทนที่จะเป็น เลื่อน)
Heinzlmaen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.