仿新聞訂閱功能,Android可拖動排序的GridView(自定義GridView)

2020-10-27 11:00:38

前言

這篇部落格只寫了拖動排序,具體的訂閱功能,有需要的小夥伴也可以看下我之前寫的關於訂閱功能實現的一篇博文👇👇👇(寫的比較早,現在有了更簡便的方法,下次再寫一篇新的)!
Android應用分類訂閱功能(新聞個性化分類訂閱)

效果圖

這裡可以看到,長按某個Item以後,進行拖動,可以實現將此Item拖動到該GridView中其他位置的功能
在這裡插入圖片描述

實現原理

通過實現自定義GridView重寫onTouchEvent方法以及實現onItemLongClick介面來實現長按一個item然後用視窗顯示出來,根據手指的移動來移動顯示的視窗,最後通過判斷手指離開螢幕的位置來確定最終item移動到的位置。

核心程式碼

DragGridView.java
首先是繼承自GridView,並且實現OnItemLongClickListener介面,再重寫一下onTouchEvent方法;
整體邏輯(理解萬歲):
1、長按某個Item時:在onTouchEvent中獲取當前手指在螢幕上的位置(記錄座標);在OnItemLongClickListener中通過獲取當前Item的影象資源放入ImageView中,然後通過WindowManager視窗將這個影象方法1.2倍顯示在螢幕上,並且隱藏掉長按的Item;
2、長按以後拖動時:讓Windowmanager視窗隨著手指移動,並且判斷移動的過程中有沒有移動到別的Item上,如果有那就進行排序;
3、停止操作以後:將隱藏掉的Item顯示回來,並且呼叫WindowManager的.removeView()方法移除ImageView影象
這裡該寫的註釋基本上都寫了,這裡面有幾個Adapter裡面寫的方法,Adapter的程式碼在這個的下面;

public class DragGridView extends GridView implements AdapterView.OnItemLongClickListener {

    private ImageView dragIiewView;//ImageView,影象容器
    private WindowManager windowManager;//視窗
    private WindowManager.LayoutParams dragParams;//用於記錄視窗展示的位置
    private int oldPos;//用於記錄拖動的item的position
    private int rawX;//用於記錄初始座標X
    private int rawY;//用於記錄初始座標Y
    private boolean isDrag;//用來判斷當前是否是在拖動的狀態

    public DragGridView(Context context) {
        super(context);
        initView();
    }

    public DragGridView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public DragGridView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        setOnItemLongClickListener(this);
        dragIiewView = new ImageView(getContext());
        windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        dragParams = new WindowManager.LayoutParams();
    }

    @Override
    public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
        //記下item的position
        oldPos = i;
        //獲取長按item的DrawingCache
        view.destroyDrawingCache();
        view.setDrawingCacheEnabled(true);
        //獲取item的Bitmap
        Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
        //設定拖動引數、以及設定顯示出來可以拖動的item放大1.2倍
        dragParams.gravity = Gravity.TOP | Gravity.START;
        dragParams.width = (int) (1.2f * bitmap.getWidth());
        dragParams.height = (int) (1.2f * bitmap.getHeight());
        //獲取拖動的中心點
        dragParams.x = rawX - dragParams.width / 2;
        dragParams.y = rawY - dragParams.height / 2;
        dragParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
        dragParams.format = PixelFormat.TRANSLUCENT;
        dragParams.windowAnimations = 0;
        //設定ImageView的影象為本次長按的item的影象
        dragIiewView.setImageBitmap(bitmap);
        //將ImageView顯示到螢幕上
        windowManager.addView(dragIiewView, dragParams);
        //設定當前狀態為true
        isDrag = true;
        //設定長按的item隱藏
        ((DragGridViewAdapter) getAdapter()).hideView(i);
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            //當手指觸碰螢幕時,記錄下座標
            rawX = (int) ev.getRawX();
            rawY = (int) ev.getRawY();
        } else if (isDrag && ev.getAction() == MotionEvent.ACTION_MOVE) {
            //item被拖動時,記錄拖動過程中的(當前)座標
            dragParams.x = (int) (ev.getRawX() - dragIiewView.getWidth() / 2);
            dragParams.y = (int) (ev.getRawY() - dragIiewView.getHeight() / 2);
            //更新視窗顯示
            windowManager.updateViewLayout(dragIiewView, dragParams);
            //獲取拖動過程中,觸控點所在GridView中item的位置(position)
            int newPos = pointToPosition(((int) ev.getX()), (int) ev.getY());
            //如果當前位置不等於上次停留的位置,則交換兩次位置的item
            if (newPos != AdapterView.INVALID_POSITION && newPos != oldPos) {
                ((DragGridViewAdapter) getAdapter()).updataView(oldPos, newPos);
                oldPos = newPos;
            }
        } else if (isDrag && ev.getAction() == MotionEvent.ACTION_UP) {
            //操作停止時(手指離開螢幕),迴歸正常顯示狀態
            ((DragGridViewAdapter) getAdapter()).showHideView();
            //清空視窗
            windowManager.removeView(dragIiewView);
            //狀態改成false
            isDrag = false;
        }
        return super.onTouchEvent(ev);
    }
}

