package pl.polidea.treelistview; import android.app.Activity; import android.content.Context; import android.database.DataSetObserver; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ImageView.ScaleType; import android.widget.LinearLayout; /** * Adapter used to feed the table view. * * @param <T> * class for ID of the tree */ public abstract class AbstractTreeListAdapter<T> extends BaseAdapter { private static final String TAG = AbstractTreeListAdapter.class.getSimpleName(); private static final boolean DEBUG = false; private final TreeStateManager<T> mTreeStateManager; private final int mNumberOfLevels; private final LayoutInflater mLayoutInflater; private int mIndentWidth = 0; private int mIndicatorGravity = 0; private Drawable mCollapsedDrawable; private Drawable mExpandedDrawable; private Drawable mIndicatorBackgroundDrawable; private Drawable mRowBackgroundDrawable; private final OnClickListener mIndicatorClickListener = new OnClickListener() { @Override public void onClick(final View v) { @SuppressWarnings("unchecked") final T id = (T) v.getTag(); expandCollapse(id); } }; private boolean mCollapsible; private final Activity mActivity; public Activity getActivity() { return mActivity; } protected TreeStateManager<T> getManager() { return mTreeStateManager; } protected void expandCollapse(final T id) { final TreeNodeInfo<T> info = mTreeStateManager.getNodeInfo(id); if (!info.isWithChildren()) { // ignore - no default action return; } if (info.isExpanded()) { mTreeStateManager.collapseChildren(id, false); } else { mTreeStateManager.expandChildren(id, false); } } private void calculateIndentWidth() { if (mExpandedDrawable != null) { mIndentWidth = Math.max(getIndentWidth(), mExpandedDrawable.getIntrinsicWidth()); } if (mCollapsedDrawable != null) { mIndentWidth = Math.max(getIndentWidth(), mCollapsedDrawable.getIntrinsicWidth()); } } public AbstractTreeListAdapter(final Activity activity, final TreeStateManager<T> treeStateManager, final int numberOfLevels) { mActivity = activity; mTreeStateManager = treeStateManager; mLayoutInflater = (LayoutInflater) activity .getSystemService(Context.LAYOUT_INFLATER_SERVICE); mNumberOfLevels = numberOfLevels; mCollapsedDrawable = null; mExpandedDrawable = null; mRowBackgroundDrawable = null; mIndicatorBackgroundDrawable = null; } @Override public void registerDataSetObserver(final DataSetObserver observer) { mTreeStateManager.registerDataSetObserver(observer); } @Override public void unregisterDataSetObserver(final DataSetObserver observer) { mTreeStateManager.unregisterDataSetObserver(observer); } @Override public int getCount() { return mTreeStateManager.getVisibleCount(); } @Override public Object getItem(final int position) { return getItemId(position); } public T getTreeId(final int position) { return mTreeStateManager.getVisibleList().get(position); } public TreeNodeInfo<T> getTreeNodeInfo(final int position) { return mTreeStateManager.getNodeInfo(getTreeId(position)); } @Override public boolean hasStableIds() { // NOPMD return true; } @Override public int getItemViewType(final int position) { return getTreeNodeInfo(position).getLevel(); } @Override public int getViewTypeCount() { return mNumberOfLevels; } @Override public boolean isEmpty() { return getCount() == 0; } @Override public boolean areAllItemsEnabled() { // NOPMD return true; } @Override public boolean isEnabled(final int position) { // NOPMD return true; } protected int getTreeListItemWrapperId() { return R.layout.tree_list_item_wrapper; } @Override public final View getView(final int position, final View convertView, final ViewGroup parent) { if (DEBUG) Log.d(TAG, "Creating a view based on " + convertView + " with position " + position); final TreeNodeInfo<T> nodeInfo = getTreeNodeInfo(position); if (convertView == null) { if (DEBUG) Log.d(TAG, "Creating the view a new"); final LinearLayout layout = (LinearLayout) mLayoutInflater.inflate( getTreeListItemWrapperId(), null); return populateTreeItem(layout, getNewChildView(nodeInfo), nodeInfo, true); } else { if (DEBUG) Log.d(TAG, "Reusing the view"); final LinearLayout linear = (LinearLayout) convertView; final FrameLayout frameLayout = (FrameLayout) linear .findViewById(R.id.tree_list_view_item_frame); final View childView = frameLayout.getChildAt(0); updateView(childView, nodeInfo); return populateTreeItem(linear, childView, nodeInfo, false); } } /** * Called when new view is to be created. * * @param treeNodeInfo * node info * @return view that should be displayed as tree content */ public abstract View getNewChildView(TreeNodeInfo<T> treeNodeInfo); /** * Called when new view is going to be reused. You should update the view * and fill it in with the data required to display the new information. You * can also create a new view, which will mean that the old view will not be * reused. * * @param view * view that should be updated with the new values * @param treeNodeInfo * node info used to populate the view * @return view to used as row indented content */ public abstract View updateView(View view, TreeNodeInfo<T> treeNodeInfo); /** * Retrieves background drawable for the node. * * @param treeNodeInfo * node info * @return drawable returned as background for the whole row. Might be null, * then default background is used */ public Drawable getBackgroundDrawable(final TreeNodeInfo<T> treeNodeInfo) { // NOPMD return null; } private Drawable getDrawableOrDefaultBackground(final Drawable r) { if (r == null) { return mActivity.getResources() .getDrawable(R.drawable.list_selector_background).mutate(); } else { return r; } } public final LinearLayout populateTreeItem(final LinearLayout layout, final View childView, final TreeNodeInfo<T> nodeInfo, final boolean newChildView) { final Drawable individualRowDrawable = getBackgroundDrawable(nodeInfo); layout.setBackground(individualRowDrawable == null ? getDrawableOrDefaultBackground(mRowBackgroundDrawable) : individualRowDrawable); final LinearLayout.LayoutParams indicatorLayoutParams = new LinearLayout.LayoutParams( calculateIndentation(nodeInfo), ViewGroup.LayoutParams.MATCH_PARENT); final LinearLayout indicatorLayout = (LinearLayout) layout .findViewById(R.id.tree_list_view_image_layout); indicatorLayout.setGravity(mIndicatorGravity); indicatorLayout.setLayoutParams(indicatorLayoutParams); final ImageView image = (ImageView) layout .findViewById(R.id.tree_list_view_item_image); image.setImageDrawable(getDrawable(nodeInfo)); image.setBackground(getDrawableOrDefaultBackground(mIndicatorBackgroundDrawable)); image.setScaleType(ScaleType.CENTER); image.setTag(nodeInfo.getId()); if (nodeInfo.isWithChildren() && mCollapsible) { image.setOnClickListener(mIndicatorClickListener); } else { image.setOnClickListener(null); } layout.setTag(nodeInfo.getId()); final FrameLayout frameLayout = (FrameLayout) layout .findViewById(R.id.tree_list_view_item_frame); final FrameLayout.LayoutParams childParams = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); if (newChildView) { frameLayout.addView(childView, childParams); } frameLayout.setTag(nodeInfo.getId()); return layout; } protected int calculateIndentation(final TreeNodeInfo<T> nodeInfo) { return getIndentWidth() * (nodeInfo.getLevel() + (mCollapsible ? 1 : 0)); } protected Drawable getDrawable(final TreeNodeInfo<T> nodeInfo) { if (!nodeInfo.isWithChildren() || !mCollapsible) { return getDrawableOrDefaultBackground(mIndicatorBackgroundDrawable); } if (nodeInfo.isExpanded()) { return mExpandedDrawable; } else { return mCollapsedDrawable; } } public void setIndicatorGravity(final int indicatorGravity) { mIndicatorGravity = indicatorGravity; } public void setCollapsedDrawable(final Drawable collapsedDrawable) { mCollapsedDrawable = collapsedDrawable; calculateIndentWidth(); } public void setExpandedDrawable(final Drawable expandedDrawable) { mExpandedDrawable = expandedDrawable; calculateIndentWidth(); } public void setIndentWidth(final int indentWidth) { mIndentWidth = indentWidth; calculateIndentWidth(); } public void setRowBackgroundDrawable(final Drawable rowBackgroundDrawable) { mRowBackgroundDrawable = rowBackgroundDrawable; } public void setIndicatorBackgroundDrawable( final Drawable indicatorBackgroundDrawable) { mIndicatorBackgroundDrawable = indicatorBackgroundDrawable; } public void setCollapsible(final boolean collapsible) { mCollapsible = collapsible; mTreeStateManager.setCollapsible(mCollapsible); } public void refresh() { mTreeStateManager.refresh(); } private int getIndentWidth() { return mIndentWidth; } @SuppressWarnings("unchecked") public void handleItemClick(final View view, final Object id) { expandCollapse((T) id); } }