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);
}
}