zoukankan      html  css  js  c++  java
  • Android学习小Demo(21)ListView的联动选择

    在日常的App开发中,尤其是在开发生活服务的应用上,非常多时候,我们会须要联动地展现省市区的数据等,需求大概例如以下:

    1)展现全部省份

    2)当点击某省份的时候,在二级菜单上展现此省份以下所属的城市列表

    3)选中返回,显示我们选中的城市

    4)当又一次进入选择页面的时候,标识出我们上一次选中(或者说当前已经选择)的值

    下图是一个类似的ListView联动选择控件。

    1)首先定义一个Layout,左右各放置一个ListView,大概界面例如以下:


    2)自己定义一个控件(ValuePicker),在控件中,首先我们要获取填充两个ListView的数据,假设在是省市的话,就要获取省市的数据,在这个Demo中,我们利用DataProvider这个类,来模拟一些数据,例如以下:

    public class DataProvider {
    
    	public static final String[] summaries = {
    		"A",
    		"B",
    		"C",
    		"D",
    		"E",
    		"F",
    		"G",
    		"H"
    	};
    	
    	public static final Map<String, String[]> details = new HashMap<>(); 
    	
    	static {
    		 details.put("A",new String[] {"A1", "A2", "A3"});
    		 details.put("B",new String[] {"B1", "B2", "B3"});
    		 details.put("C",new String[] {"C1", "C2", "C3"});
    		 details.put("D",new String[] {"D1", "D2", "D3"});
    		 details.put("E",new String[] {"E1", "E2", "E3"});
    		 details.put("F",new String[] {"F1", "F2", "F3"});
    		 details.put("G",new String[] {"G1", "G2", "G3"});
    		 details.put("H",new String[] {"G1", "H2", "H3"});
    	}
    		
    }

    然后在ValuePicker中引用这些数据。

    private String[] summaries = DataProvider.summaries;
    private Map<String, String[]> details = DataProvider.details;

    3)要将数据绑定到相应的ListView上面,我们必须分别为这两个ListView声明一个Adapter,在这里,我们继承BaseAdapter,创建一个SingleCheckedListAdpater。

    因为Android本身提供的ListView是没有选中的效果的,而基于我们的需求,

    3.1)须要在用户点击Item的时候,设置Item选中的状态,在这里,我们是利用"黑底白字",反转颜色来突出显示的效果。

    3.2)而当我们点击另外一个Item的时候,须要将原来的Item又一次置为未选中的状态,即要又一次设置其背景和字体颜色,又一次变成“白底黑字”,而将当前新选中的Item置为选中。

    3.3)一种特殊情况是,ListView里面的个数超出当前屏幕的话,当我们滑动ListView的时候,ListView是会复用我们之前已经创建过的View的,所以,我们必须对选中的Item做一个特殊处理。被选中的Item,假设滑出屏幕,其相应的View是会被复用的,所以必须在其被复用的时候,将其置为未选中状态,而当其又一次滑入屏幕的时候,将其置为选中状态。

    基于以上几种情况,我们须要在Adapter中记录下某个被选中的Item的位置和View,代码例如以下:

    	/**
    	 * the View checked
    	 */
    	private TextView mLastCheckedView = null;
    	/**
    	 * the position in the data
    	 */
    	private int mCheckedPosition = -1;
    	
    	public void setCheckedPosition(int position){
    		mCheckedPosition = position;
    	}
    		
    	...
    	/**
    	 * @param checkedView the checkedView to set
    	 */
    	public void setCheckedView(View checkedView) {
    		setViewSelected(mLastCheckedView, false);	
    		TextView textView = (TextView)checkedView;
    		setViewSelected(textView, true);		
    	}
    	
    	private void setViewSelected(TextView view, boolean selected){
    		if(view != null){
    			if(selected){
    				view.setBackgroundColor(mContext.getResources().getColor(R.color.black));
    				view.setTextColor(mContext.getResources().getColor(R.color.white));
    				mLastCheckedView = view;
    			}else{
    				view.setBackgroundColor(mContext.getResources().getColor(R.color.white));
    				view.setTextColor(mContext.getResources().getColor(R.color.black));
    			}
    		}
    	}

    在getView的时候,依据是否选中的位置来处理View的状态,例如以下:

    	@Override
    	public View getView(int position, View convertView, ViewGroup root) {		
    		ViewHolder holder = null;
    		if (convertView == null) {
    			
    			holder = new ViewHolder();
    			convertView = LayoutInflater.from(mContext).inflate(R.layout.list_item_card_number, null);			
    			holder.name = (TextView) convertView.findViewById(R.id.textView1);
    			convertView.setTag(holder);
    
    		}else{
    			
    			holder = (ViewHolder) convertView.getTag();
    			
    		}
    		
    		setViewSelected(holder.name, mCheckedPosition == position);		
    		holder.name.setText(mData[position]);		
    		return convertView;
    	}	

    另外,因为是联动的ListView,当左边的某个Item被点击的时候,右边的ListView要相应的刷新数据,所以Adapter也必须提供相应的入口去刷新数据。

    	public void setData(String[] data){
    		mData = data;
    		mLastCheckedView = null;
    		mCheckedPosition = -1;
    		notifyDataSetChanged();		
    	}

    4)而非常显然,我们也必须在ValuePicker这个控件中,记录下选中的值,才可以在数据源中查找出其相应的位置,并将其传递给Adapter。因此,我们在ValuePicker中会定义两个位置和相应的值。

    	private int mPosLeft = -1;	
    	private String mCurLeft;
    	private int mPosRight = -1;
    	private String mCurRight;
    	

    这仅仅是在这个Demo中简单的定义,在详细的应用上,这几个变量可依据详细的业务信息自己定义变量名。

    下面代码为找出相应的位置。

    for(int i = 0; i < len; i++){
    	
    	String summary = summaries[i];
    	
    	if(summary.equals(mCurLeft)){
    		mPosLeft = i;
    		break;
    	}		
    				
    }
    
    if(mPosLeft >= 0){
    	String summary = summaries[mPosLeft];
    	String[] right = details.get(summary);
    	int lenOfRight = right.length;
    	
    	for(int j = 0; j < lenOfRight; j++){
    		
    		String detail = right[j];
    		
    		if(mCurRight != null && detail.equals(mCurRight)){
    			mPosRight = j;
    			break;
    		}
    	}
    }

    接着在ListView绑定Adapter的时候,我们须要将这个值传过去,在这里不用去关心其值,由于在Adapter中会进行处理。

    final SingleCheckedListAdapter lAdapter = new SingleCheckedListAdapter(mContext, summaries);
    lAdapter.setCheckedPosition(mPosLeft);
    lvLeft.setAdapter(lAdapter);
    		
    String[] rights = new String[]{};
    if(mPosLeft >= 0 && mPosRight >= 0){
    	rights = details.get(summaries[mPosLeft]);
    }
    		
    final SingleCheckedListAdapter rAdapter = new SingleCheckedListAdapter(mContext, rights);		
    rAdapter.setCheckedPosition(mPosRight);		
    lvRight.setAdapter(rAdapter);		
    在上面也有一点要注意的就是,当左边值有选中的时候,我们要将其相应的右边的ListView的内容也给找出来,并将其绑定到右边的ListView中。

    而在OnItemClickListener中,我们就要依据我们选中的位置,又一次去置这个值。

    		lvLeft.setOnItemClickListener(new OnItemClickListener() {
    			@Override
    			public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
    				
    				mCurRight = null;
    				lAdapter.setCheckedPosition(position);
    				lAdapter.setCheckedView(view);
    				mPosLeft = position;
    				rAdapter.setData(details.get(summaries[mPosLeft]));
    			}
    		});
    		
    		lvRight.setOnItemClickListener(new OnItemClickListener() {
    			@Override
    			public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
    				rAdapter.setCheckedPosition(position);
    				rAdapter.setCheckedView(view);
    				mCurRight = details.get(summaries[mPosLeft])[position];
    			}
    		});	

    最后,在ValuePicker中,我们还定义了一个OnClickListener,这个listener的目的是为了将这个button的操作事件传递给调用者,由于当选择完之后,兴许的处理逻辑应该由调用者来处理。

    /**
     * Listener to handle the logic when current city card number is selected
     */
    public OnClickListener mListener;
    
    /**
     * @param listener the listener to set
     */
    public void setButtonOnClickListener(OnClickListener listener) {
    	this.mListener = listener;
    }
    
    ...	
    	
    btnConfirm.setOnClickListener(new OnClickListener() {			
    	@Override
    	public void onClick(View arg0) {
    		if(mListener != null){
    			mListener.onClick(arg0);
    		}
    	}
    });

    5)当上述一切都实现了之后,这个自己定义的联动ListView选择控件也就完毕了,能够在layout中使用它了,例如以下:

        <com.lms.twofoldselector.ValuePicker
            android:id="@+id/vpTest"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
        </com.lms.twofoldselector.ValuePicker>

    在Activity中初始化它,例如以下:

    public class ValuePickerMockActivity extends Activity implements OnClickListener{
    
    	public static final String SELECTED_LEFT = "SELECTED_LEFT";
    	public static final String SELECTED_RIGHT = "SELECTED_RIGHT";
    	
    	private ValuePicker vpTest;
    		
    	@Override
    	protected void onCreate(Bundle savedInstanceState){
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_vp_mock);
    		
    		vpTest = (ValuePicker) findViewById(R.id.vpTest);
    		vpTest.setButtonOnClickListener(this);
    		
    		//set the selected view
    		Intent intent = getIntent();
    		String leftValue = intent.getStringExtra(SELECTED_LEFT);
    		String rightValue = intent.getStringExtra(SELECTED_RIGHT);
    		
    		vpTest.setLeftValue(leftValue);
    		vpTest.setRightValue(rightValue);
    		
    		vpTest.initialize();
    	}
    
    	@Override
    	public void onClick(View arg0) {		
    		String rightValue = vpTest.getRightValue();
    		if(rightValue == null){
    			Toast.makeText(this, "请选择右边的值", Toast.LENGTH_SHORT).show();
    			return;
    		}
    		Intent data = new Intent();
    		data.putExtra(SELECTED_LEFT, vpTest.getLeftVaue());
    		data.putExtra(SELECTED_RIGHT, vpTest.getRightValue());
    		setResult(RESULT_OK, data);
    		finish();		
    	}
    
    }

    结束!源码下载。

  • 相关阅读:
    ServiceStack支持跨域提交
    CookiesHelper
    poj 3669 线段树成段更新+区间合并
    poj2528 线段树+离散化
    hdu3308 线段树 区间合并
    hdu1542矩阵的并 线段树+扫描线
    hdu1255 矩阵的交 线段树+扫描线
    简单单点更新线段树
    树状数组模版
    hdu1873优先队列
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4391864.html
Copyright © 2011-2022 走看看