在日常的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();
} }

结束!源码下载。

最新文章

  1. SQL初级第三课(下)
  2. struts2 redirect 配置动态传递参数
  3. mysql查询昨天本周上周上月
  4. PostgreSQL的AnynonArray的例子
  5. 如何利用OpenCV自带的级联分类器训练程序训练分类器
  6. MySQL外键约束On Delete、On Update各取值的含义
  7. PHP面向对象(OOP):__toString()方法
  8. &lt;离散数学&gt;学习笔记1--逻辑和证明
  9. hdu 4292 Food 网络流
  10. hdu2191 悼念512汶川大地震遇难同胞——珍惜如今,感恩生活
  11. 虚拟化平台cloudstack新版本的调试
  12. HDU 5934 强联通分量
  13. 【Luogu3768】简单的数学题(莫比乌斯反演,杜教筛)
  14. Git源代码管理
  15. SSL证书安装(Tomcat)腾讯云服务器
  16. bzoj3277
  17. 【scrapy】爬虫中报Forbidden by robots.txt
  18. C盘空间不够,清除VS下的 Font Cache
  19. dedecms获取当前文章所在栏目URL
  20. 深入理解Linux网络技术内幕——内核基础架构和组件初始化

热门文章

  1. Java多播通讯框架 JGroups(转)
  2. C++ tree(1)
  3. gem 安装nokigiri
  4. FastJson基本使用
  5. SWI-Prolog
  6. ASP.NET农历时间显示(两)
  7. Cocos2d-x 文本渲染
  8. Qt Quick 布局演示
  9. Php面向对象 – 继承和重写
  10. NSOJ 一个人的旅行(图论)