目前已经实现3级之内的任意级树形结构展示(如果想增加更多级,需要扩展排序算法),并支持单选和多选(使用不同的适配器)。
实现使用的控件:ListView
首先,最重要的应该是数据源的格式,支持树形结构的数据源,每条数据应该都要指明它的父级是谁,本文为parent_org,并把每条数据都存放在一个HashMap里面。这里使用的除了parent_org(父营业部),还有org_name(营业部名称),org_code(营业部编号)。
其次,对数据进行排序。因为只使用ListView实现,所以需要实现层次结构的树形结构,需要对数据按父级->2级->3级->父级->2级->父级->2级->父级->2级->3级这样的顺序排序。
接着,实现适配器,展现树形结构。
最后,调用适配器,并调用适配器里的方法获取选择结果。
排序算法:
/** * 查询操作员营业部列表类 * * @author xinyan * */ public class QueryIntOrgListWork extends BaseWork implements Runnable { private static final String TAG = "QueryIntOrgListWork"; HashMap<String, String> params; public QueryIntOrgListWork(Context context, Map args) { super(context, args); } public QueryIntOrgListWork(Context context, View layoutView, Map args) { super(context, layoutView, args); } @Override public void work() { params = new HashMap<String, String>(); AppState appState = AppState.getState(); BrokerInfo brokerInfo = appState.getBrokerInfo(); // params.put("uuid", brokerInfo.getUuid()); Utils.putUuid(params); params.put("funcno", "20017"); params.put("org_type", "0"); params.put("right_flag", "1"); try { HttpFactory httpFactory = new HttpFactory(); byte[] bytes = httpFactory.send( Configuration.getString("system.http"), params, HttpFactory.POST); if (bytes == null) { return; } String result = new String(bytes, Configuration.getString("system.charSet")); JSONObject jsonObject = new JSONObject(result); // 增加登录过期或账号已在其他地方登录的处理 try { String strPrompt = jsonObject.getString("errorInfo"); String strErrorNo = jsonObject.getString("errorNo"); if (!StringHelper.isEmpty(strPrompt)) { if (strPrompt.contains("未登") || strPrompt.contains("其它地方登陆")) { sendMessage(new LoginExpiredUiWork(null)); return; } } // 如果访问接口异常,提示信息 if (!strErrorNo.equals("0")) { HashMap<String, String> param = new HashMap<String, String>(); if (null != strPrompt) { param.put("errorInfo", "调用查询操作员机构列表接口:" + strPrompt); } else { param.put("errorInfo", ""); } sendMessage(new CallInterfaceExceptionUiWork(param)); if ("-111".equals(strErrorNo) || "-110".equals(strErrorNo) || "-2008000".equals(strErrorNo)) { return; } } } catch (Exception e) { } String errorNo = jsonObject.getString("errorNo"); if (errorNo.equals("-1")) { String errorInfo = jsonObject.getString("errorInfo"); Map<String, String> map = new HashMap<String, String>(); map.put("errorNo", "-1"); map.put("errorInfo", errorInfo); } else if (errorNo.equals("-999")) { Intent intent = new Intent("com.thinkive.action.MAIN"); context.startActivity(intent); } else if (errorNo.equals("0")) { JSONArray array = jsonObject.getJSONArray("results"); ArrayList<HashMap<String, Object>> dataList = new ArrayList<HashMap<String, Object>>(); HashMap<String, Object> dataItem = null; for (int i = 0; i < array.length(); i++) { dataItem = new HashMap<String, Object>(); JSONObject item = array.getJSONObject(i); String org_name = item.getString("org_name"); // String area_addr = item.getString("area_addr"); // // String org_type = item.getString("org_type"); // String right_flag = item.getString("right_flag"); // String zip_code = item.getString("zip_code"); // String acct_len = item.getString("acct_len"); String org_code = item.getString("org_code"); // String acct_prefix = item.getString("acct_prefix"); // String org_full_name = item.getString("org_full_name"); String parent_org = item.getString("parent_org"); // String area_no = item.getString("area_no"); // String org_status = item.getString("org_status"); // String org_cls = item.getString("org_cls"); dataItem.put("org_name", org_name); dataItem.put("org_code", org_code); dataItem.put("parent_org", parent_org); dataList.add(dataItem); } AppState.getState().getBrokerInfo() .setIntOrgs(regroupData(dataList)); } } catch (Exception e) { e.printStackTrace(); } } /** * 对数据根据子父级关系进行重组 */ private List<HashMap<String, Object>> regroupData( List<HashMap<String, Object>> dataList) { HashMap<String, Object> item = null; ArrayList<HashMap<String, Object>> regroupedDataList = null; for (int i = 0; i < dataList.size(); i++) { item = dataList.get(i); String strOrg = item.get("org_code").toString(); String strParentOrg = item.containsKey("parent_org") ? item.get( "parent_org").toString() : ""; // 如果没有父营业部,遍历列表是否存在子营业部 if (StringHelper.isEmpty(strParentOrg)) { HashMap<String, Object> item2 = null; ArrayList<HashMap<String, Object>> childList = null; // 遍历列表是否存在子营业部 for (int j = 0; j < dataList.size(); j++) { if (i == j) { continue; } item2 = dataList.get(j); // 列表存在子营业部 if (strOrg.equals(item2.get("parent_org"))) { childList = new ArrayList<HashMap<String, Object>>(); if (null != item2.get("child")) { // 如果该营业部的子营业部还存在子营业部,那么此营业部是1级,最高级 item2.put("org_level", "1"); } else { // 如果该营业部的子营业部不存在子营业部,那么将此营业部暂定为2级 item2.put("org_level", "2"); } childList.add(item2); }// end if }// end for item.put("child", childList); } else {// 如果有父营业部 HashMap<String, Object> parentOrgItem = null; boolean isParentOrgExist = false; // 首先检查列表是否存在父营业部 for (int j = 0; j < dataList.size(); j++) { if (i == j) { continue; } parentOrgItem = dataList.get(j); // 列表存在父营业部 if (strParentOrg.equals(parentOrgItem.get("org_code"))) { isParentOrgExist = true; // 加入当前营业部到该父营业部的子营业部列表,标记当前项为2级营业部 item.put("org_level", "2"); ArrayList<HashMap<String, Object>> childList = null; if (null != parentOrgItem.get("child")) { childList = (ArrayList<HashMap<String, Object>>) parentOrgItem .get("child"); } else { childList = new ArrayList<HashMap<String, Object>>(); } // 检查子营业部列表是否已经存在该营业部 boolean isOrgExist = false; if (childList.size() > 0) { HashMap<String, Object> childItem = null; for (int k = 0; k < childList.size(); k++) { childItem = (HashMap<String, Object>) childList .get(k); if (childItem.get("org_code").toString() .equals(item.get("org_code"))) { isOrgExist = true; } } } if (!isOrgExist) { childList.add(item); parentOrgItem.put("child", childList); } break; }// end if }// end for // 检查完了是否存在父营业部之后,再检查列表是否存在该营业部的子营业部 HashMap<String, Object> childOrgItem = null; ArrayList<HashMap<String, Object>> childList = new ArrayList<HashMap<String, Object>>(); // 遍历列表是否存在子营业部 for (int k = 0; k < dataList.size(); k++) { if (i == k) { continue; } childOrgItem = dataList.get(k); // 列表存在子营业部 if (strOrg.equals(childOrgItem.get("parent_org"))) { if (null != childOrgItem.get("child")) { // 如果该营业部的子营业部还存在子营业部,那么此营业部是1级,最高级 childOrgItem.put("org_level", "1"); } else { // 如果该营业部的子营业部不存在子营业部,那么将此营业部暂定为2级 childOrgItem.put("org_level", "2"); } childList.add(childOrgItem); }// end if }// end for if (childList.size() > 0) { item.put("child", childList); // 如果当前营业部存在父营业部和子营业部,那么就是2级 if (isParentOrgExist) { item.put("org_level", "2"); } else { // 不存在父营业部,那么是1级 item.put("org_level", "1"); } } else { // 不存在子营业部 if (isParentOrgExist) { // 不存在子营业部,但存在父营业部,那么是2级 item.put("org_level", "2"); } else { // 不存在子营业部和父营业部,那么是1级 item.put("org_level", "1"); } } }// end else }// end for // 当数据按数学结构组织好之后,按顺序排列为列表,以便在listview里显示 regroupedDataList = new ArrayList<HashMap<String, Object>>(); HashMap<String, Object> tempItem = null; for (int i = 0; i < dataList.size(); i++) { tempItem = dataList.get(i); if ("1".equals(tempItem.get("org_level").toString())) { regroupedDataList.add(tempItem); // 把子营业部加入到重组后的列表 if (tempItem.containsKey("child")) { ArrayList<HashMap<String, Object>> childList = (ArrayList<HashMap<String, Object>>) tempItem .get("child"); HashMap<String, Object> childItem = null; if (childList.size() > 0) { for (int j = 0; j < childList.size(); j++) { childItem = (HashMap<String, Object>) childList .get(j); regroupedDataList.add(childItem); // 检查子营业部是否还有子营业部 if (childItem.containsKey("child")) { ArrayList<HashMap<String, Object>> childList2 = (ArrayList<HashMap<String, Object>>) childItem .get("child"); HashMap<String, Object> childItem2 = null; if (childList2.size() > 0) { for (int k = 0; k < childList2.size(); k++) { childItem2 = (HashMap<String, Object>) childList2 .get(k); regroupedDataList.add(childItem2); }// end for }// end if }// end if }// end for }// end if }// end if }// end if }// end for for (int i = 0; i < regroupedDataList.size(); i++) { if (regroupedDataList.get(i).containsKey("child")) { regroupedDataList.get(i).remove("child"); } } // 父级、子级都得排序 Log.i(TAG, "重组前的营业部数据" + dataList); Log.i(TAG, "重组后的营业部数据" + regroupedDataList); return regroupedDataList; } @Override public void run() { work(); } }
多选营业部树形列表适配器:
/** * 多选营业部树形列表适配器 * * @author xinyan * @date 2013-5-27 */ public class MultiCheckIntOrgListAdapter extends BaseAdapter { public static final String TAG = "MultiCheckIntOrgListAdapter"; public static final String CHECKED_TRUE = "1"; public static final String CHECKED_FALSE = "0"; public static final String IS_CHECKED = "IS_CHECKED"; private List<HashMap<String, Object>> mDataList; private LayoutInflater mInflater; public MultiCheckIntOrgListAdapter(Context context, List<HashMap<String, Object>> dataList) { this.mInflater = LayoutInflater.from(context); mDataList = dataList; setDefaultChecked(); } @Override public int getCount() { return mDataList.size(); } @Override public Object getItem(int position) { return mDataList.get(position); } @Override public long getItemId(int position) { return 0; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder = null; final HashMap<String, Object> dataItem = mDataList.get(position); if (null == convertView) { holder = new ViewHolder(); convertView = mInflater.inflate(R.layout.lv_item_tree_structure, null); holder.cb = (CheckBox) convertView .findViewById(R.id.lv_item_tree_structure_cb); holder.text = (TextView) convertView .findViewById(R.id.lv_item_tree_structure_text); holder.joinBottom = (ImageView) convertView .findViewById(R.id.lv_item_tree_structure_join_bottom); holder.leaf = (ImageView) convertView .findViewById(R.id.lv_item_tree_structure_leaf); holder.joinBottom2 = (ImageView) convertView .findViewById(R.id.lv_item_tree_structure_join_bottom2); holder.leaf2 = (ImageView) convertView .findViewById(R.id.lv_item_tree_structure_leaf2); holder.tree = (ImageView) convertView .findViewById(R.id.lv_item_tree_structure_tree); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.tree.setVisibility(View.GONE); holder.joinBottom.setVisibility(View.GONE); holder.leaf.setVisibility(View.GONE); holder.joinBottom2.setVisibility(View.GONE); holder.leaf2.setVisibility(View.GONE); // 如果是父级 if ("1".equals(dataItem.get("org_level"))) { holder.tree.setVisibility(View.VISIBLE); holder.joinBottom.setVisibility(View.GONE); holder.leaf.setVisibility(View.GONE); holder.joinBottom2.setVisibility(View.GONE); holder.leaf2.setVisibility(View.GONE); } else if ("2".equals(dataItem.get("org_level"))) {// 如果是子级 holder.joinBottom2.setVisibility(View.GONE); holder.leaf2.setVisibility(View.GONE); holder.tree.setVisibility(View.GONE); holder.joinBottom.setVisibility(View.VISIBLE); holder.leaf.setVisibility(View.VISIBLE); } else { // 3级 holder.tree.setVisibility(View.GONE); holder.joinBottom2.setVisibility(View.VISIBLE); holder.leaf2.setVisibility(View.VISIBLE); holder.joinBottom.setVisibility(View.VISIBLE); holder.leaf.setVisibility(View.VISIBLE); } try { holder.text.setText(dataItem.get("org_code").toString() + " " + dataItem.get("org_name").toString()); } catch (Exception e) { } final CheckBox cb = holder.cb; if (CHECKED_TRUE.equals(dataItem.get(IS_CHECKED))) { cb.setChecked(true); } else { cb.setChecked(false); } convertView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (MotionEvent.ACTION_DOWN == event.getAction()) { if (cb.isChecked()) { cb.setChecked(false); dataItem.put(IS_CHECKED, CHECKED_FALSE); } else { cb.setChecked(true); dataItem.put(IS_CHECKED, CHECKED_TRUE); } if ("1".equals(dataItem.get("org_level").toString())) { // 遍历子营业部 HashMap<String, Object> item = null; for (int i = position + 1; i < mDataList.size(); i++) { item = mDataList.get(i); if ("2".equals(item.get("org_level")) || "3".equals(item.get("org_level"))) { if (cb.isChecked()) { item.put(IS_CHECKED, CHECKED_TRUE); } else { item.put(IS_CHECKED, CHECKED_FALSE); } } else if ("1".equals(item.get("org_level"))) { break; }// end else if }// end for notifyDataSetChanged(); }// end if else if ("2".equals(dataItem.get("org_level").toString())) { // 遍历子营业部 HashMap<String, Object> item = null; for (int i = position + 1; i < mDataList.size(); i++) { item = mDataList.get(i); if ("3".equals(item.get("org_level"))) { if (cb.isChecked()) { item.put(IS_CHECKED, CHECKED_TRUE); } else { item.put(IS_CHECKED, CHECKED_FALSE); } } else if ("1".equals(item.get("org_level"))) { break; }// end else if }// end for notifyDataSetChanged(); }// end else if return true; } return false; } }); return convertView; } /** * 拿到已选择营业部的代码字符串,代码之间逗号分隔 * * @return */ public String getSelectedOrgCodes() { StringBuilder sbOrgCodes = new StringBuilder(); for (int i = 0; i < mDataList.size(); i++) { if (mDataList.get(i).get(IS_CHECKED).equals(CHECKED_TRUE)) { if (sbOrgCodes.length() <= 2) { sbOrgCodes.append(mDataList.get(i).get("org_code") .toString()); } else { sbOrgCodes.append("," + mDataList.get(i).get("org_code").toString()); }// end else }// end if }// end for Log.i(TAG, "getSelectedOrgCodes: " + sbOrgCodes.toString()); return sbOrgCodes.toString(); } /** * 拿到已选择的营业部(org_code+org_name) * * @return */ public String getSelectedOrg() { StringBuilder sbOrg = new StringBuilder(); for (int i = 0; i < mDataList.size(); i++) { if (mDataList.get(i).get(IS_CHECKED).equals(CHECKED_TRUE)) { String strText = mDataList.get(i).get("org_code").toString() + " " + mDataList.get(i).get("org_name").toString(); if (sbOrg.length() <= 2) { sbOrg.append(strText); } else { sbOrg.append("," + strText); }// end else }// end if }// end for Log.i(TAG, "getSelectedOrg: " + sbOrg.toString()); return sbOrg.toString(); } /** * 默认全部选中 */ private void setDefaultChecked() { for (int i = 0; i < mDataList.size(); i++) { mDataList.get(i).put(IS_CHECKED, CHECKED_TRUE); } } class ViewHolder { TextView text; CheckBox cb; ImageView tree;// 树形图 ImageView joinBottom;// 连接线 ImageView leaf;// 子节点图 ImageView joinBottom2;// 3级连接线 ImageView leaf2;// 3级子节点图 } }
单选营业部树形列表适配器
/** * 单选营业部树形列表适配器 * * @author xinyan * @date 2013-5-27 */ public class SingleChoiceIntOrgListAdapter extends BaseAdapter { public static final String TAG = "SingleChoiceIntOrgListAdapter"; public static final String CHECKED_TRUE = "1"; public static final String CHECKED_FALSE = "0"; public static final String IS_CHECKED = "IS_CHECKED"; private List<HashMap<String, Object>> mDataList; private LayoutInflater mInflater; public SingleChoiceIntOrgListAdapter(Context context, List<HashMap<String, Object>> dataList) { this.mInflater = LayoutInflater.from(context); mDataList = dataList; } @Override public int getCount() { return mDataList.size(); } @Override public Object getItem(int position) { return mDataList.get(position); } @Override public long getItemId(int position) { return 0; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder = null; final HashMap<String, Object> dataItem = mDataList.get(position); if (null == convertView) { holder = new ViewHolder(); convertView = mInflater.inflate( R.layout.lv_item_tree_structure_single_choice, null); holder.cb = (CheckBox) convertView .findViewById(R.id.lv_item_tree_structure_cb); holder.text = (TextView) convertView .findViewById(R.id.lv_item_tree_structure_text); holder.joinBottom = (ImageView) convertView .findViewById(R.id.lv_item_tree_structure_join_bottom); holder.leaf = (ImageView) convertView .findViewById(R.id.lv_item_tree_structure_leaf); holder.joinBottom2 = (ImageView) convertView .findViewById(R.id.lv_item_tree_structure_join_bottom2); holder.leaf2 = (ImageView) convertView .findViewById(R.id.lv_item_tree_structure_leaf2); holder.tree = (ImageView) convertView .findViewById(R.id.lv_item_tree_structure_tree); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.tree.setVisibility(View.GONE); holder.joinBottom.setVisibility(View.GONE); holder.leaf.setVisibility(View.GONE); holder.joinBottom2.setVisibility(View.GONE); holder.leaf2.setVisibility(View.GONE); // 如果是父级 if ("1".equals(dataItem.get("org_level"))) { holder.tree.setVisibility(View.VISIBLE); holder.joinBottom.setVisibility(View.GONE); holder.leaf.setVisibility(View.GONE); holder.joinBottom2.setVisibility(View.GONE); holder.leaf2.setVisibility(View.GONE); } else if ("2".equals(dataItem.get("org_level"))) {// 如果是子级 holder.joinBottom2.setVisibility(View.GONE); holder.leaf2.setVisibility(View.GONE); holder.tree.setVisibility(View.GONE); holder.joinBottom.setVisibility(View.VISIBLE); holder.leaf.setVisibility(View.VISIBLE); } else { // 3级 holder.tree.setVisibility(View.GONE); holder.joinBottom2.setVisibility(View.VISIBLE); holder.leaf2.setVisibility(View.VISIBLE); holder.joinBottom.setVisibility(View.VISIBLE); holder.leaf.setVisibility(View.VISIBLE); } try { holder.text.setText(dataItem.get("org_code").toString() + " " + dataItem.get("org_name").toString()); } catch (Exception e) { } convertView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (MotionEvent.ACTION_DOWN == event.getAction()) { dataItem.put(IS_CHECKED, CHECKED_TRUE); // 把其它的营业部设置为false for (int i = 0; i < mDataList.size(); i++) { if (i == position) { continue; } else { mDataList.get(i).put(IS_CHECKED, CHECKED_FALSE); } } } return false; } }); return convertView; } /** * 拿到已选择营业部的代码字符串,代码之间逗号分隔 * * @return */ public String getSelectedOrgCodes() { StringBuilder sbOrgCodes = new StringBuilder(); for (int i = 0; i < mDataList.size(); i++) { if (mDataList.get(i).get(IS_CHECKED).equals(CHECKED_TRUE)) { if (sbOrgCodes.length() <= 2) { sbOrgCodes.append(mDataList.get(i).get("org_code") .toString()); } else { sbOrgCodes.append("," + mDataList.get(i).get("org_code").toString()); }// end else }// end if }// end for Log.i(TAG, "getSelectedOrgCodes: " + sbOrgCodes.toString()); return sbOrgCodes.toString(); } /** * 拿到已选择的营业部(org_code+org_name) * * @return */ public String getSelectedOrg() { StringBuilder sbOrg = new StringBuilder(); for (int i = 0; i < mDataList.size(); i++) { if (mDataList.get(i).get(IS_CHECKED).equals(CHECKED_TRUE)) { String strText = mDataList.get(i).get("org_code").toString() + " " + mDataList.get(i).get("org_name").toString(); if (sbOrg.length() <= 2) { sbOrg.append(strText); } else { sbOrg.append("," + strText); }// end else }// end if }// end for Log.i(TAG, "getSelectedOrg: " + sbOrg.toString()); return sbOrg.toString(); } /** * 默认全部选中 */ private void setDefaultChecked() { for (int i = 0; i < mDataList.size(); i++) { mDataList.get(i).put(IS_CHECKED, CHECKED_TRUE); } } /** * 设置默认选中的营业部 * * @param orgCode */ public void setDefaultChecked(String orgCode) { for (int i = 0; i < mDataList.size(); i++) { if (orgCode.contains(mDataList.get(i).get("org_code").toString())) { mDataList.get(i).put(IS_CHECKED, CHECKED_TRUE); } else { // 因为查询框里使用的多选营业部框跟这里使用的一个数据源, // 为了避免其它地方对这造成的影响,所以全部初始化好 mDataList.get(i).put(IS_CHECKED, CHECKED_FALSE); } } } class ViewHolder { TextView text; CheckBox cb; ImageView tree;// 树形图 ImageView joinBottom;// 连接线 ImageView leaf;// 子节点图 ImageView joinBottom2;// 3级连接线 ImageView leaf2;// 3级子节点图 } }
ListView的item布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" > <ImageView android:id="@+id/lv_item_tree_structure_tree" android:layout_width="20dp" android:layout_height="20dp" android:layout_gravity="center_vertical" android:scaleType="fitXY" android:src="@drawable/customer_account_mgr" android:visibility="gone" /> <ImageView android:id="@+id/lv_item_tree_structure_join_bottom" android:layout_width="20dp" android:layout_height="20dp" android:layout_gravity="center_vertical" android:scaleType="fitXY" android:src="@drawable/joinbottom" android:visibility="gone" /> <ImageView android:id="@+id/lv_item_tree_structure_leaf" android:layout_width="20dp" android:layout_height="20dp" android:layout_gravity="center_vertical" android:scaleType="fitXY" android:src="@drawable/leaf" android:visibility="gone" /> <ImageView android:id="@+id/lv_item_tree_structure_join_bottom2" android:layout_width="20dp" android:layout_height="20dp" android:layout_gravity="center_vertical" android:scaleType="fitXY" android:src="@drawable/joinbottom" android:visibility="gone" /> <ImageView android:id="@+id/lv_item_tree_structure_leaf2" android:layout_width="20dp" android:layout_height="20dp" android:layout_gravity="center_vertical" android:scaleType="fitXY" android:src="@drawable/leaf" android:visibility="gone" /> <CheckBox android:id="@+id/lv_item_tree_structure_cb" style="@style/custom_checkbox" android:layout_width="30dp" android:layout_height="30dp" android:layout_gravity="center_vertical" android:layout_marginLeft="5dp" android:clickable="false" /> <TextView android:id="@+id/lv_item_tree_structure_text" android:layout_width="400dp" android:layout_height="40dp" android:gravity="center_vertical" android:paddingLeft="10dp" android:textColor="@color/black" android:textSize="20sp" /> </LinearLayout>
父级标志图
子级标志图
父级和子级的连接线