1. 程式人生 > >Android仿支付寶扣款順序,動態改變ListView各Item次序

Android仿支付寶扣款順序,動態改變ListView各Item次序

前言:今天遇到個需求,需要讓使用者動態選擇語音傳輸方式的次序,突然想起支付寶選擇扣款順序的功能,恰好能滿足需要,就花了點時間寫了個demo,在此權當學習記錄



先上效果圖
  • 支付寶的效果

  • demo的效果

思路:

  • 用ListView+BaseAdapter來佈局
  • 在BaseAdapter的getView方法中,我們要設定三個點選事件

    1. 當前view的點選事件,即ListView的item的點選事件,點選時將該item的向上和向下的圖示按鈕設為可見

    2. 向上圖示按鈕的點選事件,點選時將該item往上移

    3. 向下圖示按鈕的點選時間,點選時將該item往下移

  • item向上向下移動的原理

    • 點擊向上圖示時,將當前item的資料值與前一個item的資料值進行交換,並呼叫adapter的otifyDataSetChanged()方法進行重新整理;
    • 同理,點擊向上按鈕,將當前item的資料值與下一個item的資料值進行交換後重新整理;


實現:

  • 首先定義ListView的item佈局item_change.xml,
    很簡單,線上性佈局中放置三個控制元件,TextView用於顯示文字內容,兩個ImageButton分別放置向上和向下的圖示按鈕,並在初始的時候將這兩個按鈕設為不可見。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item"
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:orientation="horizontal">
    
    <TextView
        android:id="@+id/item_name"
        android:layout_width="140dp"
        android:layout_height="50dp"
        android:layout_gravity="center"
        android:gravity="center"
        android:padding="5dp"
        android:text="餘額寶"/>
    
    
    <ImageButton
        android:id="@+id/up"
        android:layout_width="35dp"
        android:layout_height="35dp"
        android:src="@drawable/up"
        android:background="@null"
        android:layout_marginLeft="50dp"
        android:layout_gravity="center_vertical"
        android:visibility="invisible"/>
    
    <ImageButton
        android:id="@+id/down"
        android:layout_width="35dp"
        android:layout_height="35dp"
        android:src="@drawable/down"
        android:background="@null"
        android:layout_marginLeft="45dp"
        android:layout_gravity="center_vertical"
        android:visibility="invisible"/>
    
    </LinearLayout>
    
  • 接下來看Adapter的程式碼,定義ChangeAdapter繼承自BaseAdapter

    public class ChangeAdapter extends BaseAdapter implements View.OnClickListener {
    
    private ArrayList<String> itemList;
    private Context mContext;
    private Callback mCallback;
    private int mCurPosition;//定義該變數來標記當前item的點選位置
    
    //定義回撥介面實現ListView內Item的內部控制元件的點選事件
    public interface Callback {
        public void click(View v);
    }
    
    public ChangeAdapter(Context mContext, ArrayList<String> itemList, Callback mCallback, int mCurPosition) {
        this.mContext = mContext;
        this.itemList = itemList;
        this.mCallback = mCallback;
        this.mCurPosition = mCurPosition;
    }
    
    @Override
    public int getCount() {
        return itemList.size();
    }
    
    @Override
    public Object getItem(int position) {
        return itemList.get(position);
    }
    
    @Override
    public long getItemId(int position) {
        return position;
    }
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    
        ViewHolder viewHolder;
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.item_change, null);
            viewHolder = new ViewHolder();
            viewHolder.itemName = (TextView) convertView.findViewById(R.id.item_name);
            viewHolder.upBtn = (ImageButton) convertView.findViewById(R.id.up);
            viewHolder.downBtn = (ImageButton) convertView.findViewById(R.id.down);
            convertView.setTag(R.id.tag_viewholder, viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag(R.id.tag_viewholder);
        }
        viewHolder.itemName.setText(itemList.get(position));
    
    
        //根據點選或者向上向下操作的item的當前位置,來控制向上和向下的按鈕的可見與否
        if (mCurPosition == position && mCurPosition == 0) {
            viewHolder.downBtn.setVisibility(View.VISIBLE);
        } else if (mCurPosition == position && mCurPosition == itemList.size() - 1) {
            viewHolder.upBtn.setVisibility(View.VISIBLE);
        } else if (mCurPosition == position && mCurPosition != 0 && mCurPosition != itemList.size() - 1) {
            viewHolder.upBtn.setVisibility(View.VISIBLE);
            viewHolder.downBtn.setVisibility(View.VISIBLE);
        } else {
            viewHolder.upBtn.setVisibility(View.INVISIBLE);
            viewHolder.downBtn.setVisibility(View.INVISIBLE);
        }
    
        //設定item向下移動的點選時間並標誌其位置
        viewHolder.downBtn.setOnClickListener(this);
        viewHolder.downBtn.setTag(position);
    
        //設定item向上移動的點選時間並標誌其位置
        viewHolder.upBtn.setOnClickListener(this);
        viewHolder.upBtn.setTag(position);
    
        //設定整個item的點選時間並標誌其位置
        convertView.setOnClickListener(this);
        convertView.setTag(R.id.tag_item_click, position);
    
        return convertView;
    }
    
    
    class ViewHolder {
        TextView itemName;
        ImageButton upBtn;
        ImageButton downBtn;
    }
    
    //定義item內部控制元件的點選事件由回撥介面定義的點選方法來處理
    @Override
    public void onClick(View v) {
        mCallback.click(v);
    }
    
    //在對資料進行處理後,呼叫該方法,通知adapter重新整理資料
    public void refresh(int currentPosition) {
        mCurPosition = currentPosition;
        notifyDataSetChanged();
     }
    }
    

