1. 程式人生 > >Android UI設計和形成原理(實現三級選單)

Android UI設計和形成原理(實現三級選單)

本次要實現的是動態編碼之三級選單的實現,在實際應用開發中經常會使用到三級選單,比如商城專案中的省市區,分類等等。

問題:介面載入是一次性new 大量的控制元件還是先載入一級選單,點選一級選單載入他對應的二級選單

答:這個很明顯是後者更符合開發思維,因為客戶不可能每個級別都一一點開,能點開所有級別的只有萬惡的測試人員。

介面載入方案:

1.資料進行一次性載入(一次把服務端傳遞過來的資料解析封裝到資料模型

2.先載入一級選單,點選一級選單載入他對應的二級選單

技術要點:動態編寫介面,遞迴演算法,javaBean;

實現過程

1.建立javabean封裝資料

package com.xiaoyao.android.mytree;
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;//父親節點 private
List<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; } }
以上要特別說明的是getLevel()方法在該方法中返回值是當前的級別,在此通過了遞迴的演算法得到當前級別的父級別然後+1從而得到當前的級別

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已經為剛才的問題做了伏筆。