Android Staggered Grid & Record View.
Create Android Staggered Lib Challenge.
—> AndroidManifest.xml
values folder
attrs.xml
ClassLoaderSavedState.java
package deal com.android.grid;
import android.os.Parcel;
import android.os.Parcelable;
public summary class ClassLoaderSavedState implements Parcelable {
public static closing ClassLoaderSavedState EMPTY_STATE = new ClassLoaderSavedState() {};
personal Parcelable mSuperState = EMPTY_STATE;
personal ClassLoader mClassLoader;
personal ClassLoaderSavedState() {
mSuperState = null;
mClassLoader = null;
}
protected ClassLoaderSavedState(Parcelable superState, ClassLoader classLoader) {
mClassLoader = classLoader;
if (superState == null) {
throw new IllegalArgumentException(“superState must not be null”);
}
else {
mSuperState = superState != EMPTY_STATE ? superState : null;
}
}
protected ClassLoaderSavedState(Parcel supply) {
// ETSY : we’re utilizing the handed tremendous class loader in contrast to AbsSavedState
Parcelable superState = supply.readParcelable(mClassLoader);
mSuperState = superState != null ? superState : EMPTY_STATE;
}
closing public Parcelable getSuperState() {
return mSuperState;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mSuperState, flags);
}
public static closing Parcelable.Creator
= new Parcelable.Creator
public ClassLoaderSavedState createFromParcel(Parcel in) {
Parcelable superState = in.readParcelable(null);
if (superState != null) {
throw new IllegalStateException(“superState must be null”);
}
return EMPTY_STATE;
}
public ClassLoaderSavedState[] newArray(int dimension) {
return new ClassLoaderSavedState[size];
}
};
}
ExtendableListView.java
package deal com.android.grid;
import android.annotation.SuppressLint;
import android.content material.Context;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.help.v4.util.SparseArrayCompat;
import android.help.v4.view.MotionEventCompat;
import android.help.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.*;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.Scroller;
import java.util.ArrayList;
public summary class ExtendableListView extends AbsListView {
personal static closing String TAG = “ExtendableListView”;
personal static closing boolean DBG = false;
personal static closing int TOUCH_MODE_IDLE = 0;
personal static closing int TOUCH_MODE_SCROLLING = 1;
personal static closing int TOUCH_MODE_FLINGING = 2;
personal static closing int TOUCH_MODE_DOWN = 3;
personal static closing int TOUCH_MODE_TAP = 4;
personal static closing int TOUCH_MODE_DONE_WAITING = 5;
personal static closing int INVALID_POINTER = -1;
// Structure utilizing our default current state
personal static closing int LAYOUT_NORMAL = 0;
// Structure from the primary merchandise down
personal static closing int LAYOUT_FORCE_TOP = 1;
// Structure from the saved occasion state knowledge
personal static closing int LAYOUT_SYNC = 2;
personal int mLayoutMode;
personal int mTouchMode;
personal int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
// Rectangle used for hit testing youngsters
// personal Rect mTouchFrame;
// TODO : ItemClick help from AdapterView
// For managing scrolling
personal VelocityTracker mVelocityTracker = null;
personal int mTouchSlop;
personal int mMaximumVelocity;
personal int mFlingVelocity;
personal boolean mInLayout;
ListAdapter mAdapter;
personal int mMotionY;
personal int mMotionX;
personal int mMotionCorrection;
personal int mMotionPosition;
personal int mLastY;
personal int mActivePointerId = INVALID_POINTER;
protected int mFirstPosition;
// are we connected to a window – we shouldn’t deal with any contact occasions if we’re not!
personal boolean mIsAttached;
personal boolean mBlockLayoutRequests = false;
// has our knowledge modified – and will we react to it
personal boolean mDataChanged;
personal int mItemCount;
personal int mOldItemCount;
closing boolean[] mIsScrap = new boolean[1];
personal RecycleBin mRecycleBin;
personal AdapterDataSetObserver mObserver;
personal int mWidthMeasureSpec;
personal FlingRunnable mFlingRunnable;
protected boolean mClipToPadding;
personal PerformClick mPerformClick;
personal Runnable mPendingCheckForTap;
personal CheckForLongPress mPendingCheckForLongPress;
personal class CheckForLongPress extends WindowRunnnable implements Runnable {
public void run() {
closing int motionPosition = mMotionPosition;
closing View little one = getChildAt(motionPosition);
if (little one != null) {
closing int longPressPosition = mMotionPosition;
closing lengthy longPressId = mAdapter.getItemId(mMotionPosition + mFirstPosition);
boolean dealt with = false;
if (sameWindow() && !mDataChanged) {
dealt with = performLongPress(little one, longPressPosition + mFirstPosition, longPressId);
}
if (dealt with) {
mTouchMode = TOUCH_MODE_IDLE;
setPressed(false);
little one.setPressed(false);
} else {
mTouchMode = TOUCH_MODE_DONE_WAITING;
}
}
}
}
public class FixedViewInfo {
public View view;
public Object knowledge;
public boolean isSelectable;
}
personal ArrayList
personal ArrayList
public ExtendableListView(closing Context context, closing AttributeSet attrs, closing int defStyle) {
tremendous(context, attrs, defStyle);
// setting as much as be a scrollable view group
setWillNotDraw(false);
setClipToPadding(false);
setFocusableInTouchMode(false);
closing ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
mTouchSlop = viewConfiguration.getScaledTouchSlop();
mMaximumVelocity = viewConfiguration.getScaledMaximumFlingVelocity();
mFlingVelocity = viewConfiguration.getScaledMinimumFlingVelocity();
mRecycleBin = new RecycleBin();
mObserver = new AdapterDataSetObserver();
mHeaderViewInfos = new ArrayList
mFooterViewInfos = new ArrayList
// begin our structure mode drawing from the highest
mLayoutMode = LAYOUT_NORMAL;
}
@Override
protected void onAttachedToWindow() {
tremendous.onAttachedToWindow();
if (mAdapter != null) {
// Knowledge could have modified whereas we had been indifferent. Refresh.
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
}
mIsAttached = true;
}
@Override
protected void onDetachedFromWindow() {
tremendous.onDetachedFromWindow();
// Detach any view left within the scrap heap
mRecycleBin.clear();
if (mFlingRunnable != null) {
removeCallbacks(mFlingRunnable);
}
mIsAttached = false;
}
@Override
protected void onFocusChanged(boolean gainFocus, int route, Rect previouslyFocusedRect) {
// TODO : deal with focus and its influence on choice – if we add merchandise choice help
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
// TODO : deal with focus and its influence on choice – if we add merchandise choice help
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
onSizeChanged(w, h);
}
protected void onSizeChanged(int w, int h) {
if (getChildCount() > 0) {
stopFlingRunnable();
mRecycleBin.clear();
mDataChanged = true;
rememberSyncState();
}
}
@Override
public ListAdapter getAdapter() {
return mAdapter;
}
@Override
public void setAdapter(closing ListAdapter adapter) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(mObserver);
}
// use a wrapper record adapter if we now have a header or footer
if (mHeaderViewInfos.dimension() > 0 || mFooterViewInfos.dimension() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
}
else {
mAdapter = adapter;
}
mDataChanged = true;
mItemCount = mAdapter != null ? mAdapter.getCount() : 0;
if (mAdapter != null) {
mAdapter.registerDataSetObserver(mObserver);
mRecycleBin.setViewTypeCount(mAdapter.getViewTypeCount());
}
requestLayout();
}
@Override
public int getCount() {
return mItemCount;
}
@Override
public View getSelectedView() {
if (DBG) Log.e(TAG, “getSelectedView() is not supported in ExtendableListView yet”);
return null;
}
@Override
public void setSelection(closing int place) {
if (place >= 0) {
mLayoutMode = LAYOUT_SYNC;
mSpecificTop = getListPaddingTop();
mFirstPosition = 0;
if (mNeedSync) {
mSyncPosition = place;
mSyncRowId = mAdapter.getItemId(place);
}
requestLayout();
}
}
public void addHeaderView(View v, Object knowledge, boolean isSelectable) {
if (mAdapter != null && !(mAdapter instanceof HeaderViewListAdapter)) {
throw new IllegalStateException(
“Cannot add header view to list — setAdapter has already been called.”);
}
FixedViewInfo information = new FixedViewInfo();
information.view = v;
information.knowledge = knowledge;
information.isSelectable = isSelectable;
mHeaderViewInfos.add(information);
if (mAdapter != null && mObserver != null) {
mObserver.onChanged();
}
}
public void addHeaderView(View v) {
addHeaderView(v, null, true);
}
public int getHeaderViewsCount() {
return mHeaderViewInfos.dimension();
}
boolean removeHeaderView(View v) {
if (mHeaderViewInfos.dimension() > 0) {
boolean outcome = false;
if (mAdapter != null && ((HeaderViewListAdapter) mAdapter).removeHeader(v)) {
if (mObserver != null) {
mObserver.onChanged();
}
outcome = true;
}
removeFixedViewInfo(v, mHeaderViewInfos);
return outcome;
}
return false;
}
personal void removeFixedViewInfo(View v, ArrayList
int len = the place.dimension();
for (int i = 0; i < len; ++i) {
FixedViewInfo info = where.get(i);
if (info.view == v) {
where.remove(i);
break;
}
}
}
public void addFooterView(View v, Object data, boolean isSelectable) {
FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
info.isSelectable = isSelectable;
mFooterViewInfos.add(info);
// in the case of re-adding a footer view, or adding one later on,
// we need to notify the observer
if (mAdapter != null && mObserver != null) {
mObserver.onChanged();
}
}
public void addFooterView(View v) {
addFooterView(v, null, true);
}
public int getFooterViewsCount() {
return mFooterViewInfos.size();
}
public boolean removeFooterView(View v) {
if (mFooterViewInfos.size() > 0) {
boolean outcome = false;
if (mAdapter != null && ((HeaderViewListAdapter) mAdapter).removeFooter(v)) {
if (mObserver != null) {
mObserver.onChanged();
}
outcome = true;
}
removeFixedViewInfo(v, mFooterViewInfos);
return outcome;
}
return false;
}
@Override
public void setClipToPadding(closing boolean clipToPadding) {
tremendous.setClipToPadding(clipToPadding);
mClipToPadding = clipToPadding;
}
@Override
public void requestLayout() {
if (!mBlockLayoutRequests && !mInLayout) {
tremendous.requestLayout();
}
}
@Override
protected void onLayout(closing boolean modified, closing int l, closing int t, closing int r, closing int b) {
// tremendous.onLayout(modified, l, t, r, b); – skipping base AbsListView implementation on objective
// haven’t set an adapter but? get to it
if (mAdapter == null) {
return;
}
if (modified) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
getChildAt(i).forceLayout();
}
mRecycleBin.markChildrenDirty();
}
// TODO get the peak of the view??
mInLayout = true;
layoutChildren();
mInLayout = false;
}
@Override
protected void layoutChildren() {
if (mBlockLayoutRequests) return;
mBlockLayoutRequests = true;
attempt {
tremendous.layoutChildren();
invalidate();
if (mAdapter == null) {
clearState();
invokeOnItemScrollListener();
return;
}
int childrenTop = getListPaddingTop();
int childCount = getChildCount();
View oldFirst = null;
// our final state so we preserve our place
if (mLayoutMode == LAYOUT_NORMAL) {
oldFirst = getChildAt(0);
}
boolean dataChanged = mDataChanged;
if (dataChanged) {
handleDataChanged();
}
// security test!
// Deal with the empty set by eradicating all views which are seen
// and calling it a day
if (mItemCount == 0) {
clearState();
invokeOnItemScrollListener();
return;
}
else if (mItemCount != mAdapter.getCount()) {
throw new IllegalStateException("The content of the adapter has changed but "
+ "ExtendableListView did not receive a notification. Make sure the content of "
+ "your adapter is not modified from a background thread, but only "
+ "from the UI thread. [in ExtendableListView(" + getId() + ", " + getClass()
+ ") with Adapter(" + mAdapter.getClass() + ")]");
}
// Pull all youngsters into the RecycleBin.
// These views will likely be reused if attainable
closing int firstPosition = mFirstPosition;
closing RecycleBin recycleBin = mRecycleBin;
if (dataChanged) {
for (int i = 0; i < childCount; i++) {
recycleBin.addScrapView(getChildAt(i), firstPosition + i);
}
}
else {
recycleBin.fillActiveViews(childCount, firstPosition);
}
// Filter out previous views
detachAllViewsFromParent();
recycleBin.removeSkippedScrap();
change (mLayoutMode) {
case LAYOUT_FORCE_TOP: {
mFirstPosition = 0;
resetToTop();
adjustViewsUpOrDown();
fillFromTop(childrenTop);
adjustViewsUpOrDown();
break;
}
case LAYOUT_SYNC: {
fillSpecific(mSyncPosition, mSpecificTop);
break;
}
case LAYOUT_NORMAL:
default: {
if (childCount == 0) {
fillFromTop(childrenTop);
}
else if (mFirstPosition < mItemCount) {
fillSpecific(mFirstPosition,
oldFirst == null ? childrenTop : oldFirst.getTop());
}
else {
fillSpecific(0, childrenTop);
}
break;
}
}
// Flush any cached views that did not get reused above
recycleBin.scrapActiveViews();
mDataChanged = false;
mNeedSync = false;
mLayoutMode = LAYOUT_NORMAL;
invokeOnItemScrollListener();
} finally {
mBlockLayoutRequests = false;
}
}
@Override
protected void handleDataChanged() {
super.handleDataChanged();
final int count = mItemCount;
if (count > 0 && mNeedSync) {
mNeedSync = false;
mSyncState = null;
mLayoutMode = LAYOUT_SYNC;
mSyncPosition = Math.min(Math.max(0, mSyncPosition), rely – 1);
return;
}
mLayoutMode = LAYOUT_FORCE_TOP;
mNeedSync = false;
mSyncState = null;
// TODO : add choice dealing with right here
}
public void resetToTop() {
// TO override
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
tremendous.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize, heightSize);
mWidthMeasureSpec = widthMeasureSpec;
}
@Override
public boolean onTouchEvent(MotionEvent occasion) {
if (!isEnabled())
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(occasion);
if (!hasChildren()) return false;
boolean dealt with;
closing int motion = occasion.getAction() & MotionEventCompat.ACTION_MASK;
change (motion) {
case MotionEvent.ACTION_DOWN:
dealt with = onTouchDown(occasion);
break;
case MotionEvent.ACTION_MOVE:
dealt with = onTouchMove(occasion);
break;
case MotionEvent.ACTION_CANCEL:
dealt with = onTouchCancel(occasion);
break;
case MotionEvent.ACTION_POINTER_UP:
dealt with = onTouchPointerUp(occasion);
break;
case MotionEvent.ACTION_UP:
dealt with = onTouchUp(occasion);
break;
default:
dealt with = false;
break;
}
notifyTouchMode();
return dealt with;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int motion = ev.getAction();
if (!mIsAttached) {
return false;
}
change (motion & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
int touchMode = mTouchMode;
closing int x = (int) ev.getX();
closing int y = (int) ev.getY();
mActivePointerId = ev.getPointerId(0);
int motionPosition = findMotionRow(y);
if (touchMode != TOUCH_MODE_FLINGING && motionPosition >= 0) {
// Consumer clicked on an precise view (and was not stopping a fling).
// Bear in mind the place the movement occasion began
mMotionX = x;
mMotionY = y;
mMotionPosition = motionPosition;
mTouchMode = TOUCH_MODE_DOWN;
}
mLastY = Integer.MIN_VALUE;
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
if (touchMode == TOUCH_MODE_FLINGING) {
return true;
}
break;
}
case MotionEvent.ACTION_MOVE: {
change (mTouchMode) {
case TOUCH_MODE_DOWN:
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
pointerIndex = 0;
mActivePointerId = ev.getPointerId(pointerIndex);
}
closing int y = (int) ev.getY(pointerIndex);
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
if (startScrollIfNeeded(y)) {
return true;
}
break;
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
mTouchMode = TOUCH_MODE_IDLE;
mActivePointerId = INVALID_POINTER;
recycleVelocityTracker();
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
break;
}
case MotionEvent.ACTION_POINTER_UP: {
onSecondaryPointerUp(ev);
break;
}
}
return false;
}
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (disallowIntercept) {
recycleVelocityTracker();
}
tremendous.requestDisallowInterceptTouchEvent(disallowIntercept);
}
closing class CheckForTap implements Runnable {
public void run() {
if (mTouchMode == TOUCH_MODE_DOWN) {
mTouchMode = TOUCH_MODE_TAP;
closing View little one = getChildAt(mMotionPosition);
if (little one != null && !little one.hasFocusable()) {
mLayoutMode = LAYOUT_NORMAL;
if (!mDataChanged) {
layoutChildren();
little one.setPressed(true);
setPressed(true);
closing int longPressTimeout = ViewConfiguration.getLongPressTimeout();
closing boolean longClickable = isLongClickable();
if (longClickable) {
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
mPendingCheckForLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForLongPress, longPressTimeout);
} else {
mTouchMode = TOUCH_MODE_DONE_WAITING;
}
} else {
mTouchMode = TOUCH_MODE_DONE_WAITING;
}
}
}
}
}
personal boolean onTouchDown(closing MotionEvent occasion) {
closing int x = (int) occasion.getX();
closing int y = (int) occasion.getY();
int motionPosition = pointToPosition(x, y);
mVelocityTracker.clear();
mActivePointerId = MotionEventCompat.getPointerId(occasion, 0);
if ((mTouchMode != TOUCH_MODE_FLINGING) &&
!mDataChanged &&
motionPosition >= 0 &&
getAdapter().isEnabled(motionPosition)) {
// is it a faucet or a scroll .. we don’t know but!
mTouchMode = TOUCH_MODE_DOWN;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
if (occasion.getEdgeFlags() != 0 && motionPosition < 0) {
return false;
}
}
else if (mTouchMode == TOUCH_MODE_FLINGING) {
mTouchMode = TOUCH_MODE_SCROLLING;
mMotionCorrection = 0;
motionPosition = findMotionRow(y);
}
mMotionX = x;
mMotionY = y;
mMotionPosition = motionPosition;
mLastY = Integer.MIN_VALUE;
return true;
}
personal boolean onTouchMove(closing MotionEvent occasion) {
closing int index = MotionEventCompat.findPointerIndex(occasion, mActivePointerId);
if (index < 0) {
Log.e(TAG, "onTouchMove could not find pointer with id " +
mActivePointerId + " - did ExtendableListView receive an inconsistent " +
"event stream?");
return false;
}
final int y = (int) MotionEventCompat.getY(event, index);
// our data's changed so we need to do a layout before moving any further
if (mDataChanged) {
layoutChildren();
}
switch (mTouchMode) {
case TOUCH_MODE_DOWN:
case TOUCH_MODE_TAP:
case TOUCH_MODE_DONE_WAITING:
startScrollIfNeeded(y);
break;
case TOUCH_MODE_SCROLLING:
scrollIfNeeded(y);
break;
}
return true;
}
private boolean onTouchCancel(final MotionEvent event) {
mTouchMode = TOUCH_MODE_IDLE;
setPressed(false);
invalidate(); // redraw selector
final Handler handler = getHandler();
if (handler != null) {
handler.removeCallbacks(mPendingCheckForLongPress);
}
recycleVelocityTracker();
mActivePointerId = INVALID_POINTER;
return true;
}
private boolean onTouchUp(final MotionEvent event) {
switch (mTouchMode) {
case TOUCH_MODE_DOWN:
case TOUCH_MODE_TAP:
case TOUCH_MODE_DONE_WAITING:
return onTouchUpTap(event);
case TOUCH_MODE_SCROLLING:
return onTouchUpScrolling(event);
}
setPressed(false);
invalidate(); // redraw selector
final Handler handler = getHandler();
if (handler != null) {
handler.removeCallbacks(mPendingCheckForLongPress);
}
recycleVelocityTracker();
mActivePointerId = INVALID_POINTER;
return true;
}
private boolean onTouchUpScrolling(final MotionEvent event) {
if (hasChildren()) {
// 2 - Are we at the top or bottom?
int top = getFirstChildTop();
int bottom = getLastChildBottom();
final boolean atEdge = mFirstPosition == 0 &&
top >= getListPaddingTop() &&
mFirstPosition + getChildCount() < mItemCount &&
backside <= getHeight() - getListPaddingBottom();
if (!atEdge) {
mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
final float velocity = mVelocityTracker.getYVelocity(mActivePointerId);
if (Math.abs(velocity) > mFlingVelocity) {
startFlingRunnable(velocity);
mTouchMode = TOUCH_MODE_FLINGING;
mMotionY = 0;
invalidate();
return true;
}
}
}
stopFlingRunnable();
recycleVelocityTracker();
mTouchMode = TOUCH_MODE_IDLE;
return true;
}
personal boolean onTouchUpTap(closing MotionEvent occasion) {
closing int motionPosition = mMotionPosition;
if (motionPosition >= 0) {
closing View little one = getChildAt(motionPosition);
if (little one != null && !little one.hasFocusable()) {
if (mTouchMode != TOUCH_MODE_DOWN) {
little one.setPressed(false);
}
if (mPerformClick == null) {
invalidate();
mPerformClick = new PerformClick();
}
closing PerformClick performClick = mPerformClick;
performClick.mClickMotionPosition = motionPosition;
performClick.rememberWindowAttachCount();
// mResurrectToPosition = motionPosition;
if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {
closing Handler handler = getHandler();
if (handler != null) {
handler.removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?
mPendingCheckForTap : mPendingCheckForLongPress);
}
mLayoutMode = LAYOUT_NORMAL;
if (!mDataChanged && motionPosition >= 0 && mAdapter.isEnabled(motionPosition)) {
mTouchMode = TOUCH_MODE_TAP;
layoutChildren();
little one.setPressed(true);
setPressed(true);
postDelayed(new Runnable() {
public void run() {
little one.setPressed(false);
setPressed(false);
if (!mDataChanged) {
submit(performClick);
}
mTouchMode = TOUCH_MODE_IDLE;
}
}, ViewConfiguration.getPressedStateDuration());
} else {
mTouchMode = TOUCH_MODE_IDLE;
}
return true;
} else if (!mDataChanged && motionPosition >= 0 && mAdapter.isEnabled(motionPosition)) {
submit(performClick);
}
}
}
mTouchMode = TOUCH_MODE_IDLE;
return true;
}
personal boolean onTouchPointerUp(closing MotionEvent occasion) {
onSecondaryPointerUp(occasion);
closing int x = mMotionX;
closing int y = mMotionY;
closing int motionPosition = pointToPosition(x, y);
if (motionPosition >= 0) {
mMotionPosition = motionPosition;
}
mLastY = y;
return true;
}
personal void onSecondaryPointerUp(MotionEvent occasion) {
closing int pointerIndex = (occasion.getAction() &
MotionEventCompat.ACTION_POINTER_INDEX_MASK) >>
MotionEventCompat.ACTION_POINTER_INDEX_SHIFT;
closing int pointerId = occasion.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our lively pointer going up. Select a brand new
// lively pointer and alter accordingly.
// TODO: Make this choice extra clever.
closing int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mMotionX = (int) occasion.getX(newPointerIndex);
mMotionY = (int) occasion.getY(newPointerIndex);
mActivePointerId = occasion.getPointerId(newPointerIndex);
recycleVelocityTracker();
}
}
personal boolean startScrollIfNeeded(closing int y) {
closing int deltaY = y – mMotionY;
closing int distance = Math.abs(deltaY);
// TODO : Overscroll?
// closing boolean overscroll = mScrollY != 0;
closing boolean overscroll = false;
if (overscroll || distance > mTouchSlop) {
if (overscroll) {
mMotionCorrection = 0;
}
else {
mTouchMode = TOUCH_MODE_SCROLLING;
mMotionCorrection = deltaY > 0 ? mTouchSlop : -mTouchSlop;
}
closing Handler handler = getHandler();
if (handler != null) {
handler.removeCallbacks(mPendingCheckForLongPress);
}
setPressed(false);
View motionView = getChildAt(mMotionPosition – mFirstPosition);
if (motionView != null) {
motionView.setPressed(false);
}
closing ViewParent dad or mum = getParent();
if (dad or mum != null) {
dad or mum.requestDisallowInterceptTouchEvent(true);
}
scrollIfNeeded(y);
return true;
}
return false;
}
personal void scrollIfNeeded(closing int y) {
if (DBG) Log.d(TAG, “scrollIfNeeded y: ” + y);
closing int rawDeltaY = y – mMotionY;
closing int deltaY = rawDeltaY – mMotionCorrection;
int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y – mLastY : deltaY;
if (mTouchMode == TOUCH_MODE_SCROLLING) {
if (DBG) Log.d(TAG, “scrollIfNeeded TOUCH_MODE_SCROLLING”);
if (y != mLastY) {
// cease our dad or mum
if (Math.abs(rawDeltaY) > mTouchSlop) {
closing ViewParent dad or mum = getParent();
if (dad or mum != null) {
dad or mum.requestDisallowInterceptTouchEvent(true);
}
}
closing int motionIndex;
if (mMotionPosition >= 0) {
motionIndex = mMotionPosition – mFirstPosition;
}
else {
motionIndex = getChildCount() / 2;
}
// No must do all this work if we’re not going to maneuver anyway
boolean atEdge = false;
if (incrementalDeltaY != 0) {
atEdge = moveTheChildren(deltaY, incrementalDeltaY);
}
// Examine to see if we now have ran into the scroll restrict
View motionView = this.getChildAt(motionIndex);
if (motionView != null) {
if (atEdge) {
// TODO : edge impact & overscroll
}
mMotionY = y;
}
mLastY = y;
}
}
// TODO : ELSE SUPPORT OVERSCROLL!
}
personal int findMotionRow(int y) {
int childCount = getChildCount();
if (childCount > 0) {
// all the time from the highest
for (int i = 0; i < childCount; i++) {
View v = getChildAt(i);
if (y <= v.getBottom()) {
return mFirstPosition + i;
}
}
}
return INVALID_POSITION;
}
personal boolean moveTheChildren(int deltaY, int incrementalDeltaY) {
if (DBG) Log.d(TAG, "moveTheChildren deltaY: " + deltaY + "incrementalDeltaY: " + incrementalDeltaY);
// there's nothing to maneuver!
if (!hasChildren()) return true;
closing int firstTop = getHighestChildTop();
closing int lastBottom = getLowestChildBottom();
int effectivePaddingTop = 0;
int effectivePaddingBottom = 0;
if (mClipToPadding) {
effectivePaddingTop = getListPaddingTop();
effectivePaddingBottom = getListPaddingBottom();
}
closing int gridHeight = getHeight();
closing int spaceAbove = effectivePaddingTop - getFirstChildTop();
closing int finish = gridHeight - effectivePaddingBottom;
closing int spaceBelow = getLastChildBottom() - finish;
closing int top = gridHeight - getListPaddingBottom() - getListPaddingTop();
if (incrementalDeltaY < 0) {
incrementalDeltaY = Math.max(-(height - 1), incrementalDeltaY);
}
else {
incrementalDeltaY = Math.min(height - 1, incrementalDeltaY);
}
final int firstPosition = mFirstPosition;
int maxTop = getListPaddingTop();
int maxBottom = gridHeight - getListPaddingBottom();
int childCount = getChildCount();
final boolean cannotScrollDown = (firstPosition == 0 &&
firstTop >= maxTop && incrementalDeltaY >= 0);
closing boolean cannotScrollUp = (firstPosition + childCount == mItemCount &&
lastBottom <= maxBottom && incrementalDeltaY <= 0);
if (DBG) {
Log.d(TAG, "moveTheChildren " +
" firstTop " + firstTop +
" maxTop " + maxTop +
" incrementalDeltaY " + incrementalDeltaY);
Log.d(TAG, "moveTheChildren " +
" lastBottom " + lastBottom +
" maxBottom " + maxBottom +
" incrementalDeltaY " + incrementalDeltaY);
}
if (cannotScrollDown) {
if (DBG) Log.d(TAG, "moveTheChildren cannotScrollDown " + cannotScrollDown);
return incrementalDeltaY != 0;
}
if (cannotScrollUp) {
if (DBG) Log.d(TAG, "moveTheChildren cannotScrollUp " + cannotScrollUp);
return incrementalDeltaY != 0;
}
closing boolean isDown = incrementalDeltaY < 0;
closing int headerViewsCount = getHeaderViewsCount();
closing int footerViewsStart = mItemCount - getFooterViewsCount();
int begin = 0;
int rely = 0;
if (isDown) {
int prime = -incrementalDeltaY;
if (mClipToPadding) {
prime += getListPaddingTop();
}
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
if (child.getBottom() >= prime) {
break;
}
else {
rely++;
int place = firstPosition + i;
if (place >= headerViewsCount && place < footerViewsStart) {
mRecycleBin.addScrapView(child, position);
}
}
}
}
else {
int bottom = gridHeight - incrementalDeltaY;
if (mClipToPadding) {
bottom -= getListPaddingBottom();
}
for (int i = childCount - 1; i >= 0; i–) {
closing View little one = getChildAt(i);
if (little one.getTop() <= bottom) {
break;
}
else {
start = i;
count++;
int position = firstPosition + i;
if (position >= headerViewsCount && place < footerViewsStart) {
mRecycleBin.addScrapView(child, position);
}
}
}
}
mBlockLayoutRequests = true;
if (count > 0) {
if (DBG) Log.d(TAG, “scrap – detachViewsFromParent start:” + begin + ” rely:” + rely);
detachViewsFromParent(begin, rely);
mRecycleBin.removeSkippedScrap();
onChildrenDetached(begin, rely);
}
if (!awakenScrollBars()) {
invalidate();
}
offsetChildrenTopAndBottom(incrementalDeltaY);
if (isDown) {
mFirstPosition += rely;
}
closing int absIncrementalDeltaY = Math.abs(incrementalDeltaY);
if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) {
fillGap(isDown);
}
// TODO : contact mode selector dealing with
mBlockLayoutRequests = false;
invokeOnItemScrollListener();
return false;
}
protected void onChildrenDetached(closing int begin, closing int rely) {
}
protected void fillGap(boolean down) {
closing int rely = getChildCount();
if (down) {
// fill down from the highest of the place under our final
int place = mFirstPosition + rely;
closing int startOffset = getChildTop(place);
fillDown(place, startOffset);
}
else {
// refill from the underside of the place above our first.
int place = mFirstPosition - 1;
closing int startOffset = getChildBottom(place);
fillUp(place, startOffset);
}
adjustViewsAfterFillGap(down);
}
protected void adjustViewsAfterFillGap(boolean down) {
if (down) {
correctTooHigh(getChildCount());
}
else {
correctTooLow(getChildCount());
}
}
personal View fillDown(int pos, int nextTop) {
if (DBG) Log.d(TAG, "fillDown - pos:" + pos + " nextTop:" + nextTop);
View selectedView = null;
int finish = getHeight();
if (mClipToPadding) {
finish -= getListPaddingBottom();
}
whereas ((nextTop < finish || hasSpaceDown()) && pos < mItemCount) {
// TODO : add selection support
makeAndAddView(pos, nextTop, true, false);
pos++;
nextTop = getNextChildDownsTop(pos); // = child.getBottom();
}
return selectedView;
}
protected boolean hasSpaceDown() {
return false;
}
private View fillUp(int pos, int nextBottom) {
if (DBG) Log.d(TAG, "fillUp - position:" + pos + " nextBottom:" + nextBottom);
View selectedView = null;
int end = mClipToPadding ? getListPaddingTop() : 0;
while ((nextBottom > finish || hasSpaceUp()) && pos >= 0) {
// TODO : add choice help
makeAndAddView(pos, nextBottom, false, false);
pos–;
nextBottom = getNextChildUpsBottom(pos);
if (DBG) Log.d(TAG, “fillUp next – position:” + pos + ” nextBottom:” + nextBottom);
}
mFirstPosition = pos + 1;
return selectedView;
}
protected boolean hasSpaceUp() {
return false;
}
personal View fillFromTop(int nextTop) {
mFirstPosition = Math.min(mFirstPosition, mItemCount – 1);
if (mFirstPosition < 0) {
mFirstPosition = 0;
}
return fillDown(mFirstPosition, nextTop);
}
private View fillSpecific(int position, int top) {
boolean tempIsSelected = false; // ain't no body got time for that @ Etsy
View temp = makeAndAddView(position, top, true, tempIsSelected);
// Possibly changed again in fillUp if we add rows above this one.
mFirstPosition = position;
View above;
View below;
int nextBottom = getNextChildUpsBottom(position - 1);
int nextTop = getNextChildDownsTop(position + 1);
above = fillUp(position - 1, nextBottom);
// This will correct for the top of the first view not touching the top of the list
adjustViewsUpOrDown();
below = fillDown(position + 1, nextTop);
int childCount = getChildCount();
if (childCount > 0) {
correctTooHigh(childCount);
}
if (tempIsSelected) {
return temp;
}
else if (above != null) {
return above;
}
else {
return under;
}
}
personal View makeAndAddView(int place, int y, boolean flowDown, boolean chosen) {
View little one;
onChildCreated(place, flowDown);
if (!mDataChanged) {
// Attempt to use an current view for this place
little one = mRecycleBin.getActiveView(place);
if (little one != null) {
// Discovered it — we’re utilizing an current little one
// This simply must be positioned
setupChild(little one, place, y, flowDown, chosen, true);
return little one;
}
}
// Make a brand new view for this place, or convert an unused view if attainable
little one = obtainView(place, mIsScrap);
// This must be positioned and measured
setupChild(little one, place, y, flowDown, chosen, mIsScrap[0]);
return little one;
}
personal void setupChild(View little one, int place, int y, boolean flowDown,
boolean chosen, boolean recycled) {
closing boolean isSelected = false; // TODO : chosen && shouldShowSelector();
closing boolean updateChildSelected = isSelected != little one.isSelected();
closing int mode = mTouchMode;
closing boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLLING &&
mMotionPosition == position;
final boolean updateChildPressed = isPressed != child.isPressed();
final boolean needToMeasure = !recycled || updateChildSelected || child.isLayoutRequested();
int itemViewType = mAdapter.getItemViewType(position);
LayoutParams layoutParams;
if (itemViewType == ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
layoutParams = generateWrapperLayoutParams(child);
}
else {
layoutParams = generateChildLayoutParams(child);
}
layoutParams.viewType = itemViewType;
layoutParams.position = position;
if (recycled || (layoutParams.recycledHeaderFooter &&
layoutParams.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) {
if (DBG) Log.d(TAG, "setupChild attachViewToParent position:" + position);
attachViewToParent(child, flowDown ? -1 : 0, layoutParams);
}
else {
if (DBG) Log.d(TAG, "setupChild addViewInLayout position:" + position);
if (layoutParams.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
layoutParams.recycledHeaderFooter = true;
}
addViewInLayout(child, flowDown ? -1 : 0, layoutParams, true);
}
if (updateChildSelected) {
child.setSelected(isSelected);
}
if (updateChildPressed) {
child.setPressed(isPressed);
}
if (needToMeasure) {
if (DBG) Log.d(TAG, "setupChild onMeasureChild position:" + position);
onMeasureChild(child, layoutParams);
}
else {
if (DBG) Log.d(TAG, "setupChild cleanupLayoutState position:" + position);
cleanupLayoutState(child);
}
final int w = child.getMeasuredWidth();
final int h = child.getMeasuredHeight();
final int childTop = flowDown ? y : y - h;
if (DBG) {
Log.d(TAG, "setupChild position:" + position + " h:" + h + " w:" + w);
}
final int childrenLeft = getChildLeft(position);
if (needToMeasure) {
final int childRight = childrenLeft + w;
final int childBottom = childTop + h;
onLayoutChild(child, position, flowDown, childrenLeft, childTop, childRight, childBottom);
}
else {
onOffsetChild(child, position, flowDown, childrenLeft, childTop);
}
}
protected LayoutParams generateChildLayoutParams(final View child) {
return generateWrapperLayoutParams(child);
}
protected LayoutParams generateWrapperLayoutParams(final View child) {
LayoutParams layoutParams = null;
final ViewGroup.LayoutParams childParams = child.getLayoutParams();
if (childParams != null) {
if (childParams instanceof LayoutParams) {
layoutParams = (LayoutParams) childParams;
}
else {
layoutParams = new LayoutParams(childParams);
}
}
if (layoutParams == null) {
layoutParams = generateDefaultLayoutParams();
}
return layoutParams;
}
protected void onMeasureChild(final View child, final LayoutParams layoutParams) {
int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,
getListPaddingLeft() + getListPaddingRight(), layoutParams.width);
int lpHeight = layoutParams.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
}
else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
little one.measure(childWidthSpec, childHeightSpec);
}
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 0);
}
protected LayoutParams generateHeaderFooterLayoutParams(closing View little one) {
return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 0);
}
personal View obtainView(int place, boolean[] isScrap) {
isScrap[0] = false;
View scrapView;
scrapView = mRecycleBin.getScrapView(place);
View little one;
if (scrapView != null) {
if (DBG) Log.d(TAG, “getView from scrap position:” + place);
little one = mAdapter.getView(place, scrapView, this);
if (little one != scrapView) {
mRecycleBin.addScrapView(scrapView, place);
}
else {
isScrap[0] = true;
}
}
else {
if (DBG) Log.d(TAG, “getView position:” + place);
little one = mAdapter.getView(place, null, this);
}
return little one;
}
personal void correctTooHigh(int childCount) {
// First see if the final merchandise is seen. If it isn’t, it’s OK for the
// prime of the record to be pushed up.
int lastPosition = mFirstPosition + childCount – 1;
if (lastPosition == mItemCount – 1 && childCount > 0) {
// … and its backside edge
closing int lastBottom = getLowestChildBottom();
// That is backside of our drawable space
closing int finish = (getBottom() – getTop()) – getListPaddingBottom();
// That is how far the underside fringe of the final view is from the underside of the
// drawable space
int bottomOffset = finish – lastBottom;
closing int firstTop = getHighestChildTop();
// Ensure we’re 1) Too excessive, and a pair of) Both there are extra rows above the
// first row or the primary row is scrolled off the highest of the drawable space
if (bottomOffset > 0 && (mFirstPosition > 0 || firstTop < getListPaddingTop())) {
if (mFirstPosition == 0) {
// Don't pull the top too far down
bottomOffset = Math.min(bottomOffset, getListPaddingTop() - firstTop);
}
// Move everything down
offsetChildrenTopAndBottom(bottomOffset);
if (mFirstPosition > 0) {
// Fill the hole that was opened above mFirstPosition with extra rows, if
// attainable
int previousPosition = mFirstPosition – 1;
fillUp(previousPosition, getNextChildUpsBottom(previousPosition));
// Shut up the remaining hole
adjustViewsUpOrDown();
}
}
}
}
personal void correctTooLow(int childCount) {
// First see if the primary merchandise is seen. If it isn’t, it’s OK for the
// backside of the record to be pushed down.
if (mFirstPosition == 0 && childCount > 0) {
// … and its prime edge
closing int firstTop = getHighestChildTop();
// That is prime of our drawable space
closing int begin = getListPaddingTop();
// That is backside of our drawable space
closing int finish = (getTop() – getBottom()) – getListPaddingBottom();
// That is how far the highest fringe of the primary view is from the highest of the
// drawable space
int topOffset = firstTop – begin;
closing int lastBottom = getLowestChildBottom();
int lastPosition = mFirstPosition + childCount – 1;
// Ensure we’re 1) Too low, and a pair of) Both there are extra rows under the
// final row or the final row is scrolled off the underside of the drawable space
if (topOffset > 0) {
if (lastPosition < mItemCount - 1 || lastBottom > finish) {
if (lastPosition == mItemCount – 1) {
// Don’t pull the underside too far up
topOffset = Math.min(topOffset, lastBottom – finish);
}
// Transfer every little thing up
offsetChildrenTopAndBottom(-topOffset);
if (lastPosition < mItemCount - 1) {
// Fill the gap that was opened below the last position with more rows, if
// possible
int nextPosition = lastPosition + 1;
fillDown(nextPosition, getNextChildDownsTop(nextPosition));
// Close up the remaining gap
adjustViewsUpOrDown();
}
}
else if (lastPosition == mItemCount - 1) {
adjustViewsUpOrDown();
}
}
}
}
private void adjustViewsUpOrDown() {
final int childCount = getChildCount();
int delta;
if (childCount > 0) {
// Uh-oh — we got here up quick. Slide all views as much as make them
// align with the highest
delta = getHighestChildTop() – getListPaddingTop();
if (delta < 0) {
// We only are looking to see if we are too low, not too high
delta = 0;
}
if (delta != 0) {
offsetChildrenTopAndBottom(-delta);
}
}
}
protected void onChildCreated(final int position, final boolean flowDown) {
}
protected void onLayoutChild(final View child, final int position,
final boolean flowDown, final int childrenLeft, final int childTop,
final int childRight, final int childBottom) {
child.layout(childrenLeft, childTop, childRight, childBottom);
}
protected void onOffsetChild(final View child, final int position,
final boolean flowDown, final int childrenLeft, final int childTop) {
child.offsetLeftAndRight(childrenLeft - child.getLeft());
child.offsetTopAndBottom(childTop - child.getTop());
}
protected int getChildLeft(final int position) {
return getListPaddingLeft();
}
protected int getChildTop(final int position) {
int count = getChildCount();
int paddingTop = 0;
if (mClipToPadding) {
paddingTop = getListPaddingTop();
}
return count > 0 ? getChildAt(rely – 1).getBottom() : paddingTop;
}
protected int getChildBottom(closing int place) {
int rely = getChildCount();
int paddingBottom = 0;
if (mClipToPadding) {
paddingBottom = getListPaddingBottom();
}
return rely > 0 ? getChildAt(0).getTop() : getHeight() – paddingBottom;
}
protected int getNextChildDownsTop(closing int place) {
closing int rely = getChildCount();
return rely > 0 ? getChildAt(rely – 1).getBottom() : 0;
}
protected int getNextChildUpsBottom(closing int place) {
closing int rely = getChildCount();
if (rely == 0) {
return 0;
}
return rely > 0 ? getChildAt(0).getTop() : 0;
}
protected int getFirstChildTop() {
return hasChildren() ? getChildAt(0).getTop() : 0;
}
protected int getHighestChildTop() {
return hasChildren() ? getChildAt(0).getTop() : 0;
}
protected int getLastChildBottom() {
return hasChildren() ? getChildAt(getChildCount() – 1).getBottom() : 0;
}
protected int getLowestChildBottom() {
return hasChildren() ? getChildAt(getChildCount() – 1).getBottom() : 0;
}
protected boolean hasChildren() {
return getChildCount() > 0;
}
protected void offsetChildrenTopAndBottom(int offset) {
if (DBG) Log.d(TAG, “offsetChildrenTopAndBottom: ” + offset);
closing int rely = getChildCount();
for (int i = 0; i < rely; i++) {
closing View v = getChildAt(i);
v.offsetTopAndBottom(offset);
}
}
@Override
public int getFirstVisiblePosition() {
return Math.max(0, mFirstPosition - getHeaderViewsCount());
}
@Override
public int getLastVisiblePosition() {
return Math.min(mFirstPosition + getChildCount() - 1, mAdapter != null ? mAdapter.getCount() - 1 : 0);
}
personal void initOrResetVelocityTracker() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.receive();
}
else {
mVelocityTracker.clear();
}
}
personal void initVelocityTrackerIfNotExists() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.receive();
}
}
personal void recycleVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
personal void startFlingRunnable(closing float velocity) {
if (mFlingRunnable == null) {
mFlingRunnable = new FlingRunnable();
}
mFlingRunnable.begin((int) -velocity);
}
personal void stopFlingRunnable() {
if (mFlingRunnable != null) {
mFlingRunnable.endFling();
}
}
personal class FlingRunnable implements Runnable {
personal closing Scroller mScroller;
personal int mLastFlingY;
FlingRunnable() {
mScroller = new Scroller(getContext());
}
void begin(int initialVelocity) {
int initialY = initialVelocity < 0 ? Integer.MAX_VALUE : 0;
mLastFlingY = initialY;
mScroller.forceFinished(true);
mScroller.fling(0, initialY, 0, initialVelocity, 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE);
mTouchMode = TOUCH_MODE_FLINGING;
postOnAnimate(this);
}
void startScroll(int distance, int length) {
int initialY = distance < 0 ? Integer.MAX_VALUE : 0;
mLastFlingY = initialY;
mScroller.startScroll(0, initialY, 0, distance, duration);
mTouchMode = TOUCH_MODE_FLINGING;
postOnAnimate(this);
}
private void endFling() {
mLastFlingY = 0;
mTouchMode = TOUCH_MODE_IDLE;
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
removeCallbacks(this);
mScroller.forceFinished(true);
}
public void run() {
switch (mTouchMode) {
default:
return;
case TOUCH_MODE_FLINGING: {
if (mItemCount == 0 || getChildCount() == 0) {
endFling();
return;
}
final Scroller scroller = mScroller;
boolean more = scroller.computeScrollOffset();
final int y = scroller.getCurrY();
int delta = mLastFlingY - y;
// Pretend that each frame of a fling scroll is a touch scroll
if (delta > 0) {
// Record is transferring in the direction of the highest. Use first view as mMotionPosition
mMotionPosition = mFirstPosition;
// Don’t fling greater than 1 display screen
delta = Math.min(getHeight() – getPaddingBottom() – getPaddingTop() – 1, delta);
}
else {
// Record is transferring in the direction of the underside. Use final view as mMotionPosition
int offsetToLast = getChildCount() – 1;
mMotionPosition = mFirstPosition + offsetToLast;
// Don’t fling greater than 1 display screen
delta = Math.max(-(getHeight() – getPaddingBottom() – getPaddingTop() – 1), delta);
}
closing boolean atEnd = moveTheChildren(delta, delta);
if (extra && !atEnd) {
invalidate();
mLastFlingY = y;
postOnAnimate(this);
}
else {
endFling();
}
break;
}
}
}
}
personal void postOnAnimate(Runnable runnable) {
ViewCompat.postOnAnimation(this, runnable);
}
public void notifyTouchMode() {
// solely inform the scroll listener about some issues we wish it to know
change (mTouchMode) {
case TOUCH_MODE_SCROLLING:
reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
break;
case TOUCH_MODE_FLINGING:
reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
break;
case TOUCH_MODE_IDLE:
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
break;
}
}
personal OnScrollListener mOnScrollListener;
public void setOnScrollListener(OnScrollListener scrollListener) {
tremendous.setOnScrollListener(scrollListener);
mOnScrollListener = scrollListener;
}
void reportScrollStateChange(int newState) {
if (newState != mScrollState) {
mScrollState = newState;
if (mOnScrollListener != null) {
mOnScrollListener.onScrollStateChanged(this, newState);
}
}
}
void invokeOnItemScrollListener() {
if (mOnScrollListener != null) {
mOnScrollListener.onScroll(this, mFirstPosition, getChildCount(), mItemCount);
}
}
@SuppressLint(“WrongCall”) personal void updateEmptyStatus() {
boolean empty = getAdapter() == null || getAdapter().isEmpty();
if (isInFilterMode()) {
empty = false;
}
View emptyView = getEmptyView();
if (empty) {
if (emptyView != null) {
emptyView.setVisibility(View.VISIBLE);
setVisibility(View.GONE);
}
else {
// If the caller simply eliminated our empty view, ensure the record view is seen
setVisibility(View.VISIBLE);
}
// We are actually GONE, so pending layouts is not going to be dispatched.
// Drive one right here to guarantee that the state of the record matches
// the state of the adapter.
if (mDataChanged) {
this.onLayout(false, getLeft(), getTop(), getRight(), getBottom());
}
}
else {
if (emptyView != null) {
emptyView.setVisibility(View.GONE);
}
setVisibility(View.VISIBLE);
}
}
// //////////////////////////////////////////////////////////////////////////////////////////
// ADAPTER OBSERVER
//
class AdapterDataSetObserver extends DataSetObserver {
personal Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();
mRecycleBin.clearTransientStateViews();
// Detect the case the place a cursor that was beforehand invalidated has
// been repopulated with new knowledge.
if (ExtendableListView.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount > 0) {
ExtendableListView.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
}
else {
rememberSyncState();
}
updateEmptyStatus();
requestLayout();
}
@Override
public void onInvalidated() {
mDataChanged = true;
if (ExtendableListView.this.getAdapter().hasStableIds()) {
// Bear in mind the present state for the case the place our internet hosting exercise is being
// stopped and later restarted
mInstanceState = ExtendableListView.this.onSaveInstanceState();
}
// Knowledge is invalid so we should always reset our state
mOldItemCount = mItemCount;
mItemCount = 0;
mNeedSync = false;
updateEmptyStatus();
requestLayout();
}
public void clearSavedState() {
mInstanceState = null;
}
}
public static class LayoutParams extends AbsListView.LayoutParams {
boolean recycledHeaderFooter;
// Place of the view within the knowledge
int place;
// adapter ID the view represents fetched from the adapter if it’s steady
lengthy itemId = -1;
// adapter view sort
int viewType;
public LayoutParams(Context c, AttributeSet attrs) {
tremendous(c, attrs);
}
public LayoutParams(int w, int h) {
tremendous(w, h);
}
public LayoutParams(int w, int h, int viewType) {
tremendous(w, h);
this.viewType = viewType;
}
public LayoutParams(ViewGroup.LayoutParams supply) {
tremendous(supply);
}
}
class RecycleBin {
personal int mFirstActivePosition;
personal View[] mActiveViews = new View[0];
personal ArrayList
personal int mViewTypeCount;
personal ArrayList
personal ArrayList
personal SparseArrayCompat
public void setViewTypeCount(int viewTypeCount) {
if (viewTypeCount < 1) {
throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
}
//noinspection unchecked
ArrayList
for (int i = 0; i < viewTypeCount; i++) {
scrapViews[i] = new ArrayList
}
mViewTypeCount = viewTypeCount;
mCurrentScrap = scrapViews[0];
mScrapViews = scrapViews;
}
public void markChildrenDirty() {
if (mViewTypeCount == 1) {
closing ArrayList
closing int scrapCount = scrap.dimension();
for (int i = 0; i < scrapCount; i++) {
scrap.get(i).forceLayout();
}
}
else {
closing int typeCount = mViewTypeCount;
for (int i = 0; i < typeCount; i++) {
closing ArrayList
closing int scrapCount = scrap.dimension();
for (int j = 0; j < scrapCount; j++) {
scrap.get(j).forceLayout();
}
}
}
if (mTransientStateViews != null) {
closing int rely = mTransientStateViews.dimension();
for (int i = 0; i < count; i++) {
mTransientStateViews.valueAt(i).forceLayout();
}
}
}
public boolean shouldRecycleViewType(int viewType) {
return viewType >= 0;
}
void clear() {
if (mViewTypeCount == 1) {
closing ArrayList
closing int scrapCount = scrap.dimension();
for (int i = 0; i < scrapCount; i++) {
removeDetachedView(scrap.take away(scrapCount - 1 - i), false);
}
}
else {
closing int typeCount = mViewTypeCount;
for (int i = 0; i < typeCount; i++) {
closing ArrayList
closing int scrapCount = scrap.dimension();
for (int j = 0; j < scrapCount; j++) {
removeDetachedView(scrap.take away(scrapCount - 1 - j), false);
}
}
}
if (mTransientStateViews != null) {
mTransientStateViews.clear();
}
}
void fillActiveViews(int childCount, int firstActivePosition) {
if (mActiveViews.size < childCount) {
mActiveViews = new View[childCount];
}
mFirstActivePosition = firstActivePosition;
closing View[] activeViews = mActiveViews;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
// Don't put header or footer views into the scrap heap
if (lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
// Note: We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active views.
// However, we will NOT place them into scrap views.
activeViews[i] = child;
}
}
}
View getActiveView(int position) {
int index = position - mFirstActivePosition;
final View[] activeViews = mActiveViews;
if (index >= 0 && index < activeViews.size) {
closing View match = activeViews[index];
activeViews[index] = null;
return match;
}
return null;
}
View getTransientStateView(int place) {
if (mTransientStateViews == null) {
return null;
}
closing int index = mTransientStateViews.indexOfKey(place);
if (index < 0) {
return null;
}
final View result = mTransientStateViews.valueAt(index);
mTransientStateViews.removeAt(index);
return result;
}
void clearTransientStateViews() {
if (mTransientStateViews != null) {
mTransientStateViews.clear();
}
}
View getScrapView(int position) {
if (mViewTypeCount == 1) {
return retrieveFromScrap(mCurrentScrap, position);
}
else {
int whichScrap = mAdapter.getItemViewType(position);
if (whichScrap >= 0 && whichScrap < mScrapViews.size) {
return retrieveFromScrap(mScrapViews[whichScrap], place);
}
}
return null;
}
void addScrapView(View scrap, int place) {
if (DBG) Log.d(TAG, "addScrapView position = " + place);
LayoutParams lp = (LayoutParams) scrap.getLayoutParams();
if (lp == null) {
return;
}
lp.place = place;
// Do not put header or footer views or views that needs to be ignored
// into the scrap heap
int viewType = lp.viewType;
closing boolean scrapHasTransientState = ViewCompat.hasTransientState(scrap);
if (!shouldRecycleViewType(viewType) || scrapHasTransientState) {
if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER || scrapHasTransientState) {
if (mSkippedScrap == null) {
mSkippedScrap = new ArrayList
}
mSkippedScrap.add(scrap);
}
if (scrapHasTransientState) {
if (mTransientStateViews == null) {
mTransientStateViews = new SparseArrayCompat
}
mTransientStateViews.put(place, scrap);
}
return;
}
if (mViewTypeCount == 1) {
mCurrentScrap.add(scrap);
}
else {
mScrapViews[viewType].add(scrap);
}
}
void removeSkippedScrap() {
if (mSkippedScrap == null) {
return;
}
closing int rely = mSkippedScrap.dimension();
for (int i = 0; i < count; i++) {
removeDetachedView(mSkippedScrap.get(i), false);
}
mSkippedScrap.clear();
}
void scrapActiveViews() {
final View[] activeViews = mActiveViews;
final boolean multipleScraps = mViewTypeCount > 1;
ArrayList
closing int rely = activeViews.size;
for (int i = rely – 1; i >= 0; i–) {
closing View sufferer = activeViews[i];
if (sufferer != null) {
closing LayoutParams lp = (LayoutParams) sufferer.getLayoutParams();
activeViews[i] = null;
closing boolean scrapHasTransientState = ViewCompat.hasTransientState(sufferer);
int viewType = lp.viewType;
if (!shouldRecycleViewType(viewType) || scrapHasTransientState) {
// Don’t transfer views that needs to be ignored
if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER || scrapHasTransientState) {
removeDetachedView(sufferer, false);
}
if (scrapHasTransientState) {
if (mTransientStateViews == null) {
mTransientStateViews = new SparseArrayCompat
}
mTransientStateViews.put(mFirstActivePosition + i, sufferer);
}
proceed;
}
if (multipleScraps) {
scrapViews = mScrapViews[viewType];
}
lp.place = mFirstActivePosition + i;
scrapViews.add(sufferer);
}
}
pruneScrapViews();
}
personal void pruneScrapViews() {
closing int maxViews = mActiveViews.size;
closing int viewTypeCount = mViewTypeCount;
closing ArrayList
for (int i = 0; i < viewTypeCount; ++i) {
closing ArrayList
int dimension = scrapPile.dimension();
closing int extras = dimension – maxViews;
dimension–;
for (int j = 0; j < extras; j++) {
removeDetachedView(scrapPile.take away(size--), false);
}
}
if (mTransientStateViews != null) {
for (int i = 0; i < mTransientStateViews.dimension(); i++) {
closing View v = mTransientStateViews.valueAt(i);
if (!ViewCompat.hasTransientState(v)) {
mTransientStateViews.removeAt(i);
i--;
}
}
}
}
void setCacheColorHint(int shade) {
if (mViewTypeCount == 1) {
closing ArrayList
closing int scrapCount = scrap.dimension();
for (int i = 0; i < scrapCount; i++) {
scrap.get(i).setDrawingCacheBackgroundColor(shade);
}
}
else {
closing int typeCount = mViewTypeCount;
for (int i = 0; i < typeCount; i++) {
closing ArrayList
closing int scrapCount = scrap.dimension();
for (int j = 0; j < scrapCount; j++) {
scrap.get(j).setDrawingCacheBackgroundColor(shade);
}
}
}
// Simply in case that is referred to as throughout a structure go
closing View[] activeViews = mActiveViews;
closing int rely = activeViews.size;
for (int i = 0; i < rely; ++i) {
closing View sufferer = activeViews[i];
if (sufferer != null) {
sufferer.setDrawingCacheBackgroundColor(shade);
}
}
}
}
static View retrieveFromScrap(ArrayList
int dimension = scrapViews.dimension();
if (dimension > 0) {
// See if we nonetheless have a view for this place.
for (int i = 0; i < size; i++) {
View view = scrapViews.get(i);
if (((LayoutParams) view.getLayoutParams()).position == position) {
scrapViews.remove(i);
return view;
}
}
return scrapViews.remove(size - 1);
}
else {
return null;
}
}
protected int mSyncPosition;
protected int mSpecificTop;
long mSyncRowId = INVALID_ROW_ID;
long mSyncHeight;
boolean mNeedSync = false;
private ListSavedState mSyncState;
void rememberSyncState() {
if (getChildCount() > 0) {
mNeedSync = true;
mSyncHeight = getHeight();
// Sync the based mostly on the offset of the primary view
View v = getChildAt(0);
ListAdapter adapter = getAdapter();
if (mFirstPosition >= 0 && mFirstPosition < adapter.getCount()) {
mSyncRowId = adapter.getItemId(mFirstPosition);
}
else {
mSyncRowId = NO_ID;
}
if (v != null) {
mSpecificTop = v.getTop();
}
mSyncPosition = mFirstPosition;
}
}
personal void clearState() {
// cleanup headers and footers earlier than eradicating the views
clearRecycledState(mHeaderViewInfos);
clearRecycledState(mFooterViewInfos);
removeAllViewsInLayout();
mFirstPosition = 0;
mDataChanged = false;
mRecycleBin.clear();
mNeedSync = false;
mSyncState = null;
mLayoutMode = LAYOUT_NORMAL;
invalidate();
}
personal void clearRecycledState(ArrayList
if (infos == null) return;
for (FixedViewInfo information : infos) {
closing View little one = information.view;
closing ViewGroup.LayoutParams p = little one.getLayoutParams();
if (p instanceof LayoutParams) {
((LayoutParams) p).recycledHeaderFooter = false;
}
}
}
public static class ListSavedState extends ClassLoaderSavedState {
protected lengthy selectedId;
protected lengthy firstId;
protected int viewTop;
protected int place;
protected int top;
public ListSavedState(Parcelable superState) {
tremendous(superState, AbsListView.class.getClassLoader());
}
public ListSavedState(Parcel in) {
tremendous(in);
selectedId = in.readLong();
firstId = in.readLong();
viewTop = in.readInt();
place = in.readInt();
top = in.readInt();
}
@Override
public void writeToParcel(Parcel out, int flags) {
tremendous.writeToParcel(out, flags);
out.writeLong(selectedId);
out.writeLong(firstId);
out.writeInt(viewTop);
out.writeInt(place);
out.writeInt(top);
}
@Override
public String toString() {
return “ExtendableListView.ListSavedState{”
+ Integer.toHexString(System.identityHashCode(this))
+ ” selectedId=” + selectedId
+ ” firstId=” + firstId
+ ” viewTop=” + viewTop
+ ” place=” + place
+ ” top=” + top + “}”;
}
public static closing Creator
= new Creator
public ListSavedState createFromParcel(Parcel in) {
return new ListSavedState(in);
}
public ListSavedState[] newArray(int dimension) {
return new ListSavedState[size];
}
};
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = tremendous.onSaveInstanceState();
ListSavedState ss = new ListSavedState(superState);
if (mSyncState != null) {
// Simply preserve what we final restored.
ss.selectedId = mSyncState.selectedId;
ss.firstId = mSyncState.firstId;
ss.viewTop = mSyncState.viewTop;
ss.place = mSyncState.place;
ss.top = mSyncState.top;
return ss;
}
boolean haveChildren = getChildCount() > 0 && mItemCount > 0;
ss.selectedId = getSelectedItemId();
ss.top = getHeight();
// TODO : sync choice after we deal with it
if (haveChildren && mFirstPosition > 0) {
View v = getChildAt(0);
ss.viewTop = v.getTop();
int firstPos = mFirstPosition;
if (firstPos >= mItemCount) {
firstPos = mItemCount – 1;
}
ss.place = firstPos;
ss.firstId = mAdapter.getItemId(firstPos);
}
else {
ss.viewTop = 0;
ss.firstId = INVALID_POSITION;
ss.place = 0;
}
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
ListSavedState ss = (ListSavedState) state;
tremendous.onRestoreInstanceState(ss.getSuperState());
mDataChanged = true;
mSyncHeight = ss.top;
if (ss.firstId >= 0) {
mNeedSync = true;
mSyncState = ss;
mSyncRowId = ss.firstId;
mSyncPosition = ss.place;
mSpecificTop = ss.viewTop;
}
requestLayout();
}
personal class PerformClick extends WindowRunnnable implements Runnable {
int mClickMotionPosition;
public void run() {
if (mDataChanged) return;
closing ListAdapter adapter = mAdapter;
closing int motionPosition = mClickMotionPosition;
if (adapter != null && mItemCount > 0 &&
motionPosition != INVALID_POSITION &&
motionPosition < adapter.getCount() && sameWindow()) {
closing View view = getChildAt(motionPosition); // a repair by @pboos
if (view != null) {
closing int clickPosition = motionPosition + mFirstPosition;
performItemClick(view, clickPosition, adapter.getItemId(clickPosition));
}
}
}
}
personal boolean performLongPress(closing View little one,
closing int longPressPosition, closing lengthy longPressId) {
boolean dealt with = false;
OnItemLongClickListener onItemLongClickListener = getOnItemLongClickListener();
if (onItemLongClickListener != null) {
dealt with = onItemLongClickListener.onItemLongClick(ExtendableListView.this, little one,
longPressPosition, longPressId);
}
if (dealt with) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
return dealt with;
}
personal class WindowRunnnable {
personal int mOriginalAttachCount;
public void rememberWindowAttachCount() {
mOriginalAttachCount = getWindowAttachCount();
}
public boolean sameWindow() {
return hasWindowFocus() && getWindowAttachCount() == mOriginalAttachCount;
}
}
}
HeaderViewListAdapter.java
package deal com.android.grid;
import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ListAdapter;
import android.widget.WrapperListAdapter;
import java.util.ArrayList;
public class HeaderViewListAdapter implements WrapperListAdapter, Filterable {
personal closing ListAdapter mAdapter;
// These two ArrayList are assumed to NOT be null.
// They’re certainly created when declared in ListView after which shared.
ArrayList
ArrayList
// Used as a placeholder in case the supplied information views are certainly null.
// At present solely utilized by some CTS exams, which can be eliminated.
static closing ArrayList
new ArrayList
boolean mAreAllFixedViewsSelectable;
personal closing boolean mIsFilterable;
public HeaderViewListAdapter(ArrayList
ArrayList
ListAdapter adapter) {
mAdapter = adapter;
mIsFilterable = adapter instanceof Filterable;
if (headerViewInfos == null) {
mHeaderViewInfos = EMPTY_INFO_LIST;
} else {
mHeaderViewInfos = headerViewInfos;
}
if (footerViewInfos == null) {
mFooterViewInfos = EMPTY_INFO_LIST;
} else {
mFooterViewInfos = footerViewInfos;
}
mAreAllFixedViewsSelectable =
areAllListInfosSelectable(mHeaderViewInfos)
&& areAllListInfosSelectable(mFooterViewInfos);
}
public int getHeadersCount() {
return mHeaderViewInfos.dimension();
}
public int getFootersCount() {
return mFooterViewInfos.dimension();
}
public boolean isEmpty()
personal boolean areAllListInfosSelectable(ArrayList
if (infos != null) {
for (StaggeredGridView.FixedViewInfo information : infos) {
if (!information.isSelectable) {
return false;
}
}
}
return true;
}
public boolean removeHeader(View v) {
for (int i = 0; i < mHeaderViewInfos.dimension(); i++) {
StaggeredGridView.FixedViewInfo information = mHeaderViewInfos.get(i);
if (information.view == v) {
mHeaderViewInfos.take away(i);
mAreAllFixedViewsSelectable =
areAllListInfosSelectable(mHeaderViewInfos)
&& areAllListInfosSelectable(mFooterViewInfos);
return true;
}
}
return false;
}
public boolean removeFooter(View v) {
for (int i = 0; i < mFooterViewInfos.dimension(); i++) {
StaggeredGridView.FixedViewInfo information = mFooterViewInfos.get(i);
if (information.view == v) {
mFooterViewInfos.take away(i);
mAreAllFixedViewsSelectable =
areAllListInfosSelectable(mHeaderViewInfos)
&& areAllListInfosSelectable(mFooterViewInfos);
return true;
}
}
return false;
}
public int getCount() {
if (mAdapter != null) {
return getFootersCount() + getHeadersCount() + mAdapter.getCount();
} else {
return getFootersCount() + getHeadersCount();
}
}
public boolean areAllItemsEnabled() {
if (mAdapter != null) {
return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
} else {
return true;
}
}
public boolean isEnabled(int place) {
// Header (unfavourable positions will throw an ArrayIndexOutOfBoundsException)
int numHeaders = getHeadersCount();
if (place < numHeaders) {
return mHeaderViewInfos.get(place).isSelectable;
}
// Adapter
closing int adjPosition = place - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.isEnabled(adjPosition);
}
}
// Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)
return mFooterViewInfos.get(adjPosition - adapterCount).isSelectable;
}
public Object getItem(int place) {
// Header (unfavourable positions will throw an ArrayIndexOutOfBoundsException)
int numHeaders = getHeadersCount();
if (place < numHeaders) {
return mHeaderViewInfos.get(place).knowledge;
}
// Adapter
closing int adjPosition = place - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getItem(adjPosition);
}
}
// Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)
return mFooterViewInfos.get(adjPosition - adapterCount).data;
}
public long getItemId(int position) {
int numHeaders = getHeadersCount();
if (mAdapter != null && position >= numHeaders) {
int adjPosition = place – numHeaders;
int adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getItemId(adjPosition);
}
}
return -1;
}
public boolean hasStableIds() {
if (mAdapter != null) {
return mAdapter.hasStableIds();
}
return false;
}
public View getView(int place, View convertView, ViewGroup dad or mum) {
// Header (unfavourable positions will throw an ArrayIndexOutOfBoundsException)
int numHeaders = getHeadersCount();
if (place < numHeaders) {
return mHeaderViewInfos.get(place).view;
}
// Adapter
closing int adjPosition = place - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getView(adjPosition, convertView, parent);
}
}
// Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)
return mFooterViewInfos.get(adjPosition - adapterCount).view;
}
public int getItemViewType(int position) {
int numHeaders = getHeadersCount();
if (mAdapter != null && position >= numHeaders) {
int adjPosition = place – numHeaders;
int adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getItemViewType(adjPosition);
}
}
return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
}
public int getViewTypeCount() {
if (mAdapter != null) {
return mAdapter.getViewTypeCount();
}
return 1;
}
public void registerDataSetObserver(DataSetObserver observer) {
if (mAdapter != null) {
mAdapter.registerDataSetObserver(observer);
}
}
public void unregisterDataSetObserver(DataSetObserver observer) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(observer);
}
}
public Filter getFilter() {
if (mIsFilterable) {
return ((Filterable) mAdapter).getFilter();
}
return null;
}
public ListAdapter getWrappedAdapter() {
return mAdapter;
}
}
StaggeredGridView.java
package deal com.android.grid;
import android.content material.Context;
import android.content material.res.Configuration;
import android.content material.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import java.util.Arrays;
public class StaggeredGridView extends ExtendableListView {
personal static closing String TAG = “StaggeredGridView”;
personal static closing boolean DBG = false;
personal static closing int DEFAULT_COLUMNS_PORTRAIT = 2;
personal static closing int DEFAULT_COLUMNS_LANDSCAPE = 3;
personal int mColumnCount;
personal int mItemMargin;
personal int mColumnWidth;
personal boolean mNeedSync;
personal int mColumnCountPortrait = DEFAULT_COLUMNS_PORTRAIT;
personal int mColumnCountLandscape = DEFAULT_COLUMNS_LANDSCAPE;
personal SparseArray
personal int mGridPaddingLeft;
personal int mGridPaddingRight;
personal int mGridPaddingTop;
personal int mGridPaddingBottom;
static class GridItemRecord implements Parcelable {
int column;
double heightRatio;
boolean isHeaderFooter;
GridItemRecord() { }
personal GridItemRecord(Parcel in) {
column = in.readInt();
heightRatio = in.readDouble();
isHeaderFooter = in.readByte() == 1;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(column);
out.writeDouble(heightRatio);
out.writeByte((byte) (isHeaderFooter ? 1 : 0));
}
@Override
public String toString() {
return “GridItemRecord.ListSavedState{”
+ Integer.toHexString(System.identityHashCode(this))
+ ” column:” + column
+ ” heightRatio:” + heightRatio
+ ” isHeaderFooter:” + isHeaderFooter
+ “}”;
}
public static closing Parcelable.Creator
= new Parcelable.Creator
public GridItemRecord createFromParcel(Parcel in) {
return new GridItemRecord(in);
}
public GridItemRecord[] newArray(int dimension) {
return new GridItemRecord[size];
}
};
}
personal int[] mColumnTops;
personal int[] mColumnBottoms;
personal int[] mColumnLefts;
personal int mDistanceToTop;
public StaggeredGridView(closing Context context) {
this(context, null);
}
public StaggeredGridView(closing Context context, closing AttributeSet attrs) {
this(context, attrs, 0);
}
public StaggeredGridView(closing Context context, closing AttributeSet attrs, closing int defStyle) {
tremendous(context, attrs, defStyle);
if (attrs != null) {
// get the variety of columns in portrait and panorama
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.StaggeredGridView, defStyle, 0);
mColumnCount = typedArray.getInteger(
R.styleable.StaggeredGridView_column_count, 0);
if (mColumnCount > 0) {
mColumnCountPortrait = mColumnCount;
mColumnCountLandscape = mColumnCount;
}
else {
mColumnCountPortrait = typedArray.getInteger(
R.styleable.StaggeredGridView_column_count_portrait,
DEFAULT_COLUMNS_PORTRAIT);
mColumnCountLandscape = typedArray.getInteger(
R.styleable.StaggeredGridView_column_count_landscape,
DEFAULT_COLUMNS_LANDSCAPE);
}
mItemMargin = typedArray.getDimensionPixelSize(
R.styleable.StaggeredGridView_item_margin, 0);
mGridPaddingLeft = typedArray.getDimensionPixelSize(
R.styleable.StaggeredGridView_grid_paddingLeft, 0);
mGridPaddingRight = typedArray.getDimensionPixelSize(
R.styleable.StaggeredGridView_grid_paddingRight, 0);
mGridPaddingTop = typedArray.getDimensionPixelSize(
R.styleable.StaggeredGridView_grid_paddingTop, 0);
mGridPaddingBottom = typedArray.getDimensionPixelSize(
R.styleable.StaggeredGridView_grid_paddingBottom, 0);
typedArray.recycle();
}
mColumnCount = 0; // decided onMeasure
// Creating these empty arrays to keep away from saving null states
mColumnTops = new int[0];
mColumnBottoms = new int[0];
mColumnLefts = new int[0];
mPositionData = new SparseArray
}
// //////////////////////////////////////////////////////////////////////////////////////////
// PROPERTIES
//
// Grid padding is utilized to the record merchandise rows however not the header and footer
public int getRowPaddingLeft() {
return getListPaddingLeft() + mGridPaddingLeft;
}
public int getRowPaddingRight() {
return getListPaddingRight() + mGridPaddingRight;
}
public int getRowPaddingTop() {
return getListPaddingTop() + mGridPaddingTop;
}
public int getRowPaddingBottom() {
return getListPaddingBottom() + mGridPaddingBottom;
}
public void setGridPadding(int left, int prime, int proper, int backside) {
mGridPaddingLeft = left;
mGridPaddingTop = prime;
mGridPaddingRight = proper;
mGridPaddingBottom = backside;
}
public void setColumnCountPortrait(int columnCountPortrait) {
mColumnCountPortrait = columnCountPortrait;
onSizeChanged(getWidth(), getHeight());
requestLayoutChildren();
}
public void setColumnCountLandscape(int columnCountLandscape) {
mColumnCountLandscape = columnCountLandscape;
onSizeChanged(getWidth(), getHeight());
requestLayoutChildren();
}
public void setColumnCount(int columnCount) {
mColumnCountPortrait = columnCount;
mColumnCountLandscape = columnCount;
// mColumnCount set onSizeChanged();
onSizeChanged(getWidth(), getHeight());
requestLayoutChildren();
}
// //////////////////////////////////////////////////////////////////////////////////////////
// MEASUREMENT
//
personal boolean isLandscape() {
return getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
}
@Override
protected void onMeasure(closing int widthMeasureSpec, closing int heightMeasureSpec) {
tremendous.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mColumnCount <= 0) {
boolean isLandscape = isLandscape();
mColumnCount = isLandscape ? mColumnCountLandscape : mColumnCountPortrait;
}
// our column width is the width of the listview
// minus it's padding
// minus the total items margin
// divided by the number of columns
mColumnWidth = calculateColumnWidth(getMeasuredWidth());
if (mColumnTops == null || mColumnTops.length != mColumnCount) {
mColumnTops = new int[mColumnCount];
initColumnTops();
}
if (mColumnBottoms == null || mColumnBottoms.length != mColumnCount) {
mColumnBottoms = new int[mColumnCount];
initColumnBottoms();
}
if (mColumnLefts == null || mColumnLefts.length != mColumnCount) {
mColumnLefts = new int[mColumnCount];
initColumnLefts();
}
}
@Override
protected void onMeasureChild(final View child, final LayoutParams layoutParams) {
final int viewType = layoutParams.viewType;
final int position = layoutParams.position;
if (viewType == ITEM_VIEW_TYPE_HEADER_OR_FOOTER ||
viewType == ITEM_VIEW_TYPE_IGNORE) {
// for headers and weird ignored views
super.onMeasureChild(child, layoutParams);
}
else {
if (DBG) Log.d(TAG, "onMeasureChild BEFORE position:" + position +
" h:" + getMeasuredHeight());
// measure it to the width of our column.
int childWidthSpec = MeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY);
int childHeightSpec;
if (layoutParams.height > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(layoutParams.top, MeasureSpec.EXACTLY);
}
else {
childHeightSpec = MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.UNSPECIFIED);
}
little one.measure(childWidthSpec, childHeightSpec);
}
closing int childHeight = getChildHeight(little one);
setPositionHeightRatio(place, childHeight);
if (DBG) Log.d(TAG, “onMeasureChild AFTER position:” + place +
” h:” + childHeight);
}
public int getColumnWidth() {
return mColumnWidth;
}
public void resetToTop() {
if (mColumnCount > 0) {
if (mColumnTops == null) {
mColumnTops = new int[mColumnCount];
}
if (mColumnBottoms == null) {
mColumnBottoms = new int[mColumnCount];
}
initColumnTopsAndBottoms();
mPositionData.clear();
mNeedSync = false;
mDistanceToTop = 0;
setSelection(0);
}
}
// //////////////////////////////////////////////////////////////////////////////////////////
// POSITIONING
//
@Override
protected void onChildCreated(closing int place, closing boolean flowDown) {
tremendous.onChildCreated(place, flowDown);
if (!isHeaderOrFooter(place)) {
// will we have already got a column for this place?
closing int column = getChildColumn(place, flowDown);
setPositionColumn(place, column);
if (DBG) Log.d(TAG, “onChildCreated position:” + place +
” is in column:” + column);
}
else {
setPositionIsHeaderFooter(place);
}
}
personal void requestLayoutChildren() {
closing int rely = getChildCount();
for (int i = 0; i < rely; i++) {
closing View v = getChildAt(i);
if (v != null) v.requestLayout();
}
}
@Override
protected void layoutChildren() {
preLayoutChildren();
tremendous.layoutChildren();
}
personal void preLayoutChildren() {
// on a significant re-layout reset for our subsequent structure go
if (!mNeedSync) {
Arrays.fill(mColumnBottoms, 0);
}
else {
mNeedSync = false;
}
// copy the tops into the underside
// since we'll redo a structure go that can draw down from
// the highest
System.arraycopy(mColumnTops, 0, mColumnBottoms, 0, mColumnCount);
}
// NOTE : Views will both be structure out through onLayoutChild
// OR
// Views will likely be offset if they're lively however offscreen in order that we will recycle!
// Each onLayoutChild() and onOffsetChild are referred to as after we measure our view
// see ExtensibleListView.setupChild();
@Override
protected void onLayoutChild(closing View little one,
closing int place,
closing boolean flowDown,
closing int childrenLeft, closing int childTop,
closing int childRight, closing int childBottom) {
if (isHeaderOrFooter(place)) {
layoutGridHeaderFooter(little one, place, flowDown, childrenLeft, childTop, childRight, childBottom);
}
else {
layoutGridChild(little one, place, flowDown, childrenLeft, childRight);
}
}
personal void layoutGridHeaderFooter(closing View little one, closing int place, closing boolean flowDown, closing int childrenLeft, closing int childTop, closing int childRight, closing int childBottom) {
// offset the highest and backside of all our columns
// if it is the footer we wish it under the bottom little one backside
int gridChildTop;
int gridChildBottom;
if (flowDown) {
gridChildTop = getLowestPositionedBottom();
gridChildBottom = gridChildTop + getChildHeight(little one);
}
else {
gridChildBottom = getHighestPositionedTop();
gridChildTop = gridChildBottom - getChildHeight(little one);
}
for (int i = 0; i < mColumnCount; i++) {
updateColumnTopIfNeeded(i, gridChildTop);
updateColumnBottomIfNeeded(i, gridChildBottom);
}
tremendous.onLayoutChild(little one, place, flowDown,
childrenLeft, gridChildTop, childRight, gridChildBottom);
}
personal void layoutGridChild(closing View little one, closing int place,
closing boolean flowDown,
closing int childrenLeft, closing int childRight) {
// stash the underside and the highest if it is greater positioned
int column = getPositionColumn(place);
int gridChildTop;
int gridChildBottom;
int childTopMargin = getChildTopMargin(place);
int childBottomMargin = getChildBottomMargin();
int verticalMargins = childTopMargin + childBottomMargin;
if (flowDown) {
gridChildTop = mColumnBottoms[column]; // the following gadgets prime is the final gadgets backside
gridChildBottom = gridChildTop + (getChildHeight(little one) + verticalMargins);
}
else {
gridChildBottom = mColumnTops[column]; // the underside of the following column up is our prime
gridChildTop = gridChildBottom - (getChildHeight(little one) + verticalMargins);
}
if (DBG) Log.d(TAG, "onLayoutChild position:" + place +
" column:" + column +
" gridChildTop:" + gridChildTop +
" gridChildBottom:" + gridChildBottom);
// we additionally know the column of this view so let's stash it within the
// view's structure params
GridLayoutParams layoutParams = (GridLayoutParams) little one.getLayoutParams();
layoutParams.column = column;
updateColumnBottomIfNeeded(column, gridChildBottom);
updateColumnTopIfNeeded(column, gridChildTop);
// subtract the margins earlier than structure
gridChildTop += childTopMargin;
gridChildBottom -= childBottomMargin;
little one.structure(childrenLeft, gridChildTop, childRight, gridChildBottom);
}
@Override
protected void onOffsetChild(closing View little one, closing int place,
closing boolean flowDown, closing int childrenLeft, closing int childTop) {
// if the kid is recycled and is simply offset
// we nonetheless wish to add its deets into our retailer
if (isHeaderOrFooter(place)) {
offsetGridHeaderFooter(little one, place, flowDown, childrenLeft, childTop);
}
else {
offsetGridChild(little one, place, flowDown, childrenLeft, childTop);
}
}
personal void offsetGridHeaderFooter(closing View little one, closing int place, closing boolean flowDown, closing int childrenLeft, closing int childTop) {
// offset the highest and backside of all our columns
// if it is the footer we wish it under the bottom little one backside
int gridChildTop;
int gridChildBottom;
if (flowDown) {
gridChildTop = getLowestPositionedBottom();
gridChildBottom = gridChildTop + getChildHeight(little one);
}
else {
gridChildBottom = getHighestPositionedTop();
gridChildTop = gridChildBottom - getChildHeight(little one);
}
for (int i = 0; i < mColumnCount; i++) {
updateColumnTopIfNeeded(i, gridChildTop);
updateColumnBottomIfNeeded(i, gridChildBottom);
}
tremendous.onOffsetChild(little one, place, flowDown, childrenLeft, gridChildTop);
}
personal void offsetGridChild(closing View little one, closing int place, closing boolean flowDown, closing int childrenLeft, closing int childTop) {
// stash the underside and the highest if it is greater positioned
int column = getPositionColumn(place);
int gridChildTop;
int gridChildBottom;
int childTopMargin = getChildTopMargin(place);
int childBottomMargin = getChildBottomMargin();
int verticalMargins = childTopMargin + childBottomMargin;
if (flowDown) {
gridChildTop = mColumnBottoms[column]; // the following gadgets prime is the final gadgets backside
gridChildBottom = gridChildTop + (getChildHeight(little one) + verticalMargins);
}
else {
gridChildBottom = mColumnTops[column]; // the underside of the following column up is our prime
gridChildTop = gridChildBottom - (getChildHeight(little one) + verticalMargins);
}
if (DBG) Log.d(TAG, "onOffsetChild position:" + place +
" column:" + column +
" childTop:" + childTop +
" gridChildTop:" + gridChildTop +
" gridChildBottom:" + gridChildBottom);
// we additionally know the column of this view so let's stash it within the
// view's structure params
GridLayoutParams layoutParams = (GridLayoutParams) little one.getLayoutParams();
layoutParams.column = column;
updateColumnBottomIfNeeded(column, gridChildBottom);
updateColumnTopIfNeeded(column, gridChildTop);
tremendous.onOffsetChild(little one, place, flowDown, childrenLeft, gridChildTop + childTopMargin);
}
personal int getChildHeight(closing View little one) {
return little one.getMeasuredHeight();
}
personal int getChildTopMargin(closing int place) {
boolean isFirstRow = place < (getHeaderViewsCount() + mColumnCount);
return isFirstRow ? mItemMargin : 0;
}
personal int getChildBottomMargin() {
return mItemMargin;
}
@Override
protected LayoutParams generateChildLayoutParams(closing View little one) {
GridLayoutParams layoutParams = null;
closing ViewGroup.LayoutParams childParams = little one.getLayoutParams();
if (childParams != null) {
if (childParams instanceof GridLayoutParams) {
layoutParams = (GridLayoutParams) childParams;
}
else {
layoutParams = new GridLayoutParams(childParams);
}
}
if (layoutParams == null) {
layoutParams = new GridLayoutParams(
mColumnWidth, ViewGroup.LayoutParams.WRAP_CONTENT);
}
return layoutParams;
}
personal void updateColumnTopIfNeeded(int column, int childTop) {
if (childTop < mColumnTops[column]) {
mColumnTops[column] = childTop;
}
}
private void updateColumnBottomIfNeeded(int column, int childBottom) {
if (childBottom > mColumnBottoms[column]) {
mColumnBottoms[column] = childBottom;
}
}
@Override
protected int getChildLeft(closing int place) {
if (isHeaderOrFooter(place)) {
return tremendous.getChildLeft(place);
}
else {
closing int column = getPositionColumn(place);
return mColumnLefts[column];
}
}
@Override
protected int getChildTop(closing int place) {
if (isHeaderOrFooter(place)) {
return tremendous.getChildTop(place);
}
else {
closing int column = getPositionColumn(place);
if (column == -1) {
return getHighestPositionedBottom();
}
return mColumnBottoms[column];
}
}
@Override
protected int getNextChildDownsTop(closing int place) {
if (isHeaderOrFooter(place)) {
return tremendous.getNextChildDownsTop(place);
}
else {
return getHighestPositionedBottom();
}
}
@Override
protected int getChildBottom(closing int place) {
if (isHeaderOrFooter(place)) {
return tremendous.getChildBottom(place);
}
else {
closing int column = getPositionColumn(place);
if (column == -1) {
return getLowestPositionedTop();
}
return mColumnTops[column];
}
}
@Override
protected int getNextChildUpsBottom(closing int place) {
if (isHeaderOrFooter(place)) {
return tremendous.getNextChildUpsBottom(place);
}
else {
return getLowestPositionedTop();
}
}
@Override
protected int getLastChildBottom() {
closing int lastPosition = mFirstPosition + (getChildCount() – 1);
if (isHeaderOrFooter(lastPosition)) {
return tremendous.getLastChildBottom();
}
return getHighestPositionedBottom();
}
@Override
protected int getFirstChildTop() {
if (isHeaderOrFooter(mFirstPosition)) {
return tremendous.getFirstChildTop();
}
return getLowestPositionedTop();
}
@Override
protected int getHighestChildTop() {
if (isHeaderOrFooter(mFirstPosition)) {
return tremendous.getHighestChildTop();
}
return getHighestPositionedTop();
}
@Override
protected int getLowestChildBottom() {
closing int lastPosition = mFirstPosition + (getChildCount() – 1);
if (isHeaderOrFooter(lastPosition)) {
return tremendous.getLowestChildBottom();
}
return getLowestPositionedBottom();
}
@Override
protected void offsetChildrenTopAndBottom(closing int offset) {
tremendous.offsetChildrenTopAndBottom(offset);
offsetAllColumnsTopAndBottom(offset);
offsetDistanceToTop(offset);
}
protected void offsetChildrenTopAndBottom(closing int offset, closing int column) {
if (DBG) Log.d(TAG, “offsetChildrenTopAndBottom: ” + offset + ” column:” + column);
closing int rely = getChildCount();
for (int i = 0; i < rely; i++) {
closing View v = getChildAt(i);
if (v != null &&
v.getLayoutParams() != null &&
v.getLayoutParams() instanceof GridLayoutParams) {
GridLayoutParams lp = (GridLayoutParams) v.getLayoutParams();
if (lp.column == column) {
v.offsetTopAndBottom(offset);
}
}
}
offsetColumnTopAndBottom(offset, column);
}
personal void offsetDistanceToTop(closing int offset) {
mDistanceToTop += offset;
if (DBG) Log.d(TAG, "offset mDistanceToTop:" + mDistanceToTop);
}
public int getDistanceToTop() {
return mDistanceToTop;
}
personal void offsetAllColumnsTopAndBottom(closing int offset) {
if (offset != 0) {
for (int i = 0; i < mColumnCount; i++) {
offsetColumnTopAndBottom(offset, i);
}
}
}
personal void offsetColumnTopAndBottom(closing int offset, closing int column) {
if (offset != 0) {
mColumnTops[column] += offset;
mColumnBottoms[column] += offset;
}
}
@Override
protected void adjustViewsAfterFillGap(closing boolean down) {
tremendous.adjustViewsAfterFillGap(down);
// repair vertical gaps when hitting the highest after a rotate
// solely when scrolling again up!
if (!down) {
alignTops();
}
}
personal void alignTops() {
if (mFirstPosition == getHeaderViewsCount()) {
// we're exhibiting all of the views earlier than the header views
int[] nonHeaderTops = getHighestNonHeaderTops();
// we should always now have our non header tops
// align them
boolean isAligned = true;
int highestColumn = -1;
int highestTop = Integer.MAX_VALUE;
for (int i = 0; i < nonHeaderTops.length; i++) {
// are they all aligned
if (isAligned && i > 0 && nonHeaderTops[i] != highestTop) {
isAligned = false; // not all of the tops are aligned
}
// what’s the best
if (nonHeaderTops[i] < highestTop) {
highestTop = nonHeaderTops[i];
highestColumn = i;
}
}
// skip the remainder.
if (isAligned) return;
// we have the best column - lets align the others
for (int i = 0; i < nonHeaderTops.length; i++) {
if (i != highestColumn) {
// there's a gap in this column
int offset = highestTop - nonHeaderTops[i];
offsetChildrenTopAndBottom(offset, i);
}
}
invalidate();
}
}
private int[] getHighestNonHeaderTops() {
int[] nonHeaderTops = new int[mColumnCount];
int childCount = getChildCount();
if (childCount > 0) {
for (int i = 0; i < childCount; i++) {
View little one = getChildAt(i);
if (little one != null &&
little one.getLayoutParams() != null &&
little one.getLayoutParams() instanceof GridLayoutParams) {
// is that this kid's prime the best non
GridLayoutParams lp = (GridLayoutParams) little one.getLayoutParams();
// is it a toddler that is not a header
if (lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER &&
little one.getTop() < nonHeaderTops[lp.column]) {
nonHeaderTops[lp.column] = little one.getTop();
}
}
}
}
return nonHeaderTops;
}
@Override
protected void onChildrenDetached(closing int begin, closing int rely) {
tremendous.onChildrenDetached(begin, rely);
// undergo our remaining views and sync the highest and backside stash.
// Restore the highest and backside column boundaries from the views we nonetheless have
Arrays.fill(mColumnTops, Integer.MAX_VALUE);
Arrays.fill(mColumnBottoms, 0);
for (int i = 0; i < getChildCount(); i++) {
closing View little one = getChildAt(i);
if (little one != null) {
closing LayoutParams childParams = (LayoutParams) little one.getLayoutParams();
if (childParams.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER &&
childParams instanceof GridLayoutParams) {
GridLayoutParams layoutParams = (GridLayoutParams) childParams;
int column = layoutParams.column;
int place = layoutParams.place;
closing int childTop = little one.getTop();
if (childTop < mColumnTops[column]) {
mColumnTops[column] = childTop - getChildTopMargin(position);
}
final int childBottom = child.getBottom();
if (childBottom > mColumnBottoms[column]) {
mColumnBottoms[column] = childBottom + getChildBottomMargin();
}
}
else {
// the header and footer right here
closing int childTop = little one.getTop();
closing int childBottom = little one.getBottom();
for (int col = 0; col < mColumnCount; col++) { if (childTop < mColumnTops[col]) { mColumnTops[col] = childTop; } if (childBottom > mColumnBottoms
mColumnBottoms
}
}
}
}
}
}
@Override
protected boolean hasSpaceUp() {
int finish = mClipToPadding ? getRowPaddingTop() : 0;
return getLowestPositionedTop() > finish;
}
// //////////////////////////////////////////////////////////////////////////////////////////
// SYNCING ACROSS ROTATION
//
@Override
protected void onSizeChanged(closing int w, closing int h, closing int oldw, closing int oldh) {
tremendous.onSizeChanged(w, h, oldw, oldh);
onSizeChanged(w, h);
}
@Override
protected void onSizeChanged(int w, int h) {
tremendous.onSizeChanged(w, h);
boolean isLandscape = isLandscape();
int newColumnCount = isLandscape ? mColumnCountLandscape : mColumnCountPortrait;
if (mColumnCount != newColumnCount) {
mColumnCount = newColumnCount;
mColumnWidth = calculateColumnWidth(w);
mColumnTops = new int[mColumnCount];
mColumnBottoms = new int[mColumnCount];
mColumnLefts = new int[mColumnCount];
mDistanceToTop = 0;
// rebuild the columns
initColumnTopsAndBottoms();
initColumnLefts();
// if we now have knowledge
if (getCount() > 0 && mPositionData.dimension() > 0) {
onColumnSync();
}
requestLayout();
}
}
personal int calculateColumnWidth(closing int gridWidth) {
closing int listPadding = getRowPaddingLeft() + getRowPaddingRight();
return (gridWidth – listPadding – mItemMargin * (mColumnCount + 1)) / mColumnCount;
}
personal int calculateColumnLeft(closing int colIndex) {
return getRowPaddingLeft() + mItemMargin + ((mItemMargin + mColumnWidth) * colIndex);
}
/***
* Our mColumnTops and mColumnBottoms must be re-built as much as the
* mSyncPosition – the next structure request will then
* structure the that place after which fillUp and fillDown appropriately.
*/
personal void onColumnSync() {
// re-calc tops for brand new column rely!
int syncPosition = Math.min(mSyncPosition, getCount() – 1);
SparseArray
for (int pos = 0; pos < syncPosition; pos++) {
// test for weirdness
closing GridItemRecord rec = mPositionData.get(pos);
if (rec == null) break;
Log.d(TAG, "onColumnSync:" + pos + " ratio:" + rec.heightRatio);
positionHeightRatios.append(pos, rec.heightRatio);
}
mPositionData.clear();
// re-calc our relative place whereas on the similar time
// rebuilding our GridItemRecord assortment
if (DBG) Log.d(TAG, "onColumnSync column width:" + mColumnWidth);
for (int pos = 0; pos < syncPosition; pos++) {
//Examine for weirdness once more
closing Double heightRatio = positionHeightRatios.get(pos);
if(heightRatio == null){
break;
}
closing GridItemRecord rec = getOrCreateRecord(pos);
closing int top = (int) (mColumnWidth * heightRatio);
rec.heightRatio = heightRatio;
int prime;
int backside;
// test for headers
if (isHeaderOrFooter(pos)) {
// the following prime is the underside for that column
prime = getLowestPositionedBottom();
backside = prime + top;
for (int i = 0; i < mColumnCount; i++) {
mColumnTops[i] = prime;
mColumnBottoms[i] = backside;
}
}
else {
// what is the subsequent column down ?
closing int column = getHighestPositionedBottomColumn();
// the following prime is the underside for that column
prime = mColumnBottoms[column];
backside = prime + top + getChildTopMargin(pos) + getChildBottomMargin();
mColumnTops[column] = prime;
mColumnBottoms[column] = backside;
rec.column = column;
}
if (DBG) Log.d(TAG, "onColumnSync position:" + pos +
" top:" + prime +
" bottom:" + backside +
" height:" + top +
" heightRatio:" + heightRatio);
}
// our sync place will likely be displayed on this column
closing int syncColumn = getHighestPositionedBottomColumn();
setPositionColumn(syncPosition, syncColumn);
// we wish to offset from top of the sync place
// minus the offset
int syncToBottom = mColumnBottoms[syncColumn];
int offset = -syncToBottom + mSpecificTop;
// offset all columns by
offsetAllColumnsTopAndBottom(offset);
// sync the space to prime
mDistanceToTop = -syncToBottom;
// stash our bottoms in our tops - although these will likely be copied again to the bottoms
System.arraycopy(mColumnBottoms, 0, mColumnTops, 0, mColumnCount);
}
// //////////////////////////////////////////////////////////////////////////////////////////
// GridItemRecord UTILS
//
personal void setPositionColumn(closing int place, closing int column) {
GridItemRecord rec = getOrCreateRecord(place);
rec.column = column;
}
personal void setPositionHeightRatio(closing int place, closing int top) {
GridItemRecord rec = getOrCreateRecord(place);
rec.heightRatio = (double) top / (double) mColumnWidth;
if (DBG) Log.d(TAG, "position:" + place +
" width:" + mColumnWidth +
" height:" + top +
" heightRatio:" + rec.heightRatio);
}
personal void setPositionIsHeaderFooter(closing int place) {
GridItemRecord rec = getOrCreateRecord(place);
rec.isHeaderFooter = true;
}
personal GridItemRecord getOrCreateRecord(closing int place) {
GridItemRecord rec = mPositionData.get(place, null);
if (rec == null) {
rec = new GridItemRecord();
mPositionData.append(place, rec);
}
return rec;
}
personal int getPositionColumn(closing int place) {
GridItemRecord rec = mPositionData.get(place, null);
return rec != null ? rec.column : -1;
}
// //////////////////////////////////////////////////////////////////////////////////////////
// HELPERS
//
personal boolean isHeaderOrFooter(closing int place) {
closing int viewType = mAdapter.getItemViewType(place);
return viewType == ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
}
personal int getChildColumn(closing int place, closing boolean flowDown) {
// will we have already got a column for this little one place?
int column = getPositionColumn(place);
// we do not have the column or it now not matches in our grid
closing int columnCount = mColumnCount;
if (column < 0 || column >= columnCount) {
// if we’re taking place –
// get the best positioned (lowest worth)
// column backside
if (flowDown) {
column = getHighestPositionedBottomColumn();
}
else {
column = getLowestPositionedTopColumn();
}
}
return column;
}
personal void initColumnTopsAndBottoms() {
initColumnTops();
initColumnBottoms();
}
personal void initColumnTops() {
Arrays.fill(mColumnTops, getPaddingTop() + mGridPaddingTop);
}
personal void initColumnBottoms() {
Arrays.fill(mColumnBottoms, getPaddingTop() + mGridPaddingTop);
}
personal void initColumnLefts() {
for (int i = 0; i < mColumnCount; i++) {
mColumnLefts[i] = calculateColumnLeft(i);
}
}
// //////////////////////////////////////////////////////////////////////////////////////////
// BOTTOM
//
personal int getHighestPositionedBottom() {
closing int column = getHighestPositionedBottomColumn();
return mColumnBottoms[column];
}
personal int getHighestPositionedBottomColumn() {
int columnFound = 0;
int highestPositionedBottom = Integer.MAX_VALUE;
// the best positioned backside is the one with the bottom worth ?
for (int i = 0; i < mColumnCount; i++) {
int backside = mColumnBottoms[i];
if (backside < highestPositionedBottom) {
highestPositionedBottom = backside;
columnFound = i;
}
}
return columnFound;
}
personal int getLowestPositionedBottom() {
closing int column = getLowestPositionedBottomColumn();
return mColumnBottoms[column];
}
personal int getLowestPositionedBottomColumn() {
int columnFound = 0;
int lowestPositionedBottom = Integer.MIN_VALUE;
// the bottom positioned backside is the one with the best worth ?
for (int i = 0; i < mColumnCount; i++) {
int bottom = mColumnBottoms[i];
if (bottom > lowestPositionedBottom) {
lowestPositionedBottom = backside;
columnFound = i;
}
}
return columnFound;
}
// //////////////////////////////////////////////////////////////////////////////////////////
// TOP
//
personal int getLowestPositionedTop() {
closing int column = getLowestPositionedTopColumn();
return mColumnTops[column];
}
personal int getLowestPositionedTopColumn() {
int columnFound = 0;
// we’ll go backwards by way of because the proper most
// will doubtless be the bottom positioned High
int lowestPositionedTop = Integer.MIN_VALUE;
// the bottom positioned prime is the one with the best worth ?
for (int i = 0; i < mColumnCount; i++) {
int top = mColumnTops[i];
if (top > lowestPositionedTop) {
lowestPositionedTop = prime;
columnFound = i;
}
}
return columnFound;
}
personal int getHighestPositionedTop() {
closing int column = getHighestPositionedTopColumn();
return mColumnTops[column];
}
personal int getHighestPositionedTopColumn() {
int columnFound = 0;
int highestPositionedTop = Integer.MAX_VALUE;
// the best positioned prime is the one with the bottom worth ?
for (int i = 0; i < mColumnCount; i++) {
int prime = mColumnTops[i];
if (prime < highestPositionedTop) {
highestPositionedTop = top;
columnFound = i;
}
}
return columnFound;
}
// //////////////////////////////////////////////////////////////////////////////////////////
// LAYOUT PARAMS
//
/**
* Extended LayoutParams to column position and anything else we may been for the grid
*/
public static class GridLayoutParams extends LayoutParams {
// The column the view is displayed in
int column;
public GridLayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
enforceStaggeredLayout();
}
public GridLayoutParams(int w, int h) {
super(w, h);
enforceStaggeredLayout();
}
public GridLayoutParams(int w, int h, int viewType) {
super(w, h);
enforceStaggeredLayout();
}
public GridLayoutParams(ViewGroup.LayoutParams source) {
super(source);
enforceStaggeredLayout();
}
/**
* Here we're making sure that all grid view items
* are width MATCH_PARENT and height WRAP_CONTENT.
* That's what this grid is designed for
*/
private void enforceStaggeredLayout() {
if (width != MATCH_PARENT) {
width = MATCH_PARENT;
}
if (height == MATCH_PARENT) {
height = WRAP_CONTENT;
}
}
}
// //////////////////////////////////////////////////////////////////////////////////////////
// SAVED STATE
public static class GridListSavedState extends ListSavedState {
int columnCount;
int[] columnTops;
SparseArray positionData;
public GridListSavedState(Parcelable superState) {
super(superState);
}
/**
* Constructor called from {@link #CREATOR}
*/
public GridListSavedState(Parcel in) {
super(in);
columnCount = in.readInt();
columnTops = new int[columnCount >= 0 ? columnCount : 0];
in.readIntArray(columnTops);
positionData = in.readSparseArray(GridItemRecord.class.getClassLoader());
}
@Override
public void writeToParcel(Parcel out, int flags) {
tremendous.writeToParcel(out, flags);
out.writeInt(columnCount);
out.writeIntArray(columnTops);
out.writeSparseArray(positionData);
}
@Override
public String toString() {
return “StaggeredGridView.GridListSavedState{”
+ Integer.toHexString(System.identityHashCode(this)) + “}”;
}
public static closing Creator
= new Creator
public GridListSavedState createFromParcel(Parcel in) {
return new GridListSavedState(in);
}
public GridListSavedState[] newArray(int dimension) {
return new GridListSavedState[size];
}
};
}
@Override
public Parcelable onSaveInstanceState() {
ListSavedState listState = (ListSavedState) tremendous.onSaveInstanceState();
GridListSavedState ss = new GridListSavedState(listState.getSuperState());
// from the record state
ss.selectedId = listState.selectedId;
ss.firstId = listState.firstId;
ss.viewTop = listState.viewTop;
ss.place = listState.place;
ss.top = listState.top;
// our state
boolean haveChildren = getChildCount() > 0 && getCount() > 0;
if (haveChildren && mFirstPosition > 0) {
ss.columnCount = mColumnCount;
ss.columnTops = mColumnTops;
ss.positionData = mPositionData;
}
else {
ss.columnCount = mColumnCount >= 0 ? mColumnCount : 0;
ss.columnTops = new int[ss.columnCount];
ss.positionData = new SparseArray
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
GridListSavedState ss = (GridListSavedState) state;
mColumnCount = ss.columnCount;
mColumnTops = ss.columnTops;
mColumnBottoms = new int[mColumnCount];
mPositionData = ss.positionData;
mNeedSync = true;
tremendous.onRestoreInstanceState(ss);
}
}
DynamicHeightImageView.java
package deal com.android.grid.util;
import android.content material.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
public class DynamicHeightImageView extends ImageView {
personal double mHeightRatio;
public DynamicHeightImageView(Context context, AttributeSet attrs) {
tremendous(context, attrs);
}
public DynamicHeightImageView(Context context) {
tremendous(context);
}
public void setHeightRatio(double ratio) {
if (ratio != mHeightRatio) {
mHeightRatio = ratio;
requestLayout();
}
}
public double getHeightRatio() {
return mHeightRatio;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mHeightRatio > 0.0) {
// set the picture views dimension
int width = MeasureSpec.getSize(widthMeasureSpec);
int top = (int) (width * mHeightRatio);
setMeasuredDimension(width, top);
}
else {
tremendous.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
DynamicHeightTextView.java
package deal com.android.grid.util;
import android.content material.Context;
import android.util.AttributeSet;
import android.widget.TextView;
public class DynamicHeightTextView extends TextView {
personal double mHeightRatio;
public DynamicHeightTextView(Context context, AttributeSet attrs) {
tremendous(context, attrs);
}
public DynamicHeightTextView(Context context) {
tremendous(context);
}
public void setHeightRatio(double ratio) {
if (ratio != mHeightRatio) {
mHeightRatio = ratio;
requestLayout();
}
}
public double getHeightRatio() {
return mHeightRatio;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mHeightRatio > 0.0) {
// set the picture views dimension
int width = MeasureSpec.getSize(widthMeasureSpec);
int top = (int) (width * mHeightRatio);
setMeasuredDimension(width, top);
}
else {
tremendous.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
—–> Create Android Staggered Grid Challenge And Hyperlink Above Challenge AS Lib Challenge.
—> AndroidManifest.xml
In Values Folder
colours.xml
integers.xml
IN Structure Folder
activity_list_view.xml
activity_main.xml
activity_sgv_empy_view.xml
activity_sgv.xml
list_item_header_footer.xml
list_item_sample.xml
IN drawable Folder
list_item_selector.xml
ListViewActivity.java
package deal com.android.pattern;
import android.app.Exercise;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.*;
import java.util.Record;
public class ListViewActivity extends Exercise implements AdapterView.OnItemClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
tremendous.onCreate(savedInstanceState);
setContentView(R.structure.activity_list_view);
setTitle(“ListView”);
closing ListView listView = (ListView) findViewById(R.id.list_view);
LayoutInflater layoutInflater = getLayoutInflater();
View header = layoutInflater.inflate(R.structure.list_item_header_footer, null);
View footer = layoutInflater.inflate(R.structure.list_item_header_footer, null);
TextView txtHeaderTitle = (TextView) header.findViewById(R.id.txt_title);
TextView txtFooterTitle = (TextView) footer.findViewById(R.id.txt_title);
txtHeaderTitle.setText(“THE HEADER!”);
txtFooterTitle.setText(“THE FOOTER!”);
listView.addHeaderView(header);
listView.addFooterView(footer);
closing SampleAdapter adapter = new SampleAdapter(this, R.id.txt_line1);
listView.setAdapter(adapter);
listView.setOnItemClickListener(this);
closing Record
for (String knowledge : sampleData) {
adapter.add(knowledge);
}
}
@Override
public void onItemClick(AdapterView adapterView, View view, int place, lengthy id) {
Toast.makeText(this, “Item Clicked: ” + place, Toast.LENGTH_SHORT).present();
}
}
MainActivity.java
package deal com.android.pattern;
import android.app.Exercise;
import android.content material.Intent;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends Exercise implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
tremendous.onCreate(savedInstanceState);
setTitle(“SGV Sample”);
setContentView(R.structure.activity_main);
findViewById(R.id.btn_sgv).setOnClickListener(this);
findViewById(R.id.btn_sgv_fragment).setOnClickListener(this);
findViewById(R.id.btn_sgv_empty_view).setOnClickListener(this);
findViewById(R.id.btn_listview).setOnClickListener(this);
}
@Override
public void onClick(closing View v) {
if (v.getId() == R.id.btn_sgv) {
startActivity(new Intent(this, StaggeredGridActivity.class));
}
else if (v.getId() == R.id.btn_sgv_fragment) {
startActivity(new Intent(this, StaggeredGridActivityFragment.class));
}
else if (v.getId() == R.id.btn_sgv_empty_view) {
startActivity(new Intent(this, StaggeredGridEmptyViewActivity.class));
}
else if (v.getId() == R.id.btn_listview) {
startActivity(new Intent(this, ListViewActivity.class));
}
}
}
SampleAdapter.java
package deal com.android.pattern;
import java.util.ArrayList;
import java.util.Random;
import com.android.grid.util.DynamicHeightTextView;
import android.content material.Context;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Toast;
public class SampleAdapter extends ArrayAdapter
personal static closing String TAG = “SampleAdapter”;
static class ViewHolder {
DynamicHeightTextView txtLineOne;
Button btnGo;
}
personal closing LayoutInflater mLayoutInflater;
personal closing Random mRandom;
personal closing ArrayList
personal static closing SparseArray
public SampleAdapter(closing Context context, closing int textViewResourceId) {
tremendous(context, textViewResourceId);
mLayoutInflater = LayoutInflater.from(context);
mRandom = new Random();
mBackgroundColors = new ArrayList
mBackgroundColors.add(R.shade.orange);
mBackgroundColors.add(R.shade.inexperienced);
mBackgroundColors.add(R.shade.blue);
mBackgroundColors.add(R.shade.yellow);
mBackgroundColors.add(R.shade.gray);
}
@Override
public View getView(closing int place, View convertView, closing ViewGroup dad or mum) {
ViewHolder vh;
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.structure.list_item_sample, dad or mum, false);
vh = new ViewHolder();
vh.txtLineOne = (DynamicHeightTextView) convertView.findViewById(R.id.txt_line1);
vh.btnGo = (Button) convertView.findViewById(R.id.btn_go);
convertView.setTag(vh);
}
else {
vh = (ViewHolder) convertView.getTag();
}
double positionHeight = getPositionRatio(place);
int backgroundIndex = place >= mBackgroundColors.dimension() ?
place % mBackgroundColors.dimension() : place;
convertView.setBackgroundResource(mBackgroundColors.get(backgroundIndex));
Log.d(TAG, “getView position:” + place + ” h:” + positionHeight);
vh.txtLineOne.setHeightRatio(positionHeight);
vh.txtLineOne.setText(getItem(place) + place);
vh.btnGo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(closing View v) {
Toast.makeText(getContext(), “Button Clicked Position ” +
place, Toast.LENGTH_SHORT).present();
}
});
return convertView;
}
personal double getPositionRatio(closing int place) {
double ratio = sPositionHeightRatios.get(place, 0.0);
if (ratio == 0) {
ratio = getRandomHeightRatio();
sPositionHeightRatios.append(place, ratio);
Log.d(TAG, “getPositionRatio:” + place + ” ratio:” + ratio);
}
return ratio;
}
personal double getRandomHeightRatio() {
return (mRandom.nextDouble() / 2.0) + 1.0; // top will likely be 1.0 – 1.5 the width
}
}
SampleData.java
package deal com.android.pattern;
import java.util.ArrayList;
import java.util.Record;
public class SampleData {
public static closing int SAMPLE_DATA_ITEM_COUNT = 30;
public static ArrayList
closing ArrayList
for (int i = 0; i < SAMPLE_DATA_ITEM_COUNT; i++) { knowledge.add("SAMPLE #"); } return knowledge; } }
StaggeredGridActivity.java
package deal com.android.pattern;
import java.util.ArrayList;
import android.app.Exercise;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.grid.StaggeredGridView;
public class StaggeredGridActivity extends Exercise implements AbsListView.OnScrollListener, AbsListView.OnItemClickListener, AdapterView.OnItemLongClickListener {
personal static closing String TAG = “StaggeredGridActivity”;
public static closing String SAVED_DATA_KEY = “SAVED_DATA”;
personal StaggeredGridView mGridView;
personal boolean mHasRequestedMore;
personal SampleAdapter mAdapter;
personal ArrayList
@Override
protected void onCreate(Bundle savedInstanceState) {
tremendous.onCreate(savedInstanceState);
setContentView(R.structure.activity_sgv);
setTitle(“SGV”);
mGridView = (StaggeredGridView) findViewById(R.id.grid_view);
LayoutInflater layoutInflater = getLayoutInflater();
View header = layoutInflater.inflate(R.structure.list_item_header_footer, null);
View footer = layoutInflater.inflate(R.structure.list_item_header_footer, null);
TextView txtHeaderTitle = (TextView) header.findViewById(R.id.txt_title);
TextView txtFooterTitle = (TextView) footer.findViewById(R.id.txt_title);
txtHeaderTitle.setText(“THE HEADER!”);
txtFooterTitle.setText(“THE FOOTER!”);
mGridView.addHeaderView(header);
mGridView.addFooterView(footer);
mAdapter = new SampleAdapter(this, R.id.txt_line1);
// do we now have saved knowledge?
if (savedInstanceState != null) {
mData = savedInstanceState.getStringArrayList(SAVED_DATA_KEY);
}
if (mData == null) {
mData = SampleData.generateSampleData();
}
for (String knowledge : mData) {
mAdapter.add(knowledge);
}
mGridView.setAdapter(mAdapter);
mGridView.setOnScrollListener(this);
mGridView.setOnItemClickListener(this);
mGridView.setOnItemLongClickListener(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_sgv_dynamic, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem merchandise) {
change (merchandise.getItemId()) {
case R.id.col1:
mGridView.setColumnCount(1);
break;
case R.id.col2:
mGridView.setColumnCount(2);
break;
case R.id.col3:
mGridView.setColumnCount(3);
break;
}
return true;
}
@Override
protected void onSaveInstanceState(closing Bundle outState) {
tremendous.onSaveInstanceState(outState);
outState.putStringArrayList(SAVED_DATA_KEY, mData);
}
@Override
public void onScrollStateChanged(closing AbsListView view, closing int scrollState) {
Log.d(TAG, “onScrollStateChanged:” + scrollState);
}
@Override
public void onScroll(closing AbsListView view, closing int firstVisibleItem, closing int visibleItemCount, closing int totalItemCount) {
Log.d(TAG, “onScroll firstVisibleItem:” + firstVisibleItem +
” visibleItemCount:” + visibleItemCount +
” totalItemCount:” + totalItemCount);
// our dealing with
if (!mHasRequestedMore) {
int lastInScreen = firstVisibleItem + visibleItemCount;
if (lastInScreen >= totalItemCount) {
Log.d(TAG, “onScroll lastInScreen – so load more”);
mHasRequestedMore = true;
onLoadMoreItems();
}
}
}
personal void onLoadMoreItems() {
closing ArrayList
for (String knowledge : sampleData) {
mAdapter.add(knowledge);
}
// stash all the information in our backing retailer
mData.addAll(sampleData);
// notify the adapter that we will replace now
mAdapter.notifyDataSetChanged();
mHasRequestedMore = false;
}
@Override
public void onItemClick(AdapterView adapterView, View view, int place, lengthy id) {
Toast.makeText(this, “Item Clicked: ” + place, Toast.LENGTH_SHORT).present();
}
@Override
public boolean onItemLongClick(AdapterView dad or mum, View view, int place, lengthy id)
{
Toast.makeText(this, “Item Long Clicked: ” + place, Toast.LENGTH_SHORT).present();
return true;
}
}
StaggeredGridActivityFragment.java
package deal com.android.pattern;
import android.os.Bundle;
import android.help.v4.app.Fragment;
import android.help.v4.app.FragmentActivity;
import android.help.v4.app.FragmentManager;
import android.util.Log;
import android.view.*;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.grid.StaggeredGridView;
import java.util.ArrayList;
public class StaggeredGridActivityFragment extends FragmentActivity {
personal static closing String TAG = “StaggeredGridActivityFragment”;
@Override
protected void onCreate(Bundle savedInstanceState) {
tremendous.onCreate(savedInstanceState);
setTitle(“SGV”);
closing FragmentManager fm = getSupportFragmentManager();
// Create the record fragment and add it as our sole content material.
if (fm.findFragmentById(android.R.id.content material) == null) {
closing StaggeredGridFragment fragment = new StaggeredGridFragment();
fm.beginTransaction().add(android.R.id.content material, fragment).commit();
}
}
personal class StaggeredGridFragment extends Fragment implements
AbsListView.OnScrollListener, AbsListView.OnItemClickListener {
personal StaggeredGridView mGridView;
personal boolean mHasRequestedMore;
personal SampleAdapter mAdapter;
personal ArrayList
@Override
public void onCreate(closing Bundle savedInstanceState) {
tremendous.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public View onCreateView(closing LayoutInflater inflater, closing ViewGroup container, closing Bundle savedInstanceState) {
return inflater.inflate(R.structure.activity_sgv, container, false);
}
@Override
public void onActivityCreated(closing Bundle savedInstanceState) {
tremendous.onActivityCreated(savedInstanceState);
mGridView = (StaggeredGridView) getView().findViewById(R.id.grid_view);
if (savedInstanceState == null) {
closing LayoutInflater layoutInflater = getActivity().getLayoutInflater();
View header = layoutInflater.inflate(R.structure.list_item_header_footer, null);
View footer = layoutInflater.inflate(R.structure.list_item_header_footer, null);
TextView txtHeaderTitle = (TextView) header.findViewById(R.id.txt_title);
TextView txtFooterTitle = (TextView) footer.findViewById(R.id.txt_title);
txtHeaderTitle.setText(“THE HEADER!”);
txtFooterTitle.setText(“THE FOOTER!”);
mGridView.addHeaderView(header);
mGridView.addFooterView(footer);
}
if (mAdapter == null) {
mAdapter = new SampleAdapter(getActivity(), R.id.txt_line1);
}
if (mData == null) {
mData = SampleData.generateSampleData();
}
for (String knowledge : mData) {
mAdapter.add(knowledge);
}
mGridView.setAdapter(mAdapter);
mGridView.setOnScrollListener(this);
mGridView.setOnItemClickListener(this);
}
@Override
public void onScrollStateChanged(closing AbsListView view, closing int scrollState) {
Log.d(TAG, “onScrollStateChanged:” + scrollState);
}
@Override
public void onScroll(closing AbsListView view, closing int firstVisibleItem, closing int visibleItemCount, closing int totalItemCount) {
Log.d(TAG, “onScroll firstVisibleItem:” + firstVisibleItem +
” visibleItemCount:” + visibleItemCount +
” totalItemCount:” + totalItemCount);
// our dealing with
if (!mHasRequestedMore) {
int lastInScreen = firstVisibleItem + visibleItemCount;
if (lastInScreen >= totalItemCount) {
Log.d(TAG, “onScroll lastInScreen – so load more”);
mHasRequestedMore = true;
onLoadMoreItems();
}
}
}
personal void onLoadMoreItems() {
closing ArrayList
for (String knowledge : sampleData) {
mAdapter.add(knowledge);
}
// stash all the information in our backing retailer
mData.addAll(sampleData);
// notify the adapter that we will replace now
mAdapter.notifyDataSetChanged();
mHasRequestedMore = false;
}
@Override
public void onItemClick(AdapterView adapterView, View view, int place, lengthy id) {
Toast.makeText(getActivity(), “Item Clicked: ” + place, Toast.LENGTH_SHORT).present();
}
}
}
StaggeredGridEmptyViewActivity.java
package deal com.android.pattern;
import android.app.Exercise;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.grid.StaggeredGridView;
import java.util.ArrayList;
public class StaggeredGridEmptyViewActivity extends Exercise implements AbsListView.OnItemClickListener {
public static closing String SAVED_DATA_KEY = “SAVED_DATA”;
personal static closing int FETCH_DATA_TASK_DURATION = 2000;
personal StaggeredGridView mGridView;
personal SampleAdapter mAdapter;
personal ArrayList
@Override
protected void onCreate(Bundle savedInstanceState) {
tremendous.onCreate(savedInstanceState);
setContentView(R.structure.activity_sgv_empy_view);
setTitle(“SGV”);
mGridView = (StaggeredGridView) findViewById(R.id.grid_view);
LayoutInflater layoutInflater = getLayoutInflater();
View header = layoutInflater.inflate(R.structure.list_item_header_footer, null);
View footer = layoutInflater.inflate(R.structure.list_item_header_footer, null);
TextView txtHeaderTitle = (TextView) header.findViewById(R.id.txt_title);
TextView txtFooterTitle = (TextView) footer.findViewById(R.id.txt_title);
txtHeaderTitle.setText(“THE HEADER!”);
txtFooterTitle.setText(“THE FOOTER!”);
mGridView.addHeaderView(header);
mGridView.addFooterView(footer);
mGridView.setEmptyView(findViewById(android.R.id.empty));
mAdapter = new SampleAdapter(this, R.id.txt_line1);
// do we now have saved knowledge?
if (savedInstanceState != null) {
mData = savedInstanceState.getStringArrayList(SAVED_DATA_KEY);
fillAdapter();
}
if (mData == null) {
mData = SampleData.generateSampleData();
}
mGridView.setAdapter(mAdapter);
mGridView.setOnItemClickListener(this);
fetchData();
}
personal void fillAdapter() {
for (String knowledge : mData) {
mAdapter.add(knowledge);
}
}
personal void fetchData() {
new AsyncTask
@Override
protected Void doInBackground(Void… params) {
SystemClock.sleep(FETCH_DATA_TASK_DURATION);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
fillAdapter();
}
}.execute();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_sgv_empty_view, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem merchandise) {
mAdapter.clear();
fetchData();
return true;
}
@Override
public void onItemClick(AdapterView adapterView, View view, int place, lengthy id) {
Toast.makeText(this, “Item Clicked: ” + place, Toast.LENGTH_SHORT).present();
}
@Override
protected void onSaveInstanceState(closing Bundle outState) {
tremendous.onSaveInstanceState(outState);
outState.putStringArrayList(SAVED_DATA_KEY, mData);
}
}