Adapter這部分程式碼不難,幾個注意的點稍微講解下

  • ListView的Item的內部控制元件,即向上和向下的圖示按鈕的點選事件的實現.
    我們定義瞭如下的回撥介面,並在該介面中定義了一個click方法

        //定義回撥介面實現ListView內Item的內部控制元件的點選事件
            public interface Callback {
                public void click(View v);
            }
    

我們在ChangeAdapter實現了OnClickListener介面,並在getView方法中,對整個Item,及Item的內部的兩個ImageViewButton定義了點選事件

        //設定item向下移動的點選時間並標誌其位置
        viewHolder.downBtn.setOnClickListener(this);
        viewHolder.downBtn.setTag(position);

        //設定item向上移動的點選時間並標誌其位置
        viewHolder.upBtn.setOnClickListener(this);
        viewHolder.upBtn.setTag(position);

        //設定整個item的點選時間並標誌其位置
        convertView.setOnClickListener(this);
        convertView.setTag(R.id.tag_item_click, position);

接下來看onClick方法,可以看到我們item和圖示按鈕的點選這裡交給了前面定義的回撥Callback中的click方法來處理

        @Override
        public void onClick(View v) {
            mCallback.click(v);
        }

所以這樣就明顯了,我們在要呼叫ChangeAdapter的Activity裡實現這裡定義的Callback介面,並將其作為ChageAdapter構造方法的一部分,由於item的內部控制元件點選事件會由Callback處理,而此時Activity又實現了Callback,相應的點選事件就可以由Activity處理。

        public ChangeAdapter(Context mContext, ArrayList<String> itemList, Callback mCallback, int mCurPosition) {
            this.mContext = mContext;
            this.itemList = itemList;
            this.mCallback = mCallback;
            this.mCurPosition = mCurPosition;
        }
  • 在上一步的基礎上,由於在點選item或者兩個ImageButton時,我們需要獲取到當前點選的item的position來做相應的邏輯處理,所以我們在設定item和ImageButton的點選事件時,都相應的用setTag方法,儲存了當前的item的position,以便在Activity中獲取。分析下getView的程式碼

