前言:今天遇到个需求,需要让用户动态选择语音传输方式的次序,突然想起支付宝选择扣款顺序的功能,恰好能满足需要,就花了点时间写了个demo,在此权当学习记录


**先上效果图**

  • 支付宝的效果

  • demo的效果


思路:

  • 用ListView+BaseAdapter来布局
  • 在BaseAdapter的getView方法中,我们要设置三个点击事件
    1. 当前view的点击事件,即ListView的item的点击事件,点击时将该item的向上和向下的图标按钮设为可见

    2. 向上图标按钮的点击事件,点击时将该item往上移

    3. 向下图标按钮的点击时间,点击时将该item往下移

  • item向上向下移动的原理
    • 点击向上图标时,将当前item的数据值与前一个item的数据值进行交换,并调用adapter的notifyDataSetChanged()方法进行刷新;
    • 同理,点击向上按钮,将当前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的内部的两个ImageButton定义了点击事件

		//设置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,以后就可以在Activity中,用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进行存储,在下次打开后能重现之前的更改。


以上就是全部的实现,要是有什么讲错或者需要改进的地方,欢迎指出

Android仿支付宝扣款顺序,动态改变ListView各Item次序的更多相关文章

  1. 动态改变Listview的item背景颜色和item中字体的颜色

    https://blog.csdn.net/qq_14813933/article/details/50417859

  2. android 动态改变listview的内容

    本文模拟:点击一个按钮,为已有的listview添加一行数据 <?xml version="1.0" encoding="utf-8"?> < ...

  3. 【Android多屏适配】动态改变Listview item高度

    在ListView的Adapter中去直接获取传入View的LayoutParams是会报空指针异常的,唯一的方法是在xml中嵌套布局一层LinearLayout <?xml version=& ...

  4. Android中Selector的用法(改变ListView和Button的默认背景)

    Android中的Selector的用法 http://blog.csdn.net/shakespeare001/article/details/7788400#comments Android中的S ...

  5. 改变listview中item选中时文字的颜色

    摘要 当listview的某个item选中时,默认有个选中的高亮显示,如果你要自定义选中时的高亮显示效果,可以在listview中设置属性 android:listSelector="@dr ...

  6. android 项目学习随笔十三(ListView实现ITEM点击事件,将已读状态持久化到本地)

    1.因为给LISTVIEW增加了两个头布局,所以在点击事件ITEM索引会增加2,比如原来第一条数据的索引应该为0,增加两个头布局后,它的索引变为        2,为了使LISTVIEW的ITEM在点 ...

  7. Android中动态改变Listview中字体的颜色

    效果如下: 账目显示用的是Listview,要实现的功能为使其根据所在Item是“收入”还是“支出”来把数字设置成绿色或红色 方法是自定义适配器,并重写其中getView()函数,实现如下: //自定 ...

  8. 关于Android Launcher图标上面动态改变数字的实现

    由于项目需要使用到类似小米应用商店的图标数字提示功能,谷歌百度了许多文章都没看到有真正意义上的实现(没有在国外网站上搜索),有实现在APP内部的一个ImageView上面更新数字的,当然这种太简单无非 ...

  9. android仿支付宝输入车牌号

    这个是iOS的效果图,差异不大,楼主主攻OC,见谅 需要用到的xml文件 需要用到的类 number_or_letters.xml <?xml version="1.0" e ...

随机推荐

  1. 第四章 SpringCloud之Eureka-Client实现服务(Jpa,H2)

    1.pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="h ...

  2. 基于java config的springSecurity--session并发控制

    原作地址:http://blog.csdn.net/xiejx618/article/details/42892951 参考资料:spring-security-reference.pdf的Sessi ...

  3. VSCode添加 console.log 快捷键

    file - preferences - keyboard shortcuts - keybindings.json:   添加:   {   "key": "ctrl+ ...

  4. 阶段3 2.Spring_04.Spring的常用注解_4 由Component衍生的注解

    为什么要使用者三个注解 Controller:表现层 Service:业务层 Repository:持久层 在这里就是用Controller 运行也没问题 用Service Repository同样也 ...

  5. [转] margin负值的探讨

    原文:  margin负值-权威指南 [http://www.csswang.com/exp/cssexp/3863.html] static元素是没有设定成浮动的元素,下图说明了负margin对st ...

  6. 转载----c++ static修饰的函数作用与意义

    static修饰的函数叫做静态函数,静态函数有两种,根据其出现的地方来分类: 如果这个静态函数出现在类里,那么它是一个静态成员函数: 静态成员函数的作用在于:调用这个函数不会访问或者修改任何对象(非s ...

  7. k-交叉验证KFold

    交叉验证的原理放在后面,先看函数. 设X是一个9*3的矩阵,即9个样本,3个特征,y是一个9维列向量,即9个标签.现在我要进行3折交叉验证. 执行kFold = KFold(n_splits=3) : ...

  8. 【miscellaneous】监狱智能视频监控系统设计解决方案

    监狱智能视频监控系统设计解决方案 一.系统概况 随着司法监狱管理系统内视频监控系统的日益发展,现有的被动式人工监控这一传统模式已无法满足新形势下的监管工作需求,尤其是现在靠轮询的视频监控方式,无法对突 ...

  9. (转)JMeter性能测试-服务器资源监控插件详解

    零.引言 我们对被测应用进行性能测试时,除了关注吞吐量.响应时间等应用自身的表现外,对应用运行所涉及的服务器资源的使用情况,也是非常重要的方面,通过实时监控,可以准确的把握不同测试场景下服务器资源消耗 ...

  10. 运维日常之机房浪潮服务器硬盘红灯亮起,服务器一直响,raid磁盘红色。。。故障解决方法

    按Ctrl+H进入到WebBIOS内,看见的错误如下所示: 错误是PDMissing,只不过维护的IBM服务器错误的磁盘不是第一块,而是第三块而已,不过坏哪块硬盘没有影响,重要的是错误的原因.这种错误 ...