Android UI設計和形成原理(實現三級選單)
阿新 • • 發佈:2019-01-25
本次要實現的是動態編碼之三級選單的實現,在實際應用開發中經常會使用到三級選單,比如商城專案中的省市區,分類等等。
問題:介面載入是一次性new 大量的控制元件還是先載入一級選單,點選一級選單載入他對應的二級選單
答:這個很明顯是後者更符合開發思維,因為客戶不可能每個級別都一一點開,能點開所有級別的只有萬惡的測試人員。
介面載入方案:
1.資料進行一次性載入(一次把服務端傳遞過來的資料解析封裝到資料模型)
2.先載入一級選單,點選一級選單載入他對應的二級選單
技術要點:動態編寫介面,遞迴演算法,javaBean;
實現過程
1.建立javabean封裝資料
package com.xiaoyao.android.mytree;以上要特別說明的是getLevel()方法在該方法中返回值是當前的級別,在此通過了遞迴的演算法得到當前級別的父級別然後+1從而得到當前的級別import java.util.ArrayList; import java.util.List; /** * Created by Administrator on 2016/6/18. */ public class Node { private int id;//當前節點的自身的ID; private int pId = 0;//根節點pId private String name;//選單的名字 private int level;//當前的級別 private boolean isExpand = false;//是否只展開一個子選單 private Node parent;//父親節點 privateList<Node> children=new ArrayList<>(); public Node() { super(); } public Node(int id,int pId,String name){ super(); this.id=id; this.pId=pId; this.name=name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getpId() { return pId; } public void setpId(int pId) { this.pId = pId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getLevel() { return parent==null?0:parent.getLevel()+1; } public void setLevel(int level) { this.level = level; } public boolean isExpand() { return isExpand; } public void setExpand(boolean expand) { isExpand = expand; if (!isExpand){ for (Node node:children){ node.setExpand(isExpand); } } } public Node getParent() { return parent; } public void setParent(Node parent) { this.parent = parent; } public List<Node> getChildren() { return children; } public void setChildren(List<Node> children) { this.children = children; } /** * 是否為根節點 */ public boolean isRoot(){ return parent==null; } /** * 判斷父節點是否展開 */ public boolean isParentExpand(){ if (parent==null){ return false; } return parent.isExpand(); } /** * 是否為葉子節點 */ public boolean isLeaf(){ return children.size()==0; } }
return parent == null ? 0 :parent.getLevel() + 1 ;
2.建立三級列表檢視
package com.xiaoyao.android.mytree; import android.content.Context; import android.graphics.Color; import android.text.Layout; import android.view.View; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; import java.util.List; /** * Created by Administrator on 2016/6/18. */ public class MyTree extends LinearLayout { List<Node> mDatas;//資料來源 private Context mContext;//上下文 /** * 以下為容器引數 */ LayoutParams rootLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); LayoutParams itemLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); LayoutParams dividerLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, 1); public MyTree(Context context) { super(context); this.mContext=context; } public void setData(List<Node> mDatas) { this.mDatas = mDatas; initView(); } /** * 初始化介面 */ private void initView(){ //新增一個滾動檢視 ScrollView scrollView=new ScrollView(mContext); scrollView.setLayoutParams(rootLayoutParams); scrollView.setBackgroundColor(Color.parseColor("#f0f0f0")); //新增一個線性佈局作為一級選單的容器 LinearLayout rootLayout=new LinearLayout(mContext); rootLayout.setLayoutParams(rootLayoutParams); rootLayout.setOrientation(LinearLayout.VERTICAL); //載入一級選單 //得到資料來源中所有的一級選單 List<Node> rootNodes=getListByPid(0); for (int i=0;i<rootNodes.size();i++){ Node rootNode=rootNodes.get(i); //初始化一級佈局,在這裡只是簡單的使用TextView作為佈局 TextView firstLevelView=new TextView(mContext); firstLevelView.setLayoutParams(itemLayoutParams); //新增ID firstLevelView.setId(rootNode.getId()); //設定名字 firstLevelView.setText(rootNode.getName()); firstLevelView.setTextColor(Color.WHITE); firstLevelView.setBackgroundColor(Color.BLUE); firstLevelView.setPadding(30,30,30,30); //新增一級選單到一級選單的容器中 rootLayout.addView(firstLevelView); //新增分割線,沒新增一個一級選單新增一條橫線 rootLayout.addView(getDivider()); //準備載入對應的子選單(2級選單容器) LinearLayout secondLayout=new LinearLayout(mContext); secondLayout.setLayoutParams(itemLayoutParams); secondLayout.setOrientation(LinearLayout.VERTICAL); //在未點選時設定2級選單容器隱藏不可見 secondLayout.setVisibility(GONE); //讓一級選單和他自己對應的2級選單有關聯,做標記 firstLevelView.setTag(secondLayout); //將2級選單的容器新增到父一級選單容器中 rootLayout.addView(secondLayout); //新增完善一級選單的點選事件 firstLevelView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //得到二級選單的佈局容器 LinearLayout parentLayout= (LinearLayout) v.getTag(); if (parentLayout.getChildCount()==0){ //表示沒有新增過 addMenuChild(parentLayout,v.getId()); }else if (parentLayout.isShown()){ parentLayout.setVisibility(View.GONE); }else{ parentLayout.setVisibility(View.VISIBLE); } } }); } scrollView.addView(rootLayout); addView(scrollView); } private void addMenuChild(LinearLayout parentLayout, int id) { //設定容器顯示 parentLayout.setVisibility(VISIBLE); //得到該級別下的猜的的資料來源 List<Node>childNodes=getListByPid(id); if (childNodes.size()>0){ for (int i=0;i<childNodes.size();i++){ Node childNode=childNodes.get(i); //建立2級選單 TextView secondLevelView=new TextView(mContext); secondLevelView.setId(childNode.getId()); secondLevelView.setLayoutParams(itemLayoutParams); secondLevelView.setText(childNode.getName()); secondLevelView.setTextColor(Color.BLACK); List<Node> grandChildNode=getListByPid(childNode.getpId()); if (grandChildNode.size() > 0){ secondLevelView.setPadding(60, 30, 30, 30); secondLevelView.setTextColor(Color.GRAY); secondLevelView.setBackgroundColor(Color.parseColor("#f0f0f0")); }else{ secondLevelView.setPadding(120, 30, 30, 30); secondLevelView.setTextColor(Color.BLACK); secondLevelView.setBackgroundColor(Color.WHITE); } //2級容器將2級選單的item儲存起來 parentLayout.addView(secondLevelView); parentLayout.addView(getDivider()); //準備下一級子選單 LinearLayout thirdLayout=new LinearLayout(mContext); thirdLayout.setLayoutParams(itemLayoutParams); thirdLayout.setOrientation(LinearLayout.VERTICAL); thirdLayout.setVisibility(View.GONE); secondLevelView.setTag(thirdLayout); parentLayout.addView(thirdLayout); //完善子選單的點選事件 secondLevelView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //得到三級選單的佈局容器 LinearLayout parentLayout= (LinearLayout) v.getTag(); if (parentLayout.getChildCount()==0){ //表示沒有載入過 addMenuChild(parentLayout,v.getId()); }else if (parentLayout.isShown()){ parentLayout.setVisibility(GONE); }else { parentLayout.setVisibility(VISIBLE); } } }); } }else { Toast.makeText(mContext, "沒有資料,別點了!",Toast.LENGTH_SHORT).show(); } } /** * 根據pid得到物件的選單集合 * @param pid * @return */ private List<Node> getListByPid(int pid) { List<Node> resultNodes=new ArrayList<>(); for (int i=0;i<mDatas.size();i++){ Node node=mDatas.get(i); if (node.getpId()==pid){ resultNodes.add(node); } } return resultNodes; } /** * 新增橫線檢視 * @return */ public View getDivider() { View divider=new View(mContext); divider.setLayoutParams(dividerLayoutParams); divider.setBackgroundColor(Color.GRAY); return divider; } }
3.建立資料來源並且展示三級選單
package com.xiaoyao.android.mytree; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import java.util.ArrayList; import java.util.List; /** * 動態編碼實現我的三級選單 */ public class MainActivity extends AppCompatActivity { public List<Node>mDatas=new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initDatas(); //動態建立我們的三級選單,遞迴演算法 MyTree myTree=new MyTree(this); myTree.setData(mDatas); setContentView(myTree); } //準備好資料來源(開發獲取Json xml資料來源解析) private void initDatas() { // id , pid , label , 其他屬性 mDatas.add(new Node(1, 0, "遊戲")); mDatas.add(new Node(2, 0, "文件")); mDatas.add(new Node(3, 0, "程式")); mDatas.add(new Node(4, 0, "視訊")); mDatas.add(new Node(5, 0, "音樂")); mDatas.add(new Node(6, 0, "照片")); mDatas.add(new Node(7, 0, "學習")); mDatas.add(new Node(8, 0, "娛樂")); mDatas.add(new Node(9, 0, "美食")); mDatas.add(new Node(10, 0, "備忘錄")); mDatas.add(new Node(11, 1, "DOTA")); mDatas.add(new Node(12, 1, "LOL")); mDatas.add(new Node(13, 1, "war3")); mDatas.add(new Node(14, 11, "劍聖")); mDatas.add(new Node(15, 11, "敵法")); mDatas.add(new Node(16, 11, "影魔")); mDatas.add(new Node(17, 12, "德瑪西亞")); mDatas.add(new Node(18, 12, "潘森")); mDatas.add(new Node(19, 12, "蠻族之王")); mDatas.add(new Node(20, 13, "人族")); mDatas.add(new Node(21, 13, "獸族")); mDatas.add(new Node(22, 13, "不死族")); mDatas.add(new Node(23, 2, "需求文件")); mDatas.add(new Node(24, 2, "原型設計")); mDatas.add(new Node(25, 2, "詳細設計文件")); mDatas.add(new Node(26, 23, "需求調研")); mDatas.add(new Node(27, 23, "需求規格說明書")); mDatas.add(new Node(28, 23, "需求報告")); mDatas.add(new Node(29, 24, "QQ原型")); mDatas.add(new Node(30, 24, "微信原型")); mDatas.add(new Node(31, 25, "刀塔傳奇詳細設計")); mDatas.add(new Node(32, 25, "羽禾直播設計")); mDatas.add(new Node(33, 25, "YNedut設計")); mDatas.add(new Node(34, 25, "微信詳細設計")); mDatas.add(new Node(35, 3, "面向物件")); mDatas.add(new Node(36, 3, "非面向物件")); mDatas.add(new Node(37, 35, "C++")); mDatas.add(new Node(38, 35, "JAVA")); mDatas.add(new Node(39, 36, "Javascript")); mDatas.add(new Node(40, 36, "C")); mDatas.add(new Node(41, 4, "電視劇")); mDatas.add(new Node(42, 4, "電影")); mDatas.add(new Node(43, 4, "綜藝")); mDatas.add(new Node(44, 4, "動畫")); mDatas.add(new Node(45, 41, "花千骨")); mDatas.add(new Node(46, 41, "三國演義")); mDatas.add(new Node(47, 41, "匆匆那年")); mDatas.add(new Node(48, 41, "亮劍")); mDatas.add(new Node(49, 42, "金剛狼")); mDatas.add(new Node(50, 42, "復仇者聯盟")); mDatas.add(new Node(51, 42, "碟中諜")); mDatas.add(new Node(52, 42, "諜影重重")); mDatas.add(new Node(53, 43, "極限挑戰")); mDatas.add(new Node(54, 43, "奔跑吧兄弟")); mDatas.add(new Node(55, 43, "我去上學啦")); mDatas.add(new Node(56, 43, "中國好聲音")); mDatas.add(new Node(57, 44, "火影忍者")); mDatas.add(new Node(58, 44, "海賊王")); mDatas.add(new Node(59, 44, "哆啦A夢")); mDatas.add(new Node(60, 44, "蠟筆小新")); } }
以上案例只是做了一個動態佈局的一個簡單的入門,上面案例的三級選單有許多可擴充套件之處,比如能否像expandListView設定開關控制是否只展開一個子選單之處能,其實是可以,我在javabean已經為剛才的問題做了伏筆。