這部分就是我們在點選item時,根據該item的位置,重新整理adapter。點選位置為0時只顯示向下按鈕,點選位置為最後一個時只顯示向上按鈕,點選其他位置時,向上和向下按鈕都設為可見

         //根據點選或者向上向下操作的item的當前位置,來控制向上和向下的按鈕的可見與否
        if (mCurPosition == position && mCurPosition == 0) {
            viewHolder.downBtn.setVisibility(View.VISIBLE);
        } else if (mCurPosition == position && mCurPosition == itemList.size() - 1) {
            viewHolder.upBtn.setVisibility(View.VISIBLE);
        } else if (mCurPosition == position && mCurPosition != 0 && mCurPosition != itemList.size() - 1) {
            viewHolder.upBtn.setVisibility(View.VISIBLE);
            viewHolder.downBtn.setVisibility(View.VISIBLE);
        } else {
            viewHolder.upBtn.setVisibility(View.INVISIBLE);
            viewHolder.downBtn.setVisibility(View.INVISIBLE);
        }

向上和向下按鈕的setTag方法都只需傳入position,以後就可以在Activit中,用getTag取到當前點選item的position。
而item的setTag就比較特殊

        //設定整個item的點選時間並標誌其位置
        convertView.setOnClickListener(this);
        convertView.setTag(R.id.tag_item_click, position);      

追一下程式碼就能知道,在getView方法中,有兩個地方用到了convertView.setTag方法,第一個要存入的是一個ViewHolder物件,第二個存入的是當前item的position。所以需要用不同的key來標誌。不然你在呼叫getTag時必然會出錯。

        convertView.setTag(R.id.tag_viewholder, viewHolder);

而這裡的key需要是resourceId,所以我們在values/strings.xml,定義兩個item來作為key

    <item type="id" name="tag_viewholder"></item>
        <item type="id" name="tag_item_click"></item>   

這樣以後就可以通過view.getTag(key),取到相應的值

  • 重新整理adapter,我們定義了一個refresh方法,在點選的位置發生改變後,通知adapter重新整理

        //在對資料進行處理後,呼叫該方法,通知adapter重新整理資料
        public void refresh(int currentPosition) {
            mCurPosition = currentPosition;
            notifyDataSetChanged();
         }
    
  • 最後看一下MainActivity中的邏輯

定義佈局activity_main.xml,就放一個ListView

    <?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="match_parent">

    <ListView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/lv_change">
    </ListView>

    </LinearLayout>

MainActivity:

        public class MainActivity extends AppCompatActivity implements ChangeAdapter.Callback {

            private ListView lv;
            private ChangeAdapter adapter;
            private ArrayList<String> itemList;
            private int currentPosition = -1;

            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                initAndSetView();
            }

            private void initAndSetView() {
                lv = (ListView) findViewById(R.id.lv_change);
                initData();
                adapter = new ChangeAdapter(this, itemList, this, currentPosition);
                lv.setAdapter(adapter);
            }

            @Override
            public void click(View v) {
                int curPosition;
                int mCurPosition;
                switch (v.getId()) {
                    //整個item點選事件的處理邏輯
                    case R.id.item:
                        mCurPosition = (int) v.getTag(R.id.tag_item_click);
                        currentPosition = mCurPosition;
                        adapter.refresh(currentPosition);
                        break;

                    //向上圖示按鍵點選事件的處理邏輯
                    case R.id.up:
                        curPosition = (int) v.getTag();
                        if (curPosition != 0) {
                            String upFirst = itemList.get(curPosition);
                            String upSecond = itemList.get(curPosition - 1);
                            itemList.remove(curPosition);
                            itemList.remove(curPosition - 1);
                            itemList.add(curPosition - 1, upFirst);
                            itemList.add(curPosition, upSecond);
                            currentPosition = curPosition - 1;
                            adapter.refresh(currentPosition);
                        }
                        break;

                    //向下圖示按鍵點選事件的處理邏輯
                    case R.id.down:
                        curPosition = (int) v.getTag();
                        if (curPosition != itemList.size() - 1) {
                            String downFirst = itemList.get(curPosition);
                            String downSecond = itemList.get(curPosition + 1);
                            itemList.remove(curPosition + 1);
                            itemList.remove(curPosition);
                            itemList.add(curPosition, downSecond);
                            itemList.add(curPosition + 1, downFirst);
                            currentPosition = curPosition + 1;
                            adapter.refresh(currentPosition);
                        }
                        break;
                    default:
                        break;
                }

            }

            //初始化填充資料
            private void initData() {
                itemList = new ArrayList<String>();
                itemList.add("餘額寶");
                itemList.add("螞蟻花唄");
                itemList.add("餘額");
                itemList.add("工商銀行儲蓄卡(1689)");
                itemList.add("花唄分期");
            }
        }   

