alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Android example source code file (ExpandableListScenario.java)

This example Android source code file (ExpandableListScenario.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Android by Example" TM.

Java - Android tags/keywords

android, arraylist, expandablelistadapter, expandablelistview, expandableparams, illegalstateexception, myadapter, mychild, mygroup, object, override, string, textview, ui, util, view, viewgroup, widget

The ExpandableListScenario.java Android example source code

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.util;

import java.util.ArrayList;
import java.util.List;

import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ListView;
import android.widget.TextView;

/**
 * Utility base class for creating various Expandable List scenarios.
 * <p>
 * WARNING: A lot of the features are mixed between ListView's expected position
 * (flat list position) and an ExpandableListView's expected position.  You must add/change
 * features as you need them.
 * 
 * @see ListScenario
 */
public abstract class ExpandableListScenario extends ListScenario {
    protected ExpandableListAdapter mAdapter; 
    protected List<MyGroup> mGroups;
    
    @Override
    protected ListView createListView() {
        return new ExpandableListView(this);
    }

    @Override
    protected Params createParams() {
        return new ExpandableParams();
    }

    @Override
    protected void setAdapter(ListView listView) {
        ((ExpandableListView) listView).setAdapter(mAdapter = createAdapter());
    }
    
    protected ExpandableListAdapter createAdapter() {
        return new MyAdapter();
    }
    
    @Override
    protected void readAndValidateParams(Params params) {
        ExpandableParams expandableParams = (ExpandableParams) params;
        
        int[] numChildren = expandableParams.mNumChildren;
        
        mGroups = new ArrayList<MyGroup>(numChildren.length);
        for (int i = 0; i < numChildren.length; i++) {
            mGroups.add(new MyGroup(numChildren[i]));
        }
        
        expandableParams.superSetNumItems();
        
        super.readAndValidateParams(params);
    }

    /**
     * Get the ExpandableListView widget.
     * @return The main widget.
     */
    public ExpandableListView getExpandableListView() {
        return (ExpandableListView) super.getListView();
    }

    public static class ExpandableParams extends Params {
        private int[] mNumChildren;
        
        /**
         * Sets the number of children per group.
         *  
         * @param numChildrenPerGroup The number of children per group.
         */
        public ExpandableParams setNumChildren(int[] numChildren) {
            mNumChildren = numChildren;
            return this;
        }

        /**
         * Sets the number of items on the superclass based on the number of
         * groups and children per group.
         */
        private ExpandableParams superSetNumItems() {
            int numItems = 0;
            
            if (mNumChildren != null) {
                for (int i = mNumChildren.length - 1; i >= 0; i--) {
                    numItems += mNumChildren[i];
                }
            }
            
            super.setNumItems(numItems);
            
            return this;
        }
        
        @Override
        public Params setNumItems(int numItems) {
            throw new IllegalStateException("Use setNumGroups and setNumChildren instead.");
        }

        @Override
        public ExpandableParams setFadingEdgeScreenSizeFactor(double fadingEdgeScreenSizeFactor) {
            return (ExpandableParams) super.setFadingEdgeScreenSizeFactor(fadingEdgeScreenSizeFactor);
        }

        @Override
        public ExpandableParams setItemScreenSizeFactor(double itemScreenSizeFactor) {
            return (ExpandableParams) super.setItemScreenSizeFactor(itemScreenSizeFactor);
        }

        @Override
        public ExpandableParams setItemsFocusable(boolean itemsFocusable) {
            return (ExpandableParams) super.setItemsFocusable(itemsFocusable);
        }

        @Override
        public ExpandableParams setMustFillScreen(boolean fillScreen) {
            return (ExpandableParams) super.setMustFillScreen(fillScreen);
        }

        @Override
        public ExpandableParams setPositionScreenSizeFactorOverride(int position, double itemScreenSizeFactor) {
            return (ExpandableParams) super.setPositionScreenSizeFactorOverride(position, itemScreenSizeFactor);
        }

        @Override
        public ExpandableParams setPositionUnselectable(int position) {
            return (ExpandableParams) super.setPositionUnselectable(position);
        }

        @Override
        public ExpandableParams setStackFromBottom(boolean stackFromBottom) {
            return (ExpandableParams) super.setStackFromBottom(stackFromBottom);
        }

        @Override
        public ExpandableParams setStartingSelectionPosition(int startingSelectionPosition) {
            return (ExpandableParams) super.setStartingSelectionPosition(startingSelectionPosition);
        }

        @Override
        public ExpandableParams setConnectAdapter(boolean connectAdapter) {
            return (ExpandableParams) super.setConnectAdapter(connectAdapter);
        }
    }

    /**
     * Gets a string for the value of some item.
     * @param packedPosition The position of the item.
     * @return The string.
     */
    public final String getValueAtPosition(long packedPosition) {
        final int type = ExpandableListView.getPackedPositionType(packedPosition);
        
        if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
            return mGroups.get(ExpandableListView.getPackedPositionGroup(packedPosition))
                    .children.get(ExpandableListView.getPackedPositionChild(packedPosition))
                    .name;
        } else if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
            return mGroups.get(ExpandableListView.getPackedPositionGroup(packedPosition))
                    .name;
        } else {
            throw new IllegalStateException("packedPosition is not a valid position.");
        }
    }

    /**
     * Whether a particular position is out of bounds.
     * 
     * @param packedPosition The packed position.
     * @return Whether it's out of bounds.
     */
    private boolean isOutOfBounds(long packedPosition) {
        final int type = ExpandableListView.getPackedPositionType(packedPosition);
        
        if (type == ExpandableListView.PACKED_POSITION_TYPE_NULL) {
            throw new IllegalStateException("packedPosition is not a valid position.");
        }

        final int group = ExpandableListView.getPackedPositionGroup(packedPosition); 
        if (group >= mGroups.size() || group < 0) {
            return true;
        }
        
        if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
            final int child = ExpandableListView.getPackedPositionChild(packedPosition); 
            if (child >= mGroups.get(group).children.size() || child < 0) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Gets a view for the packed position, possibly reusing the convertView.
     * 
     * @param packedPosition The position to get a view for.
     * @param convertView Optional view to convert.
     * @param parent The future parent.
     * @return A view.
     */
    private View getView(long packedPosition, View convertView, ViewGroup parent) {
        if (isOutOfBounds(packedPosition)) {
            throw new IllegalStateException("position out of range for adapter!");
        }
        
        final ExpandableListView elv = getExpandableListView();
        final int flPos = elv.getFlatListPosition(packedPosition); 
        
        if (convertView != null) {
            ((TextView) convertView).setText(getValueAtPosition(packedPosition));
            convertView.setId(flPos);
            return convertView;
        }

        int desiredHeight = getHeightForPosition(flPos);
        return createView(packedPosition, flPos, parent, desiredHeight);
    }
    
    /**
     * Create a view for a group or child position.
     * 
     * @param packedPosition The packed position (has type, group pos, and optionally child pos).
     * @param flPos The flat list position (the position that the ListView goes by).
     * @param parent The parent view.
     * @param desiredHeight The desired height.
     * @return A view.
     */
    protected View createView(long packedPosition, int flPos, ViewGroup parent, int desiredHeight) {
        TextView result = new TextView(parent.getContext());
        result.setHeight(desiredHeight);
        result.setText(getValueAtPosition(packedPosition));
        final ViewGroup.LayoutParams lp = new AbsListView.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        result.setLayoutParams(lp);
        result.setGravity(Gravity.CENTER_VERTICAL);
        result.setPadding(36, 0, 0, 0);
        result.setId(flPos);
        return result;
    }
    
    /**
     * Returns a group index containing either the number of children or at
     * least one child.
     * 
     * @param numChildren The group must have this amount, or -1 if using
     *            atLeastOneChild.
     * @param atLeastOneChild The group must have at least one child, or false
     *            if using numChildren.
     * @return A group index with the requirements.
     */
    public int findGroupWithNumChildren(int numChildren, boolean atLeastOneChild) {
        final ExpandableListAdapter adapter = mAdapter;
        
        for (int i = adapter.getGroupCount() - 1; i >= 0; i--) {
            final int curNumChildren = adapter.getChildrenCount(i);
            
            if (numChildren == curNumChildren || atLeastOneChild && curNumChildren > 0) {
                return i;
            }
        }
        
        return -1;
    }
    
    public List<MyGroup> getGroups() {
        return mGroups;
    }
    
    public ExpandableListAdapter getAdapter() {
        return mAdapter;
    }

    /**
     * Simple expandable list adapter.
     */
    protected class MyAdapter extends BaseExpandableListAdapter {
        public Object getChild(int groupPosition, int childPosition) {
            return getValueAtPosition(ExpandableListView.getPackedPositionForChild(groupPosition,
                    childPosition));
        }

        public long getChildId(int groupPosition, int childPosition) {
            return mGroups.get(groupPosition).children.get(childPosition).id;
        }

        public int getChildrenCount(int groupPosition) {
            return mGroups.get(groupPosition).children.size();
        }

        public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
                View convertView, ViewGroup parent) {
            return getView(ExpandableListView.getPackedPositionForChild(groupPosition,
                    childPosition), convertView, parent);
        }

        public Object getGroup(int groupPosition) {
            return getValueAtPosition(ExpandableListView.getPackedPositionForGroup(groupPosition));
        }

        public int getGroupCount() {
            return mGroups.size();
        }

        public long getGroupId(int groupPosition) {
            return mGroups.get(groupPosition).id;
        }

        public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
                ViewGroup parent) {
            return getView(ExpandableListView.getPackedPositionForGroup(groupPosition),
                    convertView, parent);
        }

        public boolean isChildSelectable(int groupPosition, int childPosition) {
            return true;
        }

        public boolean hasStableIds() {
            return true;
        }
        
    }

    public static class MyGroup {
        private static long mNextId = 1000;
        
        String name;
        long id = mNextId++;
        List<MyChild> children;
        
        public MyGroup(int numChildren) {
            name = "Group " + id;
            children = new ArrayList<MyChild>(numChildren);
            for (int i = 0; i < numChildren; i++) {
                children.add(new MyChild());
            }
        }
    }
    
    public static class MyChild {
        private static long mNextId = 2000;
        
        String name;
        long id = mNextId++;
        
        public MyChild() {
            name = "Child " + id;
        }
    }
    
    @Override
    protected final void init(Params params) {
        init((ExpandableParams) params);
    }

    /**
     * @see ListScenario#init
     */
    protected abstract void init(ExpandableParams params);
}

Other Android examples (source code examples)

Here is a short list of links related to this Android ExpandableListScenario.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.