Tag: tree structure

Implementing a hierarchy (tree like) structure with Java / Android and navigating them with Buttons

For my Canadian Classifieds Alerter app, I needed to implement a hierarchy like structure to represent locations and categories. This is what I used in my solution.

For example,

Buy N’ Sell -> Computers -> Laptops

Canada -> New Brunswick -> Moncton

Laptops belongs to Computers, which a sub category of Buy N’ Sell’

Moncton belongs to New Brunswick, which belongs to Canada

Solution

Here it is in it’s simplest form. We’ll actually make it do something further down.

public enum KijijiCategory {
    //root category
    CATEGORY(null),
    //buy n sell, pass in the root category as the parent
    BUY_N_SELL(CATEGORY),
    //Pass in Buy N Sell as the parent category
    COMPUTERS(BUY_N_SELL),
    //Pass in Computers as the parent category
    LAPTOPS(COMPUTERS),
   

    private KijijiCategory parent = null;
   

	//Constructor, you pass in the parent
    KijijiCategory(KijijiCategory parent) {
        this.parent = parent;
    }

}

General explanation of what’s going on:

For CATEGORY, there is no parent, so I pass in null.

For BUY_N_SELL, the parent is CATEGORY, so I pass it in

For COMPUTERS, the parent is BUY_N_SELL, so I pass it in.

For LAPTOPS, the parent is COMPUTERS, so I pass it in.

Are you starting to see where this is going?

What can we use it for?

Check out this video.

Notice how easy it is to traverse through menu options? To do this we’re going to have to add more to the code I posted above

public enum KijijiCategory {
    //root category
    CATEGORY(null),
    //buy n sell, pass in the root category as the parent
    BUY_N_SELL(CATEGORY, 10, "Buy n sell"),
    //Pass in Buy N Sell as the parent category
    COMPUTERS(BUY_N_SELL, 16, "Computers"),
    //Pass in Computers as the parent category
    LAPTOPS(COMPUTERS,773, "Laptops"),
   
	
    private KijijiCategory parent = null;
    //unique identifier used internally by my app, you might not
	//need this or you may need to add your own
	private int id;
	//title will give us a nicer looking 
    private String title = "";


	//In this example
    KijijiCategory(KijijiCategory parent) {
        this.parent = parent;
    }

	//This is the constructor used by almost all of the categories above
    KijijiCategory(KijijiCategory parent, int id, String title) {
        this.parent = parent;
        this.id = id;
        this.title = title;
    }

    /*
     * Getters
      * */
    public String getTitle() {
        return this.title;

    }

    public String getParentName() {

        if (parent == null)
        {
            return "none";
        } else {
            return parent.getTitle();
        }
    }

    public KijijiCategory getParentCategory() {
        return parent;
    }

    public int getId() {
        return this.id;
    }

    @Override
    public String toString() {
        return "Kijiji Category name of: " + this.getTitle() + " and id of " + this.getId() + " and a parent of " + getParentName();
    }
}

You’ll notice I added a new constructor. I’m passing in an internally used identifier, and a nicer to look at string called title we’ll use to set the text for the boxes. I also added a few getters and overrode the toString() method

This is how I generated the LinearLayout

First step is to call the new method in onCreate (I prefer to keep this stuff out of onCreate as it makes future modifications easier)

generateKijijiCategories(KijijiCategory.CATEGORY, categoryLayout);

Here is where all the buttons are generated.

private LinearLayout generateKijijiCategories(final KijijiCategory categoryParent, final LinearLayout categoryLayout) {

        //Shows all of the buttons if we're on the main category 

        if (categoryParent == KijijiCategory.CATEGORY) {
            final Button button = new Button(this);
            button.setText("All Categories");
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    button.setTextColor(Color.GREEN);
                    categoryToQuery = KijijiCategory.CATEGORY;
                }
            });
            categoryLayout.addView(button);

        }
        //iterate through all potential values
        for (final KijijiCategory kijijiCategory : KijijiCategory.values()) {
            //abandon the current iteration if it's not a child of the parent we passed in
	    if (kijijiCategory.getParentCategory() != categoryParent) {
                continue;
            }
	    //Instantiate a button
            final Button button = new Button(this);
	//Give it a nice looking text
            button.setText(kijijiCategory.getTitle());
			//Used internally in my app
            button.setId(kijijiCategory.getId());
			button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
		    //turns focus to the top
                    txtCategories.requestFocus();
		    //empty out all buttons
                    categoryLayout.removeAllViews();
                    button.setTextColor(Color.GREEN);
                    categoryLayout.addView(button);
                    categoryToQuery = kijijiCategory;
		    //This isn't true recursion, it's debateable though
                    generateKijijiCategories(kijijiCategory, categoryLayout);

                }
            });
			//add the button to the layout
            categoryLayout.addView(button);
        }
        //add back button!
        if (categoryParent != KijijiCategory.CATEGORY)
        {

            Button backButton = new Button(this);
            final Button parentButton = new Button(this);
            parentButton.setText(categoryParent.getParentCategory().getTitle());
            parentButton.setTextColor(Color.GREEN);
            backButton.setText("Back");
            backButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    txtCategories.requestFocus();
                    categoryLayout.removeAllViews();

                    if(categoryParent.getParentCategory() != KijijiCategory.CATEGORY) {
                        categoryLayout.addView(parentButton);
                    }
                    categoryToQuery = categoryParent.getParentCategory();
                    generateKijijiCategories(categoryToQuery, categoryLayout);
                }
            });
            categoryLayout.addView(backButton);
        }

        return categoryLayout;
    }

See not so bad!