這部分邏輯很簡單,如之前所講的,MainActivity實現ChangeAdapter的Callback介面,並實現介面的click方法,注意導包不要導錯了。而後在adapter的構造方法中,傳入this,這樣item及其內部控制元件的點選事件就可以在MainActivity中處理。重點看click方法:

    @Override
    public void click(View v) {
        int curPosition;
        int mCurPosition;
        switch (v.getId()) {
            //整個item點選事件的處理邏輯
            case R.id.item:
                mCurPosition = (int) v.getTag(R.id.tag_item_click);
                currentPosition = mCurPosition;
                adapter.refresh(currentPosition);
                break;
            //向上圖示按鍵點選事件的處理邏輯
            case R.id.up:
                curPosition = (int) v.getTag();
                if (curPosition != 0) {
                    String upFirst = itemList.get(curPosition);
                    String upSecond = itemList.get(curPosition - 1);
                    itemList.remove(curPosition);
                    itemList.remove(curPosition - 1);
                    itemList.add(curPosition - 1, upFirst);
                    itemList.add(curPosition, upSecond);
                    currentPosition = curPosition - 1;
                    adapter.refresh(currentPosition);
                }
                break;
            //向上圖示按鍵點選事件的處理邏輯
            case R.id.down:
                curPosition = (int) v.getTag();
                if (curPosition != itemList.size() - 1) {
                    String downFirst = itemList.get(curPosition);
                    String downSecond = itemList.get(curPosition + 1);
                    itemList.remove(curPosition + 1);
                    itemList.remove(curPosition);
                    itemList.add(curPosition, downSecond);
                    itemList.add(curPosition + 1, downFirst);
                    currentPosition = curPosition + 1;
                    adapter.refresh(currentPosition);
                }
                break;
            default:
                break;
        }

    }
  • 對於item的點選事件,我們用之前講的getTag(key)方法並用強制型別轉換獲取該item的position,並呼叫之前定義的refresh方法,通知adapter當前的position資料更新了,而後adapter中就會根據這個position處理向上和向下的圖示按鈕的顯示與否。

  • 對於向上的ImageButton的點選事件,我們用getTag方法獲取其position,並調換當前位置與前一個位置的資料的值,就實現了item的向上移動的效果。注意對於第一個item,我們不處理向上移動的邏輯,所以這裡要加個判斷。處理完之後,呼叫refresh方法,就可以重新整理資料。

  • 對於向下的ImageButton的點選事件,與向上的ImageButton的邏輯是相似的,這裡就不再贅述。

  • 要注意的是ArrayList的remove方法,當你remove了一個position的資料,後面位置的資料就會前移,所以這裡處理資料要小心點,稍微思考下就能明白。

  • 在這裡,item位置移動後退出並沒有儲存,可以用SharedPreference進行儲存,在下次開啟後能重現之前的更改。

以上就是全部的實現,要是有什麼講錯或者有改進的地方,歡迎指出