Source code
package android.support.v7.internal.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.graphics.drawable.DrawableWrapper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsListView;
import android.widget.ListAdapter;
import android.widget.ListView;
import java.lang.reflect.Field;
public class ListViewCompat extends ListView {
public static final int INVALID_POSITION = -1;
public static final int NO_POSITION = -1;
private static final int[] STATE_SET_NOTHING = new int[]{0};
private Field mIsChildViewEnabled;
protected int mMotionPosition;
int mSelectionBottomPadding;
int mSelectionLeftPadding;
int mSelectionRightPadding;
int mSelectionTopPadding;
private GateKeeperDrawable mSelector;
final Rect mSelectorRect;
private static class GateKeeperDrawable extends DrawableWrapper {
private boolean mEnabled = true;
public GateKeeperDrawable(Drawable drawable) {
super(drawable);
}
void setEnabled(boolean enabled) {
this.mEnabled = enabled;
}
public boolean setState(int[] stateSet) {
if (this.mEnabled) {
return super.setState(stateSet);
}
return false;
}
public void draw(Canvas canvas) {
if (this.mEnabled) {
super.draw(canvas);
}
}
public void setHotspot(float x, float y) {
if (this.mEnabled) {
super.setHotspot(x, y);
}
}
public void setHotspotBounds(int left, int top, int right, int bottom) {
if (this.mEnabled) {
super.setHotspotBounds(left, top, right, bottom);
}
}
public boolean setVisible(boolean visible, boolean restart) {
if (this.mEnabled) {
return super.setVisible(visible, restart);
}
return false;
}
}
public ListViewCompat(Context context) {
this(context, null);
}
public ListViewCompat(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ListViewCompat(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mSelectorRect = new Rect();
this.mSelectionLeftPadding = 0;
this.mSelectionTopPadding = 0;
this.mSelectionRightPadding = 0;
this.mSelectionBottomPadding = 0;
try {
this.mIsChildViewEnabled = AbsListView.class.getDeclaredField("mIsChildViewEnabled");
this.mIsChildViewEnabled.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
public void setSelector(Drawable sel) {
this.mSelector = sel != null ? new GateKeeperDrawable(sel) : null;
super.setSelector(this.mSelector);
Rect padding = new Rect();
if (sel != null) {
sel.getPadding(padding);
}
this.mSelectionLeftPadding = padding.left;
this.mSelectionTopPadding = padding.top;
this.mSelectionRightPadding = padding.right;
this.mSelectionBottomPadding = padding.bottom;
}
protected void drawableStateChanged() {
super.drawableStateChanged();
setSelectorEnabled(true);
updateSelectorStateCompat();
}
protected void dispatchDraw(Canvas canvas) {
drawSelectorCompat(canvas);
super.dispatchDraw(canvas);
}
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case 0:
this.mMotionPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
break;
}
return super.onTouchEvent(ev);
}
protected void updateSelectorStateCompat() {
Drawable selector = getSelector();
if (selector != null && shouldShowSelectorCompat()) {
selector.setState(getDrawableState());
}
}
protected boolean shouldShowSelectorCompat() {
return touchModeDrawsInPressedStateCompat() && isPressed();
}
protected boolean touchModeDrawsInPressedStateCompat() {
return false;
}
protected void drawSelectorCompat(Canvas canvas) {
if (!this.mSelectorRect.isEmpty()) {
Drawable selector = getSelector();
if (selector != null) {
selector.setBounds(this.mSelectorRect);
selector.draw(canvas);
}
}
}
public int lookForSelectablePosition(int position, boolean lookDown) {
ListAdapter adapter = getAdapter();
if (adapter == null || isInTouchMode()) {
return -1;
}
int count = adapter.getCount();
if (!getAdapter().areAllItemsEnabled()) {
if (lookDown) {
position = Math.max(0, position);
while (position < count && !adapter.isEnabled(position)) {
position++;
}
} else {
position = Math.min(position, count - 1);
while (position >= 0 && !adapter.isEnabled(position)) {
position--;
}
}
if (position < 0 || position >= count) {
return -1;
}
return position;
} else if (position < 0 || position >= count) {
return -1;
} else {
return position;
}
}
protected void positionSelectorLikeTouchCompat(int position, View sel, float x, float y) {
positionSelectorLikeFocusCompat(position, sel);
Drawable selector = getSelector();
if (selector != null && position != -1) {
DrawableCompat.setHotspot(selector, x, y);
}
}
protected void positionSelectorLikeFocusCompat(int position, View sel) {
boolean manageState;
boolean z = true;
Drawable selector = getSelector();
if (selector == null || position == -1) {
manageState = false;
} else {
manageState = true;
}
if (manageState) {
selector.setVisible(false, false);
}
positionSelectorCompat(position, sel);
if (manageState) {
Rect bounds = this.mSelectorRect;
float x = bounds.exactCenterX();
float y = bounds.exactCenterY();
if (getVisibility() != 0) {
z = false;
}
selector.setVisible(z, false);
DrawableCompat.setHotspot(selector, x, y);
}
}
protected void positionSelectorCompat(int position, View sel) {
Rect selectorRect = this.mSelectorRect;
selectorRect.set(sel.getLeft(), sel.getTop(), sel.getRight(), sel.getBottom());
selectorRect.left -= this.mSelectionLeftPadding;
selectorRect.top -= this.mSelectionTopPadding;
selectorRect.right += this.mSelectionRightPadding;
selectorRect.bottom += this.mSelectionBottomPadding;
try {
boolean isChildViewEnabled = this.mIsChildViewEnabled.getBoolean(this);
if (sel.isEnabled() != isChildViewEnabled) {
this.mIsChildViewEnabled.set(this, Boolean.valueOf(!isChildViewEnabled));
if (position != -1) {
refreshDrawableState();
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public int measureHeightOfChildrenCompat(int widthMeasureSpec, int startPosition, int endPosition, int maxHeight, int disallowPartialChildPosition) {
int paddingTop = getListPaddingTop();
int paddingBottom = getListPaddingBottom();
int paddingLeft = getListPaddingLeft();
int paddingRight = getListPaddingRight();
int reportedDividerHeight = getDividerHeight();
Drawable divider = getDivider();
ListAdapter adapter = getAdapter();
if (adapter == null) {
return paddingTop + paddingBottom;
}
int returnedHeight = paddingTop + paddingBottom;
int dividerHeight = (reportedDividerHeight <= 0 || divider == null) ? 0 : reportedDividerHeight;
int prevHeightWithoutPartialChild = 0;
View child = null;
int viewType = 0;
int count = adapter.getCount();
int i = 0;
while (i < count) {
int heightMeasureSpec;
int newType = adapter.getItemViewType(i);
if (newType != viewType) {
child = null;
viewType = newType;
}
child = adapter.getView(i, child, this);
LayoutParams childLp = child.getLayoutParams();
if (childLp == null) {
childLp = generateDefaultLayoutParams();
child.setLayoutParams(childLp);
}
if (childLp.height > 0) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(childLp.height, 1073741824);
} else {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, 0);
}
child.measure(widthMeasureSpec, heightMeasureSpec);
child.forceLayout();
if (i > 0) {
returnedHeight += dividerHeight;
}
returnedHeight += child.getMeasuredHeight();
if (returnedHeight < maxHeight) {
if (disallowPartialChildPosition >= 0 && i >= disallowPartialChildPosition) {
prevHeightWithoutPartialChild = returnedHeight;
}
i++;
} else if (disallowPartialChildPosition < 0 || i <= disallowPartialChildPosition || prevHeightWithoutPartialChild <= 0 || returnedHeight == maxHeight) {
return maxHeight;
} else {
return prevHeightWithoutPartialChild;
}
}
return returnedHeight;
}
protected void setSelectorEnabled(boolean enabled) {
if (this.mSelector != null) {
this.mSelector.setEnabled(enabled);
}
}
}