Android仿支付寶扣款順序,動態改變ListView各Item次序
前言:今天遇到個需求,需要讓使用者動態選擇語音傳輸方式的次序,突然想起支付寶選擇扣款順序的功能,恰好能滿足需要,就花了點時間寫了個demo,在此權當學習記錄
先上效果圖
- 支付寶的效果
- demo的效果
思路:
- 用ListView+BaseAdapter來佈局
在BaseAdapter的getView方法中,我們要設定三個點選事件
當前view的點選事件,即ListView的item的點選事件,點選時將該item的向上和向下的圖示按鈕設為可見
向上圖示按鈕的點選事件,點選時將該item往上移
向下圖示按鈕的點選時間,點選時將該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進行儲存,在下次開啟後能重現之前的更改。
以上就是全部的實現,要是有什麼講錯或者有改進的地方,歡迎指出