DragGridViewAdapter.java
整個介面卡除了加了幾個方法以外,和正常的Adapter沒啥區別,這裡主要有三個方法:
1、hideView,這個方法用於隱藏長按的那個Item(如果你的Item比較複雜,這個隱藏的方法可以改寫,這裡不具體講怎麼改)
2、showHideView,這個方法用於顯示hideView方法隱藏掉的Item(用於拖動操作進行完了以後)
3、updataView,更新GridView的檢視顯示,主要邏輯是:通過判斷移動前Item的position和移動後Item的position,進行比較再進行相應的資料排序(這裡排序的實質是進行資料的插入和移除)

public class DragGridViewAdapter extends BaseAdapter {

    private List<String> list;

    private int hidePos = AdapterView.INVALID_POSITION;

    public DragGridViewAdapter(List<String> list) {
        this.list = list;
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int i) {
        return list.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    class ViewHolder {
        TextView txv;
    }

    //隱藏item
    public void hideView(int pos) {
        hidePos = pos;
        notifyDataSetChanged();
    }

    //顯示隱藏了的item
    public void showHideView() {
        hidePos = AdapterView.INVALID_POSITION;
        notifyDataSetChanged();
    }

    //處理當item的位置發生改變時,更新顯示
    public void updataView(int oldPos, int newPos) {
        if (oldPos > newPos) {//item從後往前拖動
            list.add(newPos, list.get(oldPos));
            list.remove(oldPos + 1);
        } else if (oldPos < newPos) {//item從前往後拖動
            list.add(newPos + 1, list.get(oldPos));
            list.remove(oldPos);
        }
        hidePos = newPos;
        notifyDataSetChanged();
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ViewHolder viewHolder = new ViewHolder();
        if (view == null) {
            view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.drag_grid_item, null);
            viewHolder.txv = (TextView) view.findViewById(R.id.txv);
            view.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) view.getTag();
        }
        if (i != hidePos) {
            viewHolder.txv.setText(list.get(i));
        } else {
            viewHolder.txv.setText("");
        }
        return view;
    }
}

寫到這裡,會寫GridView的小夥伴可以忽略下面的非核心程式碼部分,Activity中和正常寫GridView一樣(加資料、範例化介面卡、GridView呼叫setAdapter()方法)

非核心程式碼部分

DragGridViewActivity.java
這裡就簡單的加了20組資料,然後範例化Adapter、呼叫setAdapter()方法

public class DragGridViewActivity extends AppCompatActivity {

    private DragGridView gvDrag;
    private List<String> list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drag_grid_view);
        initView();
        list = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            list.add("小黑" + i);
        }
        DragGridViewAdapter adapter = new DragGridViewAdapter(list);
        gvDrag.setAdapter(adapter);
    }

    private void initView() {
        gvDrag = (DragGridView) findViewById(R.id.gv_drag);
    }
}

activity_drag_grid_view.xml
這裡就是一個簡單的自己寫的DragGridView(自定義的那個GridView)

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

    <com.example.asyu.draggridview.DragGridView
        android:id="@+id/gv_drag"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:numColumns="5" />

</LinearLayout>

drag_grid_item.xml
這裡也是隨便寫了個Item

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/txv"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_margin="5dp"
        android:layout_weight="1"
        android:background="#619db4"
        android:gravity="center" />

</LinearLayout>

專案地址

專案地址:DragGridView專案地址(GitHub)
點個贊再走!!!