• Android仿支付宝扣款顺序,动态改变ListView各Item次序


    前言:今天遇到个需求,需要让用户动态选择语音传输方式的次序,突然想起支付宝选择扣款顺序的功能,恰好能满足需要,就花了点时间写了个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进行存储,在下次打开后能重现之前的更改。


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

  • 相关阅读:
    [Noi2011]阿狸的打字机
    Bzoj3530: [Sdoi2014]数数
    Bzoj2037: [Sdoi2008]Sue的小球
    Bzoj4869: [Shoi2017]相逢是问候
    Bzoj1899: [Zjoi2004]Lunch 午餐
    Bzoj3884: 上帝与集合的正确用法
    UVA10692:Huge Mods
    Bzoj1009: [HNOI2008]GT考试
    Bzoj1212: [HNOI2004]L语言
    【国家集训队2012】tree(伍一鸣)
  • 原文地址:https://www.cnblogs.com/AaronPasi/p/6817151.html
Copyright © 2020-2023  润新知