轉自:http://www.tuicool.com/articles/FJZfma
Android中提供了可展開的列表控件,很不幸,和很多其他原生控件一樣,這個控件有些地方設計的 ridiculous !其中的一個很重要的地方就是本文中要說的這個 groupIndicator 了。話說這玩意是幹嘛用的?就是用來展示一個group的展開狀態用的↓
好吧,這東西蛋疼的地方有如下幾點:
- 位置只能放在固定的位置上(神馬?你說可以通過android:indicatorLeft來控制位置?come on 那上下的位置呢?)
- 這個Indicator和你的itemView是完全沒關係的2個東西,也就是說這東西可能會覆蓋在你原本的view上面哦
- 其實上下的位置也能解決,通過設置自定義Indicator的draw9patch的拉伸區域可以 大概 的控制 = =#
OK,吐槽先到這,看看大家是怎麼替換的吧,一般來說是這樣:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/list_item_expand" android:state_expanded="true"/>
<item android:drawable="@drawable/list_item_collapse"></item>
</selector>
===================================================
<ExpandableListView
…
android:groupIndicator="@drawable/list_expand"
…
/>
不過這樣做是會遇到上面幾個問題的,爲了解決這個事情我們來翻看一下源碼,看看系統是怎麼去繪製這個Indicator的
先看怎麼獲得繪製狀態
private static final int[] EMPTY_STATE_SET = {};
/** State indicating the group is expanded. */
private static final int[] GROUP_EXPANDED_STATE_SET = { android.R.attr.state_expanded };
/** State indicating the group is empty (has no children). */
private static final int[] GROUP_EMPTY_STATE_SET = { android.R.attr.state_empty };
/** State indicating the group is expanded and empty (has no children). */
private static final int[] GROUP_EXPANDED_EMPTY_STATE_SET = { android.R.attr.state_expanded,
android.R.attr.state_empty };
/** States for the group where the 0th bit is expanded and 1st bit is empty. */
private static final int[][] GROUP_STATE_SETS = { EMPTY_STATE_SET, // 00
GROUP_EXPANDED_STATE_SET, // 01
GROUP_EMPTY_STATE_SET, // 10
GROUP_EXPANDED_EMPTY_STATE_SET // 11
};
======================================================================
if (pos.position.type == ExpandableListPosition.GROUP) {
indicator = mGroupIndicator;
if (indicator != null && indicator.isStateful()) {
// Empty check based on availability of data. If the groupMetadata isn't null,
// we do a check on it. Otherwise, the group is collapsed so we consider it
// empty for performance reasons.
boolean isEmpty = (pos.groupMetadata == null) ||
(pos.groupMetadata.lastChildFlPos == pos.groupMetadata.flPos);
final int stateSetIndex =
(pos.isExpanded() ? 1 : 0) | // Expanded?
(isEmpty ? 2 : 0); // Empty?
indicator.setState(GROUP_STATE_SETS[stateSetIndex]);
}
}
然後看看控制位置和區域
public void setGroupIndicator(Drawable groupIndicator) {
mGroupIndicator = groupIndicator;
if (mIndicatorRight == 0 && mGroupIndicator != null) {
mIndicatorRight = mIndicatorLeft + mGroupIndicator.getIntrinsicWidth();
}
========================================================
if (indicatorRect.left != indicatorRect.right) {
// Use item's full height + the divider height
↑看這句註釋就知道開發這個控件的攻城師沒帶投石車 =。=
if (mStackFromBottom) {
// See ListView#dispatchDraw
indicatorRect.top = t;// - mDividerHeight;
indicatorRect.bottom = b;
} else {
indicatorRect.top = t;
indicatorRect.bottom = b;// + mDividerHeight;
}
// Get the indicator (with its state set to the item's state)
indicator = getIndicator(pos);
if (indicator != null) {
// Draw the indicator
indicator.setBounds(indicatorRect);
indicator.draw(canvas);
}
}
原理大概明白了就可以自己搞搞了~爲了便於開發中使用,我們還是應該把這個Indicator放到item的layout裏面去。隨便大概寫一個demo的樣子如下:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="8dp"
android:paddingLeft="10dp"
android:paddingRight="8dp"
android:paddingTop="8dp" >
<ImageView
android:id="@+id/indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@drawable/list_expand" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:drawablePadding="3dp"
android:ellipsize="end"
android:paddingLeft="3dp"
android:singleLine="true"
android:textStyle="bold" />
</LinearLayout>
然後在adapter裏面把剛剛的那些控制的代碼弄進來~
public abstract class ExpandableAdapter<Group, Child> extends BaseExpandableListAdapter {
private static final int[] EMPTY_STATE_SET = {};
/** State indicating the group is expanded. */
private static final int[] GROUP_EXPANDED_STATE_SET = { android.R.attr.state_expanded };
/** State indicating the group is empty (has no children). */
private static final int[] GROUP_EMPTY_STATE_SET = { android.R.attr.state_empty };
/** State indicating the group is expanded and empty (has no children). */
private static final int[] GROUP_EXPANDED_EMPTY_STATE_SET = { android.R.attr.state_expanded,
android.R.attr.state_empty };
/** States for the group where the 0th bit is expanded and 1st bit is empty. */
private static final int[][] GROUP_STATE_SETS = { EMPTY_STATE_SET, // 00
GROUP_EXPANDED_STATE_SET, // 01
GROUP_EMPTY_STATE_SET, // 10
GROUP_EXPANDED_EMPTY_STATE_SET // 11
};
在group的展開和收起的回調中控制一下刷新,這裏爲了防止子類覆蓋父類的回調,這裏把這2個方法都final標註一下,然後提供一個擴展的回調。
@Override
public final void onGroupCollapsed(int groupPosition) {
onGroupCollapsedEx(groupPosition);
notifyDataSetChanged();
}
@Override
public final void onGroupExpanded(int groupPosition) {
onGroupExpandedEx(groupPosition);
notifyDataSetChanged();
}
protected void onGroupCollapsedEx(int groupPosition) {
}
protected void onGroupExpandedEx(int groupPosition) {
}
然後提供一個設置Indicator狀態的方法
protected void setIndicatorState(Drawable indicator, int groupPosition, boolean isExpanded) {
final int stateSetIndex = (isExpanded ? 1 : 0) | // Expanded?
(getChildrenCount(groupPosition) == 0 ? 2 : 0); // Empty?
indicator.setState(GROUP_STATE_SETS[stateSetIndex]);
}
在子類的getView方法我們可以這樣寫
ImageView indicatorView = (ImageView)view.findViewById(R.id.indicator);
setIndicatorState(indicatorView.getDrawable(),groupPosition,isExpanded);
很簡單,就把這個事情解決了,可見 開發這個控件的攻城師那天真的沒帶投石車 =。= 其實這個adapter還可以進一步的定製,從而用起來更簡潔方便,不容易出錯,這部分內容會稍後帶來。