views) {
* associated to the View.
*
* @param listener The recycler listener to be notified of views set aside
- * in the recycler.
- *
- * @see android.widget.PLA_AbsListView.RecycleBin
+ * in the recycler.
* @see android.widget.AbsListView.RecyclerListener
*/
public void setRecyclerListener(RecyclerListener listener) {
@@ -2801,7 +2854,6 @@ public LayoutParams(ViewGroup.LayoutParams source) {
* inside the RecycleBin's scrap heap. This listener is used to free resources
* associated to Views placed in the RecycleBin.
*
- * @see android.widget.PLA_AbsListView.RecycleBin
* @see android.widget.AbsListView#setRecyclerListener(android.widget.AbsListView.RecyclerListener)
*/
public static interface RecyclerListener {
@@ -2926,7 +2978,7 @@ void fillActiveViews(int childCount, int firstActivePosition) {
final View[] activeViews = mActiveViews;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
- PLA_AbsListView.LayoutParams lp = (PLA_AbsListView.LayoutParams) child.getLayoutParams();
+ 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.
@@ -3002,7 +3054,7 @@ View getScrapView(int position) {
void addScrapView(View scrap) {
DebugUtil.i("addToScrap");
- PLA_AbsListView.LayoutParams lp = (PLA_AbsListView.LayoutParams) scrap.getLayoutParams();
+ LayoutParams lp = (LayoutParams) scrap.getLayoutParams();
if (lp == null) {
return;
}
@@ -3046,7 +3098,7 @@ void scrapActiveViews() {
for (int i = count - 1; i >= 0; i--) {
final View victim = activeViews[i];
if (victim != null) {
- int whichScrap = ((PLA_AbsListView.LayoutParams) victim.getLayoutParams()).viewType;
+ int whichScrap = ((LayoutParams) victim.getLayoutParams()).viewType;
activeViews[i] = null;
@@ -3228,4 +3280,96 @@ protected int getScrollChildBottom() {
return 0;
return getChildAt(count - 1).getBottom();
}
+
+
+ static class SavedState {
+ long firstId;
+ int viewTop;
+ int position;
+ int height;
+ int childCount;
+ int[] viewTops;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+
+ Bundle ss = new Bundle();
+ ss.putParcelable("instanceState", super.onSaveInstanceState());
+
+ if (mPendingSync != null) {
+ // Just keep what we last restored.
+ ss.putLong("firstId", mPendingSync.firstId);
+ ss.putInt("viewTop", mPendingSync.viewTop);
+ ss.putIntArray("viewTops", mPendingSync.viewTops);
+ ss.putInt("position", mPendingSync.position);
+ ss.putInt("height", mPendingSync.height);
+ ss.putInt("childCount", mPendingSync.childCount);
+ return ss;
+ }
+
+ ss.putInt("height", getHeight());
+ int childCount = getChildCount();
+ ss.putInt("childCount", childCount);
+ boolean haveChildren = childCount > 0 && mItemCount > 0;
+ if (haveChildren && mFirstPosition > 0) {
+ // Remember the position of the first child.
+ // We only do this if we are not currently at the top of
+ // the list, for two reasons:
+ // (1) The list may be in the process of becoming empty, in
+ // which case mItemCount may not be 0, but if we try to
+ // ask for any information about position 0 we will crash.
+ // (2) Being "at the top" seems like a special case, anyway,
+ // and the user wouldn't expect to end up somewhere else when
+ // they revisit the list even if its content has changed.
+
+ int firstPos = mFirstPosition;
+ if (firstPos >= mItemCount) {
+ firstPos = mItemCount - 1;
+ }
+ ss.putInt("position", firstPos);
+ ss.putLong("firstId", mAdapter.getItemId(firstPos));
+ View v = getChildAt(0);
+ ss.putInt("viewTop", v.getTop());
+ int[] viewTops = new int[childCount];
+ for (int i = 0; i < childCount; i++) {
+ viewTops[i] = getChildAt(i).getTop();
+ }
+ ss.putIntArray("viewTops", viewTops);
+ } else {
+ ss.putInt("viewTop", 0);
+ ss.putLong("firstId", INVALID_POSITION);
+ ss.putInt("position", 0);
+ ss.putIntArray("viewTops", new int[1]);
+ }
+ return ss;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ if (state instanceof Bundle) {
+ Bundle bundle = (Bundle) state;
+ mDataChanged = true;
+ mSyncHeight = bundle.getInt("height");
+ long firstId = bundle.getLong("firstId");
+ if (firstId >= 0) {
+ mNeedSync = true;
+ SavedState ss = new SavedState();
+ ss.firstId = firstId;
+ ss.height = (int) mSyncHeight;
+ ss.position = bundle.getInt("position");
+ ss.viewTop = bundle.getInt("viewTop");
+ ss.childCount = bundle.getInt("childCount");
+ ss.viewTops = bundle.getIntArray("viewTops");
+ mPendingSync = ss;
+ mSyncRowId = ss.firstId;
+ mSyncPosition = ss.position;
+ mSpecificTop = ss.viewTop;
+ mSpecificTops = ss.viewTops;
+ }
+ state = bundle.getParcelable("instanceState");
+ }
+ super.onRestoreInstanceState(state);
+ requestLayout();
+ }
}//end of class
diff --git a/src/main/java/com/huewu/pla/lib/internal/PLA_AdapterView.java b/library/src/main/java/com/huewu/pla/lib/internal/PLA_AdapterView.java
similarity index 98%
rename from src/main/java/com/huewu/pla/lib/internal/PLA_AdapterView.java
rename to library/src/main/java/com/huewu/pla/lib/internal/PLA_AdapterView.java
index 8f66c9e..aa587a6 100644
--- a/src/main/java/com/huewu/pla/lib/internal/PLA_AdapterView.java
+++ b/library/src/main/java/com/huewu/pla/lib/internal/PLA_AdapterView.java
@@ -23,21 +23,19 @@
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.SparseArray;
-import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
import android.widget.Adapter;
import com.huewu.pla.lib.DebugUtil;
/**
- * An AdapterView is a view whose children are determined by an {@link Adapter}.
+ * An AdapterView is a view whose children are determined by an {@link android.widget.Adapter}.
*
*
* See {@link ListView}, {@link GridView}, {@link Spinner} and
@@ -46,13 +44,13 @@
public abstract class PLA_AdapterView extends ViewGroup {
/**
- * The item view type returned by {@link Adapter#getItemViewType(int)} when
+ * The item view type returned by {@link android.widget.Adapter#getItemViewType(int)} when
* the adapter does not want the item's view recycled.
*/
public static final int ITEM_VIEW_TYPE_IGNORE = -1;
/**
- * The item view type returned by {@link Adapter#getItemViewType(int)} when
+ * The item view type returned by {@link android.widget.Adapter#getItemViewType(int)} when
* the item is a header or footer.
*/
public static final int ITEM_VIEW_TYPE_HEADER_OR_FOOTER = -2;
@@ -66,10 +64,11 @@ public abstract class PLA_AdapterView extends ViewGroup {
/**
* The offset in pixels from the top of the AdapterView to the top
* of the view to select during the next layout.
- */
- int mSpecificTop;
-
- /**
+ */
+ int mSpecificTop;
+ int[] mSpecificTops;
+
+ /**
* Position from which to start looking for mSyncRowId
*/
int mSyncPosition;
@@ -359,11 +358,11 @@ public final OnItemSelectedListener getOnItemSelectedListener() {
/**
* Extra menu information provided to the
- * {@link android.view.View.OnCreateContextMenuListener#onCreateContextMenu(ContextMenu, View, ContextMenuInfo) }
+ * {@link android.view.View.OnCreateContextMenuListener#onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo) }
* callback when a context menu is brought up for this AdapterView.
*
*/
- public static class AdapterContextMenuInfo implements ContextMenu.ContextMenuInfo {
+ public static class AdapterContextMenuInfo implements ContextMenuInfo {
public AdapterContextMenuInfo(View targetView, int position, long id) {
this.targetView = targetView;
diff --git a/src/main/java/com/huewu/pla/lib/internal/PLA_HeaderViewListAdapter.java b/library/src/main/java/com/huewu/pla/lib/internal/PLA_HeaderViewListAdapter.java
similarity index 100%
rename from src/main/java/com/huewu/pla/lib/internal/PLA_HeaderViewListAdapter.java
rename to library/src/main/java/com/huewu/pla/lib/internal/PLA_HeaderViewListAdapter.java
diff --git a/src/main/java/com/huewu/pla/lib/internal/PLA_ListView.java b/library/src/main/java/com/huewu/pla/lib/internal/PLA_ListView.java
similarity index 95%
rename from src/main/java/com/huewu/pla/lib/internal/PLA_ListView.java
rename to library/src/main/java/com/huewu/pla/lib/internal/PLA_ListView.java
index 95f6622..27f5f4c 100644
--- a/src/main/java/com/huewu/pla/lib/internal/PLA_ListView.java
+++ b/library/src/main/java/com/huewu/pla/lib/internal/PLA_ListView.java
@@ -32,7 +32,6 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ListAdapter;
-import android.widget.WrapperListAdapter;
import com.huewu.pla.R;
import com.huewu.pla.lib.DebugUtil;
@@ -51,7 +50,7 @@
/**
* A view that shows items in a vertically scrolling list. The items
- * come from the {@link ListAdapter} associated with this view.
+ * come from the {@link android.widget.ListAdapter} associated with this view.
*
* See the List View
* tutorial.
@@ -90,14 +89,14 @@ public class PLA_ListView extends PLA_AbsListView {
public class FixedViewInfo {
/** The view to add to the list */
public View view;
- /** The data backing the view. This is returned from {@link ListAdapter#getItem(int)}. */
+ /** The data backing the view. This is returned from {@link android.widget.ListAdapter#getItem(int)}. */
public Object data;
/** true if the fixed view should be selectable in the list */
public boolean isSelectable;
}
- private ArrayList mHeaderViewInfos = new ArrayList();
- private ArrayList mFooterViewInfos = new ArrayList();
+ private ArrayList mHeaderViewInfos = new ArrayList();
+ private ArrayList mFooterViewInfos = new ArrayList();
Drawable mDivider;
int mDividerHeight;
@@ -119,6 +118,7 @@ public class FixedViewInfo {
// used for temporary calculations.
private final Rect mTempRect = new Rect();
private Paint mDividerPaint;
+ private Paint mContentPaint = new Paint();
public PLA_ListView(Context context) {
this(context, null);
@@ -162,6 +162,12 @@ public PLA_ListView(Context context, AttributeSet attrs, int defStyle) {
mHeaderDividersEnabled = a.getBoolean(R.styleable.ListView_headerDividersEnabled, true);
mFooterDividersEnabled = a.getBoolean(R.styleable.ListView_footerDividersEnabled, true);
+ if (a.hasValue(R.styleable.ListView_plaContentBackground)) {
+ int contentBackgroundColor = a.getColor(R.styleable.ListView_plaContentBackground, 0);
+ mContentPaint = new Paint();
+ mContentPaint.setColor(contentBackgroundColor);
+ }
+
a.recycle();
}
@@ -316,7 +322,7 @@ public boolean isFixedView( View v ) {
public boolean removeHeaderView(View v) {
if (mHeaderViewInfos.size() > 0) {
boolean result = false;
- if (((PLA_HeaderViewListAdapter) mAdapter).removeHeader(v)) {
+ if (mAdapter != null && ((PLA_HeaderViewListAdapter) mAdapter).removeHeader(v)) {
mDataSetObserver.onChanged();
result = true;
}
@@ -393,7 +399,7 @@ public int getFooterViewsCount() {
public boolean removeFooterView(View v) {
if (mFooterViewInfos.size() > 0) {
boolean result = false;
- if (((PLA_HeaderViewListAdapter) mAdapter).removeFooter(v)) {
+ if (mAdapter != null && ((PLA_HeaderViewListAdapter) mAdapter).removeFooter(v)) {
mDataSetObserver.onChanged();
result = true;
}
@@ -405,12 +411,12 @@ public boolean removeFooterView(View v) {
/**
* Returns the adapter currently in use in this ListView. The returned adapter
- * might not be the same adapter passed to {@link #setAdapter(ListAdapter)} but
- * might be a {@link WrapperListAdapter}.
+ * might not be the same adapter passed to {@link #setAdapter(android.widget.ListAdapter)} but
+ * might be a {@link android.widget.WrapperListAdapter}.
*
* @return The adapter currently used to display data in this ListView.
*
- * @see #setAdapter(ListAdapter)
+ * @see #setAdapter(android.widget.ListAdapter)
*/
@Override
public ListAdapter getAdapter() {
@@ -420,7 +426,7 @@ public ListAdapter getAdapter() {
/**
* Sets the data behind this ListView.
*
- * The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},
+ * The adapter passed to this method may be wrapped by a {@link android.widget.WrapperListAdapter},
* depending on the ListView features currently in use. For instance, adding
* headers and/or footers will cause the adapter to be wrapped.
*
@@ -647,12 +653,11 @@ protected int getItemBottom(int pos) {
protected void fillGap(boolean down) {
final int count = getChildCount();
if (down) {
- fillDown(mFirstPosition + count, getItemTop(mFirstPosition + count));
- onAdjustChildViews( down );
+ fillDown(mFirstPosition + count, getFillChildBottom() + mDividerHeight);
} else {
- fillUp(mFirstPosition - 1, getItemBottom(mFirstPosition - 1));
- onAdjustChildViews( down );
+ fillUp(mFirstPosition - 1, getFillChildTop());
}
+ onAdjustChildViews(down);
}
/**
@@ -666,21 +671,16 @@ protected void fillGap(boolean down) {
* @return The view that is currently selected, if it happens to be in the
* range that we draw.
*/
- private View fillDown(int pos, int top) {
-
+ private View fillDown(int pos, int nextTop) {
DebugUtil.i("fill down: " + pos);
- //int end = (mBottom - mTop) - mListPadding.bottom;
- int end = (getBottom() - getTop()) - mListPadding.bottom;
- int childTop = getFillChildBottom() + mDividerHeight;
-
- while (childTop < end && pos < mItemCount) {
+ int end = getBottom() - getTop() - mListPadding.bottom;
+ while (nextTop < end && pos < mItemCount) {
// is this the selected item?
makeAndAddView(pos, getItemTop(pos), true, false);
pos++;
- childTop = getFillChildBottom() + mDividerHeight;
+ nextTop = getFillChildBottom() + mDividerHeight;
}
-
return null;
}
@@ -694,22 +694,20 @@ private View fillDown(int pos, int top) {
*
* @return The view that is currently selected
*/
- private View fillUp(int pos, int bottom) {
+ private View fillUp(int pos, int nextBottom) {
DebugUtil.i("fill up: " + pos);
int end = mListPadding.top;
- int childBottom = getFillChildTop();
- while (childBottom > end && pos >= 0) {
+ while (nextBottom > end && pos >= 0) {
// is this the selected item?
makeAndAddView(pos, getItemBottom(pos), false, false);
// nextBottom = child.getTop() - mDividerHeight;
pos--;
- childBottom = getItemBottom(pos);
+ nextBottom = getItemBottom(pos);
}
mFirstPosition = pos + 1;
-
return null;
}
@@ -817,7 +815,7 @@ protected boolean recycleOnMeasure() {
* current height reaches maxHeight.
*
* @param widthMeasureSpec The width measure spec to be given to a child's
- * {@link View#measure(int, int)}.
+ * {@link android.view.View#measure(int, int)}.
* @param startPosition The position of the first child to be shown.
* @param endPosition The (inclusive) position of the last child to be
* shown. Specify {@link #NO_POSITION} if the last child should be
@@ -940,16 +938,15 @@ private View fillSpecific(int position, int top) {
DebugUtil.i("fill specific: " + position + ":" + top);
View temp = makeAndAddView(position, top, true, false);
-
// Possibly changed again in fillUp if we add rows above this one.
-
mFirstPosition = position;
+
final int dividerHeight = mDividerHeight;
if (!mStackFromBottom) {
- fillUp(position - 1, temp.getTop() - dividerHeight);
+ fillUp(position - 1, getFillChildTop());
// This will correct for the top of the first view not touching the top of the list
adjustViewsUpOrDown();
- fillDown(position + 1, temp.getBottom() + dividerHeight);
+ fillDown(position + 1, getFillChildBottom() + mDividerHeight);
int childCount = getChildCount();
if (childCount > 0) {
correctTooHigh(childCount);
@@ -958,7 +955,7 @@ private View fillSpecific(int position, int top) {
fillDown(position + 1, temp.getBottom() + dividerHeight);
// This will correct for the bottom of the last view not touching the bottom of the list
adjustViewsUpOrDown();
- fillUp(position - 1, temp.getTop() - dividerHeight);
+ fillUp(position - 1, getFillChildTop());
int childCount = getChildCount();
if (childCount > 0) {
correctTooLow(childCount);
@@ -1001,7 +998,7 @@ private void correctTooHigh(int childCount) {
// Make sure we are 1) Too high, and 2) Either there are more rows above the
// first row or the first row is scrolled off the top of the drawable area
- if (bottomOffset > 0 && (mFirstPosition > 0 || firstTop < mListPadding.top)) {
+ if (bottomOffset > 0 && (mFirstPosition > 0 || firstTop < mListPadding.top)) {
if (mFirstPosition == 0) {
// Don't pull the top too far down
bottomOffset = Math.min(bottomOffset, mListPadding.top - firstTop);
@@ -1013,7 +1010,7 @@ private void correctTooHigh(int childCount) {
// Fill the gap that was opened above mFirstPosition with more rows, if
// possible
int newFirstTop = getScrollChildTop();
- fillUp(mFirstPosition - 1, newFirstTop - mDividerHeight);
+ fillUp(mFirstPosition - 1, getFillChildTop());
// Close up the remaining gap
adjustViewsUpOrDown();
}
@@ -1041,7 +1038,7 @@ private void correctTooLow(int childCount) {
final int start = mListPadding.top;
// This is bottom of our drawable area
- final int end = (getBottom() -getTop()) - mListPadding.bottom;
+ final int end = (getBottom() - getTop()) - mListPadding.bottom;
// This is how far the top edge of the first view is from the top of the drawable area
int topOffset = firstTop - start;
@@ -1052,7 +1049,7 @@ private void correctTooLow(int childCount) {
// Make sure we are 1) Too low, and 2) Either there are more rows below the
// last row or the last row is scrolled off the bottom of the drawable area
if (topOffset > 0) {
- if (lastPosition < mItemCount - 1 || lastBottom > end) {
+ if (lastPosition < mItemCount - 1 || lastBottom > end) {
if (lastPosition == mItemCount - 1) {
// Don't pull the bottom too far up
topOffset = Math.min(topOffset, lastBottom - end);
@@ -1062,7 +1059,7 @@ private void correctTooLow(int childCount) {
if (lastPosition < mItemCount - 1) {
// Fill the gap that was opened below the last position with more rows, if
// possible
- fillDown(lastPosition + 1, getFillChildTop() + mDividerHeight);
+ fillDown(lastPosition + 1, getFillChildBottom() + mDividerHeight);
// Close up the remaining gap
adjustViewsUpOrDown();
}
@@ -1091,9 +1088,8 @@ protected void layoutChildren() {
return;
}
- int childrenTop = mListPadding.top;
- //int childrenBottom = mBottom - mTop - mListPadding.bottom;
- int childrenBottom = getBottom() - getTop() - mListPadding.bottom;
+ int childrenTop = getFillChildBottom() + mDividerHeight;
+ int childrenBottom = getFillChildTop();
int childCount = getChildCount();
int index = 0;
@@ -1157,7 +1153,12 @@ protected void layoutChildren() {
onLayoutSync(mSyncPosition);
// Clear out old views
detachAllViewsFromParent();
- fillSpecific(mSyncPosition, mSpecificTop);
+ if (mSpecificTops != null) {
+ fillSynced(mSyncPosition, mSpecificTops);
+ mSpecificTops = null;
+ } else {
+ fillSpecific(mSyncPosition, mSpecificTop);
+ }
onLayoutSyncFinished(mSyncPosition);
break;
case LAYOUT_FORCE_BOTTOM:
@@ -1226,6 +1227,14 @@ protected void layoutChildren() {
}
}
+ private void fillSynced(int position, int[] specificTops) {
+ for (int i = 0; i < specificTops.length; i++) {
+ makeAndAddView(position + i, specificTops[i], true, false);
+ adjustViewsUpOrDown();
+ }
+ mFirstPosition = position;
+ }
+
/**
* Obtain the view and add it to our list of children. The view can be made
* fresh, converted from an unused view, or used as is if it was in the
@@ -1235,7 +1244,6 @@ protected void layoutChildren() {
* @param childrenBottomOrTop Top or bottom edge of the view to add
* @param flow If flow is true, align top edge to y. If false, align bottom
* edge to y.
- * @param childrenLeft Left edge where children should be positioned
* @param selected Is this position selected?
* @return View that was added
*/
@@ -1516,7 +1524,7 @@ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
* Go to the last or first item if possible (not worrying about panning across or navigating
* within the internal focus of the currently selected item.)
*
- * @param direction either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN}
+ * @param direction either {@link android.view.View#FOCUS_UP} or {@link android.view.View#FOCUS_DOWN}
*
* @return whether selection was moved
*/
@@ -1748,6 +1756,8 @@ protected void dispatchDraw(Canvas canvas) {
final boolean drawOverscrollFooter = overscrollFooter != null;
final boolean drawDividers = dividerHeight > 0 && mDivider != null;
+ drawContentBackground(canvas);
+
if (drawDividers || drawOverscrollHeader || drawOverscrollFooter) {
// Only modify the top and bottom in the loop, we set the left and right here
final Rect bounds = mTempRect;
@@ -1888,6 +1898,31 @@ protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
}
+ private void drawContentBackground(Canvas canvas) {
+ if (getHeaderViewsCount() > 0) {
+ Rect rect = mTempRect;
+ rect.left = getLeft();
+ rect.right = getRight();
+ View firstVisibleView = getChildAt(getFirstVisiblePosition());
+
+ if (isHeaderView(firstVisibleView)) {
+ View lastHeader = mHeaderViewInfos.get(mHeaderViewInfos.size() - 1).view;
+ rect.top = lastHeader.getBottom();
+ } else
+ rect.top = 0;
+ rect.bottom = getBottom();
+ canvas.drawRect(rect, mContentPaint);
+ }
+ }
+
+ private boolean isHeaderView(View view) {
+ for (FixedViewInfo info : mHeaderViewInfos) {
+ if (info.view == view)
+ return true;
+ }
+ return false;
+ }
+
/**
* Draws a divider for the given child in the given bounds.
*
@@ -1953,7 +1988,7 @@ public int getDividerHeight() {
/**
* Sets the height of the divider that will be drawn between each item in the list. Calling
- * this will override the intrinsic height as set by {@link #setDivider(Drawable)}
+ * this will override the intrinsic height as set by {@link #setDivider(android.graphics.drawable.Drawable)}
*
* @param height The new height of the divider in pixels.
*/
@@ -2124,7 +2159,7 @@ public boolean performItemClick(View view, int position, long id) {
* Sets the checked state of the specified position. The is only valid if
* the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or
* {@link #CHOICE_MODE_MULTIPLE}.
- *
+ *
* @param position The item whose checked state is to be checked
* @param value The new checked state for the item
*/
@@ -2175,10 +2210,10 @@ public SparseBooleanArray getCheckedItemPositions() {
/**
* Returns the set of checked items ids. The result is only valid if the
* choice mode has not been set to {@link #CHOICE_MODE_NONE}.
- *
+ *
* @return A new array which contains the id of each checked item in the
* list.
- *
+ *
* @deprecated Use {@link #getCheckedItemIds()} instead.
*/
@Deprecated
@@ -2194,8 +2229,8 @@ public long[] getCheckItemIds() {
/**
* Returns the set of checked items ids. The result is only valid if the
* choice mode has not been set to {@link #CHOICE_MODE_NONE} and the adapter
- * has stable IDs. ({@link ListAdapter#hasStableIds()} == {@code true})
- *
+ * has stable IDs. ({@link android.widget.ListAdapter#hasStableIds()} == {@code true})
+ *
* @return A new array which contains the id of each checked item in the
* list.
*/
diff --git a/res/drawable-hdpi/ad_button_015_refresh_down_arrow.png b/library/src/main/res/drawable-hdpi/ad_button_015_refresh_down_arrow.png
similarity index 100%
rename from res/drawable-hdpi/ad_button_015_refresh_down_arrow.png
rename to library/src/main/res/drawable-hdpi/ad_button_015_refresh_down_arrow.png
diff --git a/res/drawable-hdpi/arrow.png b/library/src/main/res/drawable-hdpi/arrow.png
similarity index 100%
rename from res/drawable-hdpi/arrow.png
rename to library/src/main/res/drawable-hdpi/arrow.png
diff --git a/res/drawable-hdpi/updating.png b/library/src/main/res/drawable-hdpi/updating.png
similarity index 100%
rename from res/drawable-hdpi/updating.png
rename to library/src/main/res/drawable-hdpi/updating.png
diff --git a/res/drawable-hdpi/updating_spinner.xml b/library/src/main/res/drawable-hdpi/updating_spinner.xml
similarity index 100%
rename from res/drawable-hdpi/updating_spinner.xml
rename to library/src/main/res/drawable-hdpi/updating_spinner.xml
diff --git a/res/drawable-mdpi/ad_button_015_refresh_down_arrow.png b/library/src/main/res/drawable-mdpi/ad_button_015_refresh_down_arrow.png
similarity index 100%
rename from res/drawable-mdpi/ad_button_015_refresh_down_arrow.png
rename to library/src/main/res/drawable-mdpi/ad_button_015_refresh_down_arrow.png
diff --git a/res/drawable-xhdpi/ad_button_015_refresh_down_arrow.png b/library/src/main/res/drawable-xhdpi/ad_button_015_refresh_down_arrow.png
similarity index 100%
rename from res/drawable-xhdpi/ad_button_015_refresh_down_arrow.png
rename to library/src/main/res/drawable-xhdpi/ad_button_015_refresh_down_arrow.png
diff --git a/res/layout/pull_to_refresh_header.xml b/library/src/main/res/layout/pull_to_refresh_header.xml
similarity index 100%
rename from res/layout/pull_to_refresh_header.xml
rename to library/src/main/res/layout/pull_to_refresh_header.xml
diff --git a/res/values/attrs.xml b/library/src/main/res/values/attrs.xml
similarity index 99%
rename from res/values/attrs.xml
rename to library/src/main/res/values/attrs.xml
index e0d6cfd..78068d1 100644
--- a/res/values/attrs.xml
+++ b/library/src/main/res/values/attrs.xml
@@ -838,6 +838,8 @@
+
+
\ No newline at end of file
diff --git a/res/values/dimens.xml b/library/src/main/res/values/dimens.xml
similarity index 100%
rename from res/values/dimens.xml
rename to library/src/main/res/values/dimens.xml
diff --git a/res/values/ids.xml b/library/src/main/res/values/ids.xml
similarity index 100%
rename from res/values/ids.xml
rename to library/src/main/res/values/ids.xml
diff --git a/res/values/strings.xml b/library/src/main/res/values/strings.xml
similarity index 100%
rename from res/values/strings.xml
rename to library/src/main/res/values/strings.xml
diff --git a/lint.xml b/lint.xml
deleted file mode 100644
index ee0eead..0000000
--- a/lint.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index 6447511..0000000
--- a/pom.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-
-
- 4.0.0
-
- com.huewu.pla
- pla
- 1.02
- apklib
-
- Vingle Android App
-
- http://www.vingle.net
-
-
- UTF-8
- UTF-8
-
-
- 1.6
-
- 4.1.1.4
- 17
-
-
-
- https://github.com/balmbees/PinterestLikeAdapterView.git
- scm:git@github.com:balmbees/PinterestLikeAdapterView.git
- scm:git@github.com:balmbees/PinterestLikeAdapterView.git
-
-
-
- GitHub Issues
- https://github.com/balmbees/PinterestLikeAdapterView/issues
-
-
-
- Vingle
- http://www.vingle.net/
-
-
-
-
- repo
- https://github.com/balmbees/vingle-maven-repo/raw/PLA/master/releases
-
-
- snapshot-repo
- https://github.com/balmbees/vingle-maven-repo/raw/PLA/master/snapshots
-
-
-
-
-
- com.jayway.maven.plugins.android.generation2
- android-maven-plugin
- true
- 3.5.3
-
- ignored
-
- ${android.platform}
-
-
-
-
-
-
-
- pusher-java-client-mvn-repo
- Pusher Java Client Library
- https://raw.github.com/pusher/pusher-java-client/mvn-repo/
-
-
- true
-
-
- true
-
-
-
-
-
-
-
- com.google.android
- android
- ${android.version}
- provided
-
-
-
-
diff --git a/proguard-project.txt b/proguard-project.txt
deleted file mode 100644
index f2fe155..0000000
--- a/proguard-project.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# To enable ProGuard in your project, edit project.properties
-# to define the proguard.config property as described in that file.
-#
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in ${sdk.dir}/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the ProGuard
-# include property in project.properties.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
diff --git a/project.properties b/project.properties
deleted file mode 100644
index 484dab0..0000000
--- a/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-17
-android.library=true
diff --git a/res/drawable-mdpi/ic_launcher.png b/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index f170ae5..0000000
Binary files a/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/res/layout/sample_pull_to_refresh_act.xml b/res/layout/sample_pull_to_refresh_act.xml
deleted file mode 100644
index 1c045fe..0000000
--- a/res/layout/sample_pull_to_refresh_act.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/res/values-v11/styles.xml b/res/values-v11/styles.xml
deleted file mode 100644
index d408cbc..0000000
--- a/res/values-v11/styles.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/res/values-v14/styles.xml b/res/values-v14/styles.xml
deleted file mode 100644
index 1c089a7..0000000
--- a/res/values-v14/styles.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/res/values/styles.xml b/res/values/styles.xml
deleted file mode 100644
index 4dba0d0..0000000
--- a/res/values/styles.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/sample/.gitignore b/sample/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/sample/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/sample/build.gradle b/sample/build.gradle
new file mode 100644
index 0000000..6b98576
--- /dev/null
+++ b/sample/build.gradle
@@ -0,0 +1,22 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion '21.1.2'
+
+ defaultConfig {
+ minSdkVersion 8
+ targetSdkVersion 21
+ versionCode 1
+ versionName "1.0"
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+}
+
+dependencies {
+ compile project(':library')
+ compile 'com.android.support:appcompat-v7:21.0.3'
+}
diff --git a/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
similarity index 54%
rename from AndroidManifest.xml
rename to sample/src/main/AndroidManifest.xml
index b31b852..01eb42c 100644
--- a/AndroidManifest.xml
+++ b/sample/src/main/AndroidManifest.xml
@@ -1,24 +1,22 @@
-
+
-
-
-
-
+
+
-
+
-
-
-
+
diff --git a/sample/src/main/java/com/huewu/pla/sample/SampleActivity.java b/sample/src/main/java/com/huewu/pla/sample/SampleActivity.java
new file mode 100644
index 0000000..079d5e6
--- /dev/null
+++ b/sample/src/main/java/com/huewu/pla/sample/SampleActivity.java
@@ -0,0 +1,131 @@
+package com.huewu.pla.sample;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+
+import com.huewu.pla.lib.MultiColumnListView;
+import com.huewu.pla.sample.view.SlidingTabLayout;
+
+public class SampleActivity extends ActionBarActivity {
+ private SectionsPagerAdapter mSectionsPagerAdapter;
+ private ViewPager mViewPager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.sample_act);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
+
+ // Set up the ViewPager with the sections adapter.
+ mViewPager = (ViewPager) findViewById(R.id.pager);
+ mViewPager.setAdapter(mSectionsPagerAdapter);
+
+ final SlidingTabLayout tabLayout = (SlidingTabLayout)findViewById(R.id.sliding_tabs);
+ tabLayout.setViewPager(mViewPager);
+ }
+
+ public class SectionsPagerAdapter extends FragmentPagerAdapter {
+
+ public SectionsPagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ return PlaceholderFragment.newInstance(position);
+ }
+
+ @Override
+ public int getCount() {
+ return 4;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return "SECTION " + position;
+ }
+ }
+
+ public static class PlaceholderFragment extends Fragment {
+
+ private PLAAdapter mAdapter;
+
+ public static PlaceholderFragment newInstance(int i) {
+ return new PlaceholderFragment();
+ }
+
+ public PlaceholderFragment() {
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.sample_frag, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+
+ MultiColumnListView listView = (MultiColumnListView) view.findViewById(R.id.list);
+ mAdapter = new PLAAdapter(getActivity());
+ fillAdapter(mAdapter, 30);
+ listView.setAdapter(mAdapter);
+ }
+
+ private void fillAdapter(PLAAdapter adapter, int count) {
+ for (int i = 0; i < count; ++i) {
+ StringBuilder builder = new StringBuilder();
+ for (int j = adapter.getCount(), max = (i * 1234) % 500; j < max; j++)
+ builder.append(i).append(' ');
+ adapter.add(builder.toString());
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ menu.add(Menu.NONE, 1002, 0, "Load More Contents");
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+
+ switch (item.getItemId()) {
+ case 1002: {
+ fillAdapter(mAdapter, 100);
+ }
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private static class PLAAdapter extends ArrayAdapter {
+ public PLAAdapter(Context context) {
+ super(context, R.layout.sample_item, android.R.id.text1);
+ }
+ }
+}
diff --git a/sample/src/main/java/com/huewu/pla/sample/view/SlidingTabLayout.java b/sample/src/main/java/com/huewu/pla/sample/view/SlidingTabLayout.java
new file mode 100644
index 0000000..a5d0b43
--- /dev/null
+++ b/sample/src/main/java/com/huewu/pla/sample/view/SlidingTabLayout.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.huewu.pla.sample.view;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.HorizontalScrollView;
+import android.widget.TextView;
+
+/**
+ * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
+ * the user's scroll progress.
+ *
+ * To use the component, simply add it to your view hierarchy. Then in your
+ * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
+ * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
+ *
+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors
+ * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
+ * alternative is via the {@link TabColorizer} interface which provides you complete control over
+ * which color is used for any individual position.
+ *
+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
+ * providing the layout ID of your custom layout.
+ */
+public class SlidingTabLayout extends HorizontalScrollView {
+
+ /**
+ * Allows complete control over the colors drawn in the tab layout. Set with
+ * {@link #setCustomTabColorizer(TabColorizer)}.
+ */
+ public interface TabColorizer {
+
+ /**
+ * @return return the color of the indicator used when {@code position} is selected.
+ */
+ int getIndicatorColor(int position);
+
+ /**
+ * @return return the color of the divider drawn to the right of {@code position}.
+ */
+ int getDividerColor(int position);
+
+ }
+
+ private static final int TITLE_OFFSET_DIPS = 24;
+ private static final int TAB_VIEW_PADDING_DIPS = 16;
+ private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
+
+ private int mTitleOffset;
+
+ private int mTabViewLayoutId;
+ private int mTabViewTextViewId;
+
+ private ViewPager mViewPager;
+ private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
+
+ private final SlidingTabStrip mTabStrip;
+
+ public SlidingTabLayout(Context context) {
+ this(context, null);
+ }
+
+ public SlidingTabLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ // Disable the Scroll Bar
+ setHorizontalScrollBarEnabled(false);
+ // Make sure that the Tab Strips fills this View
+ setFillViewport(true);
+
+ mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
+
+ mTabStrip = new SlidingTabStrip(context);
+ addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ /**
+ * Set the custom {@link TabColorizer} to be used.
+ *
+ * If you only require simple custmisation then you can use
+ * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
+ * similar effects.
+ */
+ public void setCustomTabColorizer(TabColorizer tabColorizer) {
+ mTabStrip.setCustomTabColorizer(tabColorizer);
+ }
+
+ /**
+ * Sets the colors to be used for indicating the selected tab. These colors are treated as a
+ * circular array. Providing one color will mean that all tabs are indicated with the same color.
+ */
+ public void setSelectedIndicatorColors(int... colors) {
+ mTabStrip.setSelectedIndicatorColors(colors);
+ }
+
+ /**
+ * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
+ * Providing one color will mean that all tabs are indicated with the same color.
+ */
+ public void setDividerColors(int... colors) {
+ mTabStrip.setDividerColors(colors);
+ }
+
+ /**
+ * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
+ * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
+ * that the layout can update it's scroll position correctly.
+ *
+ * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
+ */
+ public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+ mViewPagerPageChangeListener = listener;
+ }
+
+ /**
+ * Set the custom layout to be inflated for the tab views.
+ *
+ * @param layoutResId Layout id to be inflated
+ * @param textViewId id of the {@link TextView} in the inflated view
+ */
+ public void setCustomTabView(int layoutResId, int textViewId) {
+ mTabViewLayoutId = layoutResId;
+ mTabViewTextViewId = textViewId;
+ }
+
+ /**
+ * Sets the associated view pager. Note that the assumption here is that the pager content
+ * (number of tabs and tab titles) does not change after this call has been made.
+ */
+ public void setViewPager(ViewPager viewPager) {
+ mTabStrip.removeAllViews();
+
+ mViewPager = viewPager;
+ if (viewPager != null) {
+ viewPager.setOnPageChangeListener(new InternalViewPagerListener());
+ populateTabStrip();
+ }
+ }
+
+ /**
+ * Create a default view to be used for tabs. This is called if a custom tab view is not set via
+ * {@link #setCustomTabView(int, int)}.
+ */
+ protected TextView createDefaultTabView(Context context) {
+ TextView textView = new TextView(context);
+ textView.setGravity(Gravity.CENTER);
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
+ textView.setTypeface(Typeface.DEFAULT_BOLD);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ // If we're running on Honeycomb or newer, then we can use the Theme's
+ // selectableItemBackground to ensure that the View has a pressed state
+ TypedValue outValue = new TypedValue();
+ getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+ outValue, true);
+ textView.setBackgroundResource(outValue.resourceId);
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
+ textView.setAllCaps(true);
+ }
+
+ int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
+ textView.setPadding(padding, padding, padding, padding);
+
+ return textView;
+ }
+
+ private void populateTabStrip() {
+ final PagerAdapter adapter = mViewPager.getAdapter();
+ final View.OnClickListener tabClickListener = new TabClickListener();
+
+ for (int i = 0; i < adapter.getCount(); i++) {
+ View tabView = null;
+ TextView tabTitleView = null;
+
+ if (mTabViewLayoutId != 0) {
+ // If there is a custom tab view layout id set, try and inflate it
+ tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
+ false);
+ tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
+ }
+
+ if (tabView == null) {
+ tabView = createDefaultTabView(getContext());
+ }
+
+ if (tabTitleView == null && TextView.class.isInstance(tabView)) {
+ tabTitleView = (TextView) tabView;
+ }
+
+ tabTitleView.setText(adapter.getPageTitle(i));
+ tabView.setOnClickListener(tabClickListener);
+
+ mTabStrip.addView(tabView);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (mViewPager != null) {
+ scrollToTab(mViewPager.getCurrentItem(), 0);
+ }
+ }
+
+ private void scrollToTab(int tabIndex, int positionOffset) {
+ final int tabStripChildCount = mTabStrip.getChildCount();
+ if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
+ return;
+ }
+
+ View selectedChild = mTabStrip.getChildAt(tabIndex);
+ if (selectedChild != null) {
+ int targetScrollX = selectedChild.getLeft() + positionOffset;
+
+ if (tabIndex > 0 || positionOffset > 0) {
+ // If we're not at the first child and are mid-scroll, make sure we obey the offset
+ targetScrollX -= mTitleOffset;
+ }
+
+ scrollTo(targetScrollX, 0);
+ }
+ }
+
+ private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
+ private int mScrollState;
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ int tabStripChildCount = mTabStrip.getChildCount();
+ if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+ return;
+ }
+
+ mTabStrip.onViewPagerPageChanged(position, positionOffset);
+
+ View selectedTitle = mTabStrip.getChildAt(position);
+ int extraOffset = (selectedTitle != null)
+ ? (int) (positionOffset * selectedTitle.getWidth())
+ : 0;
+ scrollToTab(position, extraOffset);
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
+ positionOffsetPixels);
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ mScrollState = state;
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageScrollStateChanged(state);
+ }
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+ mTabStrip.onViewPagerPageChanged(position, 0f);
+ scrollToTab(position, 0);
+ }
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageSelected(position);
+ }
+ }
+
+ }
+
+ private class TabClickListener implements View.OnClickListener {
+ @Override
+ public void onClick(View v) {
+ for (int i = 0; i < mTabStrip.getChildCount(); i++) {
+ if (v == mTabStrip.getChildAt(i)) {
+ mViewPager.setCurrentItem(i);
+ return;
+ }
+ }
+ }
+ }
+
+}
+
diff --git a/sample/src/main/java/com/huewu/pla/sample/view/SlidingTabStrip.java b/sample/src/main/java/com/huewu/pla/sample/view/SlidingTabStrip.java
new file mode 100644
index 0000000..4b80fc8
--- /dev/null
+++ b/sample/src/main/java/com/huewu/pla/sample/view/SlidingTabStrip.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.huewu.pla.sample.view;
+
+import android.R;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+class SlidingTabStrip extends LinearLayout {
+
+ private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
+ private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
+ private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
+ private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
+
+ private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
+ private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
+ private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
+
+ private final int mBottomBorderThickness;
+ private final Paint mBottomBorderPaint;
+
+ private final int mSelectedIndicatorThickness;
+ private final Paint mSelectedIndicatorPaint;
+
+ private final int mDefaultBottomBorderColor;
+
+ private final Paint mDividerPaint;
+ private final float mDividerHeight;
+
+ private int mSelectedPosition;
+ private float mSelectionOffset;
+
+ private SlidingTabLayout.TabColorizer mCustomTabColorizer;
+ private final SimpleTabColorizer mDefaultTabColorizer;
+
+ SlidingTabStrip(Context context) {
+ this(context, null);
+ }
+
+ SlidingTabStrip(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setWillNotDraw(false);
+
+ final float density = getResources().getDisplayMetrics().density;
+
+ TypedValue outValue = new TypedValue();
+ context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
+ final int themeForegroundColor = outValue.data;
+
+ mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
+ DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
+
+ mDefaultTabColorizer = new SimpleTabColorizer();
+ mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
+ mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
+ DEFAULT_DIVIDER_COLOR_ALPHA));
+
+ mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
+ mBottomBorderPaint = new Paint();
+ mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
+
+ mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
+ mSelectedIndicatorPaint = new Paint();
+
+ mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
+ mDividerPaint = new Paint();
+ mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
+ }
+
+ void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
+ mCustomTabColorizer = customTabColorizer;
+ invalidate();
+ }
+
+ void setSelectedIndicatorColors(int... colors) {
+ // Make sure that the custom colorizer is removed
+ mCustomTabColorizer = null;
+ mDefaultTabColorizer.setIndicatorColors(colors);
+ invalidate();
+ }
+
+ void setDividerColors(int... colors) {
+ // Make sure that the custom colorizer is removed
+ mCustomTabColorizer = null;
+ mDefaultTabColorizer.setDividerColors(colors);
+ invalidate();
+ }
+
+ void onViewPagerPageChanged(int position, float positionOffset) {
+ mSelectedPosition = position;
+ mSelectionOffset = positionOffset;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ final int height = getHeight();
+ final int childCount = getChildCount();
+ final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
+ final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
+ ? mCustomTabColorizer
+ : mDefaultTabColorizer;
+
+ // Thick colored underline below the current selection
+ if (childCount > 0) {
+ View selectedTitle = getChildAt(mSelectedPosition);
+ int left = selectedTitle.getLeft();
+ int right = selectedTitle.getRight();
+ int color = tabColorizer.getIndicatorColor(mSelectedPosition);
+
+ if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
+ int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
+ if (color != nextColor) {
+ color = blendColors(nextColor, color, mSelectionOffset);
+ }
+
+ // Draw the selection partway between the tabs
+ View nextTitle = getChildAt(mSelectedPosition + 1);
+ left = (int) (mSelectionOffset * nextTitle.getLeft() +
+ (1.0f - mSelectionOffset) * left);
+ right = (int) (mSelectionOffset * nextTitle.getRight() +
+ (1.0f - mSelectionOffset) * right);
+ }
+
+ mSelectedIndicatorPaint.setColor(color);
+
+ canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
+ height, mSelectedIndicatorPaint);
+ }
+
+ // Thin underline along the entire bottom edge
+ canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
+
+ // Vertical separators between the titles
+ int separatorTop = (height - dividerHeightPx) / 2;
+ for (int i = 0; i < childCount - 1; i++) {
+ View child = getChildAt(i);
+ mDividerPaint.setColor(tabColorizer.getDividerColor(i));
+ canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
+ separatorTop + dividerHeightPx, mDividerPaint);
+ }
+ }
+
+ /**
+ * Set the alpha value of the {@code color} to be the given {@code alpha} value.
+ */
+ private static int setColorAlpha(int color, byte alpha) {
+ return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
+ }
+
+ /**
+ * Blend {@code color1} and {@code color2} using the given ratio.
+ *
+ * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
+ * 0.0 will return {@code color2}.
+ */
+ private static int blendColors(int color1, int color2, float ratio) {
+ final float inverseRation = 1f - ratio;
+ float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
+ float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
+ float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
+ return Color.rgb((int) r, (int) g, (int) b);
+ }
+
+ private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
+ private int[] mIndicatorColors;
+ private int[] mDividerColors;
+
+ @Override
+ public final int getIndicatorColor(int position) {
+ return mIndicatorColors[position % mIndicatorColors.length];
+ }
+
+ @Override
+ public final int getDividerColor(int position) {
+ return mDividerColors[position % mDividerColors.length];
+ }
+
+ void setIndicatorColors(int... colors) {
+ mIndicatorColors = colors;
+ }
+
+ void setDividerColors(int... colors) {
+ mDividerColors = colors;
+ }
+ }
+}
diff --git a/res/drawable-hdpi/ic_launcher.png b/sample/src/main/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from res/drawable-hdpi/ic_launcher.png
rename to sample/src/main/res/drawable-hdpi/ic_launcher.png
diff --git a/res/drawable-xhdpi/ic_launcher.png b/sample/src/main/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from res/drawable-xhdpi/ic_launcher.png
rename to sample/src/main/res/drawable-xhdpi/ic_launcher.png
diff --git a/sample/src/main/res/layout/sample_act.xml b/sample/src/main/res/layout/sample_act.xml
new file mode 100644
index 0000000..56d7142
--- /dev/null
+++ b/sample/src/main/res/layout/sample_act.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/sample_act.xml b/sample/src/main/res/layout/sample_frag.xml
similarity index 52%
rename from res/layout/sample_act.xml
rename to sample/src/main/res/layout/sample_frag.xml
index 1e08b31..923a05f 100644
--- a/res/layout/sample_act.xml
+++ b/sample/src/main/res/layout/sample_frag.xml
@@ -1,16 +1,15 @@
-
+ android:layout_height="match_parent">
-
+ pla:plaLandscapeColumnNumber="3"/>
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/res/layout/sample_item.xml b/sample/src/main/res/layout/sample_item.xml
similarity index 100%
rename from res/layout/sample_item.xml
rename to sample/src/main/res/layout/sample_item.xml
diff --git a/sample/src/main/res/values/ids.xml b/sample/src/main/res/values/ids.xml
new file mode 100644
index 0000000..a17848e
--- /dev/null
+++ b/sample/src/main/res/values/ids.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml
new file mode 100644
index 0000000..dcabfb3
--- /dev/null
+++ b/sample/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ PLASample
+
diff --git a/sample/src/main/res/values/styles.xml b/sample/src/main/res/values/styles.xml
new file mode 100644
index 0000000..8f6ad3b
--- /dev/null
+++ b/sample/src/main/res/values/styles.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/screenshot.png b/screenshot.png
index 726acc2..cbda31f 100644
Binary files a/screenshot.png and b/screenshot.png differ
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..c504e93
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+include ':sample'
+include ':library'
diff --git a/src/main/java/com/huewu/pla/lib/MultiColumnPullToRefreshListView.java b/src/main/java/com/huewu/pla/lib/MultiColumnPullToRefreshListView.java
deleted file mode 100644
index e10bfed..0000000
--- a/src/main/java/com/huewu/pla/lib/MultiColumnPullToRefreshListView.java
+++ /dev/null
@@ -1,783 +0,0 @@
-
-package com.huewu.pla.lib;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Typeface;
-import android.os.Handler;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-import android.view.animation.Animation;
-import android.view.animation.Animation.AnimationListener;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.RotateAnimation;
-import android.view.animation.TranslateAnimation;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import com.huewu.pla.R;
-
-import java.lang.ref.WeakReference;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-/**
- * A generic, customizable Android ListView implementation that has 'Pull to
- * Refresh' functionality.
- *
- * This ListView can be used in place of the normal Android
- * android.widget.ListView class.
- *
- * Users of this class should implement OnRefreshListener and call
- * setOnRefreshListener(..) to get notified on refresh events. The using class
- * should call onRefreshComplete() when refreshing is finished.
- *
- * The using class can call setRefreshing() to set the state explicitly to
- * refreshing. This is useful when you want to show the spinner and 'Refreshing'
- * text when the refresh was not triggered by 'Pull to Refresh', for example on
- * start.
- *
- * For more information, visit the project page:
- * https://github.com/erikwt/PullToRefresh-ListView
- *
- * @author Erik Wallentinsen
- * @version 1.0.0
- */
-public class MultiColumnPullToRefreshListView extends MultiColumnListView {
-
- private static final float PULL_RESISTANCE = 3.0f;
- private static final int BOUNCE_ANIMATION_DURATION = 215;
- private static final int BOUNCE_ANIMATION_DELAY = 20;
- private static final int ROTATE_ARROW_ANIMATION_DURATION = 250;
-
- // Loading...
- private LoadingThread mLoadingThread = null;
- final static int LOADINGBUFFER = 400;
- final static int LOADINGZERO = 100;
- final static int LOADINGONE = 101;
- final static int LOADINGTWO = 102;
- final static int LOADINGTHREE = 103;
-
- private static enum State {
- PULL_TO_REFRESH,
- RELEASE_TO_REFRESH,
- REFRESHING
- }
-
- /**
- * Interface to implement when you want to get notified of 'pull to refresh'
- * events. Call setOnRefreshListener(..) to activate an OnRefreshListener.
- */
- public interface OnRefreshListener {
-
- /**
- * Method to be called when a refresh is requested
- */
- public void onRefresh();
- }
-
- private static int measuredHeaderHeight;
-
- private boolean scrollbarEnabled;
- private boolean bounceBackHeader;
- private boolean lockScrollWhileRefreshing;
- private boolean showLastUpdatedText;
- private String pullToRefreshText;
- private String releaseToRefreshText;
- private String refreshingText;
- private String lastUpdatedText;
- private SimpleDateFormat lastUpdatedDateFormat = new SimpleDateFormat("dd/MM HH:mm");
-
- private float previousY;
- private int headerPadding;
- private boolean hasResetHeader;
- private long lastUpdated = -1;
- private State state;
- private LinearLayout headerContainer;
- private RelativeLayout header;
- private RotateAnimation flipAnimation;
- private RotateAnimation reverseFlipAnimation;
- private ImageView image;
- private ProgressBar spinner;
- private TextView text;
- // private TextView loadingText;
- private TextView lastUpdatedTextView;
- private OnRefreshListener onRefreshListener;
- private TranslateAnimation bounceAnimation;
-
- private boolean isHeaderRefreshing = false;
- private boolean isHeaderShowing = false;
-
- public MultiColumnPullToRefreshListView(Context context) {
- super(context);
- init(context, null);
- }
-
- public MultiColumnPullToRefreshListView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context, attrs);
- }
-
- public MultiColumnPullToRefreshListView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- init(context, attrs);
- }
-
- /**
- * Activate an OnRefreshListener to get notified on 'pull to refresh'
- * events.
- *
- * @param onRefreshListener The OnRefreshListener to get notified
- */
- public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
- this.onRefreshListener = onRefreshListener;
- }
-
- /**
- * @return If the list is in 'Refreshing' state
- */
- public boolean isRefreshing() {
- return state == State.REFRESHING;
- }
-
- /**
- * Default is false. When lockScrollWhileRefreshing is set to true, the list
- * cannot scroll when in 'refreshing' mode. It's 'locked' on refreshing.
- *
- * @param lockScrollWhileRefreshing
- */
- public void setLockScrollWhileRefreshing(boolean lockScrollWhileRefreshing) {
- this.lockScrollWhileRefreshing = lockScrollWhileRefreshing;
- }
-
- /**
- * Default is false. Show the last-updated date/time in the 'Pull ro
- * Refresh' header. See 'setLastUpdatedDateFormat' to set the date/time
- * formatting.
- *
- * @param showLastUpdatedText
- */
- public void setShowLastUpdatedText(boolean showLastUpdatedText) {
- this.showLastUpdatedText = showLastUpdatedText;
- if (!showLastUpdatedText)
- lastUpdatedTextView.setVisibility(View.GONE);
- }
-
- /**
- * Default: "dd/MM HH:mm". Set the format in which the last-updated
- * date/time is shown. Meaningless if 'showLastUpdatedText == false
- * (default)'. See 'setShowLastUpdatedText'.
- *
- * @param lastUpdatedDateFormat
- */
- public void setLastUpdatedDateFormat(SimpleDateFormat lastUpdatedDateFormat) {
- this.lastUpdatedDateFormat = lastUpdatedDateFormat;
- }
-
- /**
- * Explicitly set the state to refreshing. This is useful when you want to
- * show the spinner and 'Refreshing' text when the refresh was not triggered
- * by 'pull to refresh', for example on start.
- */
- public void setRefreshing() {
- state = State.REFRESHING;
- setUiRefreshing();
- // setHeaderPadding(0);
- // scrollTo(0, 0);
- }
-
- /**
- * Set the state back to 'pull to refresh'. Call this method when refreshing
- * the data is finished.
- */
- public void onRefreshComplete() {
- state = State.PULL_TO_REFRESH;
- resetHeader();
- lastUpdated = System.currentTimeMillis();
- }
-
- /**
- * Change the label text on state 'Pull to Refresh'
- *
- * @param pullToRefreshText Text
- */
- public void setTextPullToRefresh(String pullToRefreshText) {
- this.pullToRefreshText = pullToRefreshText;
- if (state == State.PULL_TO_REFRESH) {
- text.setText(pullToRefreshText);
- image.setVisibility(VISIBLE);
- if (mLoadingThread != null) {
- mLoadingThread.interrupt();
- mLoadingThread = null;
- }
- isHeaderRefreshing = false;
- // loadingText.setVisibility(View.GONE);
- }
- }
-
- /**
- * Change the label text on state 'Release to Refresh'
- *
- * @param releaseToRefreshText Text
- */
- public void setTextReleaseToRefresh(String releaseToRefreshText) {
- this.releaseToRefreshText = releaseToRefreshText;
- if (state == State.RELEASE_TO_REFRESH) {
- text.setText(releaseToRefreshText);
- image.setVisibility(VISIBLE);
- if (mLoadingThread != null) {
- mLoadingThread.interrupt();
- mLoadingThread = null;
- }
- isHeaderRefreshing = false;
- // loadingText.setVisibility(View.GONE);
- }
- }
-
- /**
- * Change the label text on state 'Refreshing'
- *
- * @param refreshingText Text
- */
- public void setTextRefreshing(String refreshingText) {
- this.refreshingText = refreshingText;
- if (state == State.REFRESHING) {
- text.setText(refreshingText);
- image.setVisibility(INVISIBLE);
- mLoadingThread = new LoadingThread(mLoadingHandler);
- mLoadingThread.start();
- isHeaderRefreshing = true;
- // loadingText.setVisibility(View.VISIBLE);
- }
- }
-
- public static float getDimensionDpSize(int id, Context context, AttributeSet attrs) {
- TypedArray typedArray = context
- .obtainStyledAttributes(attrs, R.styleable.PullToRefreshView);
- Resources resources = context.getResources();
- DisplayMetrics metrics = resources.getDisplayMetrics();
- float dp = typedArray.getDimension(id, -1) / (metrics.densityDpi / 160f);
- return dp;
- }
-
- private void init(Context context, AttributeSet attrs) {
- setVerticalFadingEdgeEnabled(false);
-
- headerContainer = (LinearLayout) LayoutInflater.from(getContext()).inflate(
- R.layout.pull_to_refresh_header, null);
- header = (RelativeLayout) headerContainer.findViewById(R.id.ptr_id_header);
- text = (TextView) header.findViewById(R.id.ptr_id_text);
- // loadingText = (TextView)
- // header.findViewById(R.id.ptr_id_loading_text);
- lastUpdatedTextView = (TextView) header.findViewById(R.id.ptr_id_last_updated);
- image = (ImageView) header.findViewById(R.id.ptr_id_arrow);
- spinner = (ProgressBar) header.findViewById(R.id.ptr_id_spinner);
-
- if (attrs == null) {
- text.setTextSize(15);
- // loadingText.setTextSize(15);
- // loadingText.setLayoutParams(new
- // android.view.ViewGroup.LayoutParams(
- // (int)
- // getDimensionDpSize(R.styleable.PullToRefreshView_ptrTextSize,
- // context,
- // attrs), android.view.ViewGroup.LayoutParams.MATCH_PARENT));
- lastUpdatedTextView.setTextSize(12);
- image.setPadding(0, 0, 5, 0);
- spinner.setPadding(0, 0, 5, 0);
- } else {
- text.setTextSize(getDimensionDpSize(R.styleable.PullToRefreshView_ptrTextSize, context,
- attrs));
- // loadingText.setTextSize(getDimensionDpSize(R.styleable.PullToRefreshView_ptrTextSize,
- // context, attrs));
- // loadingText.setLayoutParams(new
- // android.view.ViewGroup.LayoutParams(
- // (int)
- // getDimensionDpSize(R.styleable.PullToRefreshView_ptrTextSize,
- // context,
- // attrs), android.view.ViewGroup.LayoutParams.MATCH_PARENT));
- lastUpdatedTextView.setTextSize(getDimensionDpSize(
- R.styleable.PullToRefreshView_ptrLastUpdateTextSize, context, attrs));
- image.setPadding(
- 0,
- 0,
- (int) getDimensionDpSize(R.styleable.PullToRefreshView_ptrArrowMarginRight,
- context, attrs), 0);
- spinner.setPadding(
- 0,
- 0,
- (int) getDimensionDpSize(R.styleable.PullToRefreshView_ptrSpinnerMarginRight,
- context, attrs), 0);
- }
-
- TextView tv = new TextView(context);
- tv.setText("Loading");
- tv.setTypeface(Typeface.DEFAULT_BOLD);
- tv.setTextSize(getDimensionDpSize(R.styleable.PullToRefreshView_ptrTextSize, context, attrs));
-
- pullToRefreshText = getContext().getString(R.string.ptr_pull_to_refresh);
- releaseToRefreshText = getContext().getString(R.string.ptr_release_to_refresh);
- refreshingText = getContext().getString(R.string.ptr_loading);
- lastUpdatedText = getContext().getString(R.string.ptr_last_updated);
-
- flipAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f,
- RotateAnimation.RELATIVE_TO_SELF, 0.5f);
- flipAnimation.setInterpolator(new LinearInterpolator());
- flipAnimation.setDuration(ROTATE_ARROW_ANIMATION_DURATION);
- flipAnimation.setFillAfter(true);
-
- reverseFlipAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f,
- RotateAnimation.RELATIVE_TO_SELF, 0.5f);
- reverseFlipAnimation.setInterpolator(new LinearInterpolator());
- reverseFlipAnimation.setDuration(ROTATE_ARROW_ANIMATION_DURATION);
- reverseFlipAnimation.setFillAfter(true);
-
- addHeaderView(headerContainer);
- setState(State.PULL_TO_REFRESH);
- scrollbarEnabled = isVerticalScrollBarEnabled();
-
- ViewTreeObserver vto = header.getViewTreeObserver();
- vto.addOnGlobalLayoutListener(new PTROnGlobalLayoutListener());
-
- // super.setOnItemClickListener(new PTROnItemClickListener());
- // super.setOnItemLongClickListener(new PTROnItemLongClickListener());
- }
-
- private void setHeaderPadding(int padding) {
- headerPadding = padding;
-
- MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) header.getLayoutParams();
- mlp.setMargins(0, Math.round(padding), 0, 0);
- header.setLayoutParams(mlp);
- }
-
- private boolean isPulling = false;
-
- private boolean isPull(MotionEvent event) {
- return isPulling;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- // Log.i("Vingle", "interceptEvent x : " + event.getX() + ", y : " +
- // event.getY());
- // MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)
- // header.getLayoutParams();
- // Log.i("Vingle", "interceptEvent hx : " + mlp.topMargin);
- // Log.i("Vingle", "isHeaderRefresing : " + isHeaderRefreshing);
-
- if (isHeaderRefreshing && isHeaderShowing) {
-
- }
-
- if (lockScrollWhileRefreshing
- && (state == State.REFRESHING || getAnimation() != null
- && !getAnimation().hasEnded())) {
- return true; // consume touch event here..
- }
-
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- if (getFirstVisiblePosition() == 0)
- previousY = event.getY();
- break;
- case MotionEvent.ACTION_MOVE:
- if (getFirstVisiblePosition() == 0 && event.getY() - previousY > 0) {
- isPulling = true;
- return true;
- } else {
- isPulling = false;
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- isPulling = false;
- break;
- }
-
- return super.onInterceptTouchEvent(event);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- // Log.i("Vingle", "Event x : " + event.getX() + ", y : " +
- // event.getY());
- // MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)
- // header.getLayoutParams();
- // Log.i("Vingle", "Event hx : " + mlp.topMargin);
- // Log.i("Vingle", "isHeaderRefresing : " + isHeaderRefreshing);
-
- if (isHeaderRefreshing && isHeaderShowing) {
-
- }
-
- if (lockScrollWhileRefreshing
- && (state == State.REFRESHING || getAnimation() != null
- && !getAnimation().hasEnded())) {
- return true;
- }
-
- switch (event.getAction()) {
-
- case MotionEvent.ACTION_UP:
- if (isPull(event)
- && (state == State.RELEASE_TO_REFRESH || getFirstVisiblePosition() == 0)) {
- switch (state) {
- case RELEASE_TO_REFRESH:
- setState(State.REFRESHING);
- bounceBackHeader();
- break;
- case PULL_TO_REFRESH:
- resetHeader();
- break;
- default:
- break;
- }
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (isPull(event)) {
- float y = event.getY();
- float diff = y - previousY;
- if (diff > 0)
- diff /= PULL_RESISTANCE;
- previousY = y;
-
- int newHeaderPadding = Math.max(Math.round(headerPadding + diff),
- -header.getHeight());
-
- if (newHeaderPadding != headerPadding && state != State.REFRESHING) {
- setHeaderPadding(newHeaderPadding);
-
- if (state == State.PULL_TO_REFRESH && headerPadding > 0) {
- setState(State.RELEASE_TO_REFRESH);
-
- image.clearAnimation();
- image.startAnimation(flipAnimation);
- } else if (state == State.RELEASE_TO_REFRESH && headerPadding < 0) {
- setState(State.PULL_TO_REFRESH);
-
- image.clearAnimation();
- image.startAnimation(reverseFlipAnimation);
- }
- }
- }
-
- break;
- }
-
- return super.onTouchEvent(event);
- }
-
- private void bounceBackHeader() {
- int yTranslate = state == State.REFRESHING ?
- header.getHeight() - headerContainer.getHeight() :
- -headerContainer.getHeight() - headerContainer.getTop();
-
- bounceAnimation = new TranslateAnimation(
- TranslateAnimation.ABSOLUTE, 0,
- TranslateAnimation.ABSOLUTE, 0,
- TranslateAnimation.ABSOLUTE, 0,
- TranslateAnimation.ABSOLUTE, yTranslate);
-
- bounceAnimation.setDuration(BOUNCE_ANIMATION_DURATION);
- bounceAnimation.setFillEnabled(true);
- bounceAnimation.setFillAfter(false);
- bounceAnimation.setFillBefore(true);
- // bounceAnimation.setInterpolator(new
- // OvershootInterpolator(BOUNCE_OVERSHOOT_TENSION));
- bounceAnimation.setAnimationListener(new HeaderAnimationListener(yTranslate));
- startAnimation(bounceAnimation);
- }
-
- private void resetHeader() {
- if (getFirstVisiblePosition() > 0) {
- setHeaderPadding(-header.getHeight());
- setState(State.PULL_TO_REFRESH);
- return;
- }
-
- if (getAnimation() != null && !getAnimation().hasEnded()) {
- bounceBackHeader = true;
- } else {
- bounceBackHeader();
- }
- }
-
- private void setUiRefreshing() {
- spinner.setVisibility(View.GONE);
- image.clearAnimation();
- image.setVisibility(View.INVISIBLE);
- text.setText(refreshingText);
- mLoadingThread = new LoadingThread(mLoadingHandler);
- mLoadingThread.start();
- isHeaderRefreshing = true;
- // loadingText.setVisibility(View.VISIBLE);
- }
-
- private void setState(State state) {
- this.state = state;
- switch (state) {
- case PULL_TO_REFRESH:
- spinner.setVisibility(View.GONE);
- image.setVisibility(View.VISIBLE);
- text.setText(pullToRefreshText);
- if (mLoadingThread != null) {
- mLoadingThread.interrupt();
- mLoadingThread = null;
- }
- isHeaderRefreshing = false;
- // loadingText.setVisibility(View.GONE);
-
- if (showLastUpdatedText && lastUpdated != -1) {
- lastUpdatedTextView.setVisibility(View.VISIBLE);
- lastUpdatedTextView.setText(String.format(lastUpdatedText,
- lastUpdatedDateFormat.format(new Date(lastUpdated))));
- }
-
- break;
-
- case RELEASE_TO_REFRESH:
- spinner.setVisibility(View.GONE);
- image.setVisibility(View.VISIBLE);
- text.setText(releaseToRefreshText);
- if (mLoadingThread != null) {
- mLoadingThread.interrupt();
- mLoadingThread = null;
- }
- // loadingText.setVisibility(View.GONE);
- isHeaderRefreshing = false;
- break;
-
- case REFRESHING:
- setUiRefreshing();
-
- lastUpdated = System.currentTimeMillis();
- if (onRefreshListener == null) {
- setState(State.PULL_TO_REFRESH);
- } else {
- onRefreshListener.onRefresh();
- }
-
- break;
- }
- }
-
- @Override
- protected void onScrollChanged(int l, int t, int oldl, int oldt) {
- super.onScrollChanged(l, t, oldl, oldt);
-
- Log.i("Vingle", "hasResetHeader : " + hasResetHeader + ", t : " + t + ", oldt : " + oldt);
-
- if (!hasResetHeader) {
- if (measuredHeaderHeight > 0 && state != State.REFRESHING) {
- setHeaderPadding(-measuredHeaderHeight);
- }
-
- hasResetHeader = true;
- }
- }
-
- private class HeaderAnimationListener implements AnimationListener {
-
- private int height, translation;
- private State stateAtAnimationStart;
-
- public HeaderAnimationListener(int translation) {
- this.translation = translation;
- }
-
- @Override
- public void onAnimationStart(Animation animation) {
- stateAtAnimationStart = state;
-
- android.view.ViewGroup.LayoutParams lp = getLayoutParams();
- height = lp.height;
- lp.height = getHeight() - translation;
- setLayoutParams(lp);
-
- if (scrollbarEnabled) {
- setVerticalScrollBarEnabled(false);
- }
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- setHeaderPadding(stateAtAnimationStart == State.REFRESHING ? 0 : -measuredHeaderHeight
- - headerContainer.getTop());
- // setSelection(0);
-
- android.view.ViewGroup.LayoutParams lp = getLayoutParams();
- lp.height = height;
- setLayoutParams(lp);
-
- if (scrollbarEnabled) {
- setVerticalScrollBarEnabled(true);
- }
-
- if (bounceBackHeader) {
- bounceBackHeader = false;
-
- postDelayed(new Runnable() {
-
- @Override
- public void run() {
- resetHeader();
- }
- }, BOUNCE_ANIMATION_DELAY);
- } else if (stateAtAnimationStart != State.REFRESHING) {
- setState(State.PULL_TO_REFRESH);
- }
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- }
- }
-
- private class PTROnGlobalLayoutListener implements OnGlobalLayoutListener {
-
- @SuppressWarnings("deprecation")
- @Override
- public void onGlobalLayout() {
- int initialHeaderHeight = header.getHeight();
-
- if (initialHeaderHeight > 0) {
- measuredHeaderHeight = initialHeaderHeight;
-
- if (measuredHeaderHeight > 0 && state != State.REFRESHING) {
- setHeaderPadding(-measuredHeaderHeight);
- requestLayout();
- }
- }
-
- getViewTreeObserver().removeGlobalOnLayoutListener(this);
- }
- }
-
- // //////////////////////////////////////////////////////////////////
- // Loading Thread & Handler */
- // //////////////////////////////////////////////////////////////////
-
- private class LoadingThread extends Thread {
- Handler mHandler;
-
- public LoadingThread(Handler mHandler) {
- this.mHandler = mHandler;
- }
-
- @Override
- public void run() {
- try {
- while (true) {
- Message msg_loading_0 = mHandler.obtainMessage(LOADINGZERO);
- msg_loading_0.obj = new WeakReference(
- text);
- mHandler.sendMessage(msg_loading_0);
- Thread.sleep(LOADINGBUFFER);
-
- Message msg_loading_1 = mHandler.obtainMessage(LOADINGONE);
- msg_loading_1.obj = new WeakReference(
- text);
- mHandler.sendMessage(msg_loading_1);
- Thread.sleep(LOADINGBUFFER);
-
- Message msg_loading_2 = mHandler.obtainMessage(LOADINGTWO);
- msg_loading_2.obj = new WeakReference(
- text);
- mHandler.sendMessage(msg_loading_2);
- Thread.sleep(LOADINGBUFFER);
-
- Message msg_loading_3 = mHandler
- .obtainMessage(LOADINGTHREE);
- msg_loading_3.obj = new WeakReference(
- text);
- mHandler.sendMessage(msg_loading_3);
- Thread.sleep(LOADINGBUFFER);
- }
- } catch (InterruptedException e) {
- // do nothing.
- }
- }
- }
-
- private static Handler mLoadingHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
-
- @SuppressWarnings("unchecked")
- TextView tv = ((WeakReference) msg.obj).get();
- if (tv == null)
- return;
-
- switch (msg.what) {
- case LOADINGZERO:
- tv.setText("Loading");
- break;
-
- case LOADINGONE:
- tv.setText("Loading.");
- break;
-
- case LOADINGTWO:
- tv.setText("Loading..");
- break;
-
- case LOADINGTHREE:
- tv.setText("Loading...");
- break;
-
- default:
- break;
- }
- }
- };
-
- // private class PTROnItemClickListener implements OnItemClickListener {
- //
- // @Override
- // public void onItemClick(AdapterView> adapterView, View view, int
- // position, long id){
- // hasResetHeader = false;
- //
- // if(onItemClickListener != null && state == State.PULL_TO_REFRESH){
- // // Passing up onItemClick. Correct position with the number of header
- // views
- // onItemClickListener.onItemClick(adapterView, view, position -
- // getHeaderViewsCount(), id);
- // }
- // }
- // }
- //
- // private class PTROnItemLongClickListener implements
- // OnItemLongClickListener{
- //
- // @Override
- // public boolean onItemLongClick(AdapterView> adapterView, View view, int
- // position, long id){
- // hasResetHeader = false;
- //
- // if(onItemLongClickListener != null && state == State.PULL_TO_REFRESH){
- // // Passing up onItemLongClick. Correct position with the number of header
- // views
- // return onItemLongClickListener.onItemLongClick(adapterView, view,
- // position - getHeaderViewsCount(), id);
- // }
- //
- // return false;
- // }
- // }
-}
diff --git a/src/main/java/com/huewu/pla/sample/PullToRefreshSampleActivity.java b/src/main/java/com/huewu/pla/sample/PullToRefreshSampleActivity.java
deleted file mode 100644
index ab0669e..0000000
--- a/src/main/java/com/huewu/pla/sample/PullToRefreshSampleActivity.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.huewu.pla.sample;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.widget.ArrayAdapter;
-import android.widget.ListAdapter;
-
-import com.huewu.pla.R;
-import com.huewu.pla.lib.internal.PLA_AdapterView;
-
-import java.util.Arrays;
-import java.util.Random;
-
-public class PullToRefreshSampleActivity extends Activity {
-
- private class MySimpleAdapter extends ArrayAdapter {
-
- public MySimpleAdapter(Context context, int layoutRes) {
- super(context, layoutRes, android.R.id.text1);
- }
- }
-
- private PLA_AdapterView mAdapterView = null;
- private MySimpleAdapter mAdapter = null;
-
- @SuppressWarnings("unchecked")
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.sample_pull_to_refresh_act);
- //mAdapterView = (PLA_AdapterView) findViewById(R.id.list);
- mAdapterView = (PLA_AdapterView) findViewById(R.id.list);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(Menu.NONE, 1001, 0, "Load More Contents");
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
-
- switch(item.getItemId()){
- case 1001:
- {
- int startCount = mAdapter.getCount();
- for( int i = 0; i < 100; ++i){
- //generate 100 random items.
-
- StringBuilder builder = new StringBuilder();
- builder.append("Hello!![");
- builder.append(startCount + i);
- builder.append("] ");
-
- char[] chars = new char[mRand.nextInt(100)];
- Arrays.fill(chars, '1');
- builder.append(chars);
- mAdapter.add(builder.toString());
- }
- }
- break;
- case 1002:
- {
- Intent intent = new Intent(this, PullToRefreshSampleActivity.class);
- startActivity(intent);
- }
- break;
- }
- return true;
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- initAdapter();
- mAdapterView.setAdapter(mAdapter);
- //mAdapterView.setAdapter(mAdapter);
- }
-
- private Random mRand = new Random();
- private void initAdapter() {
- mAdapter = new MySimpleAdapter(this, R.layout.sample_item);
-
- for( int i = 0; i < 30; ++i){
- //generate 30 random items.
-
- StringBuilder builder = new StringBuilder();
- builder.append("Hello!![");
- builder.append(i);
- builder.append("] ");
-
- char[] chars = new char[mRand.nextInt(500)];
- Arrays.fill(chars, '1');
- builder.append(chars);
- mAdapter.add(builder.toString());
- }
-
- }
-
-}//end of class
diff --git a/src/main/java/com/huewu/pla/sample/SampleActivity.java b/src/main/java/com/huewu/pla/sample/SampleActivity.java
deleted file mode 100644
index 24e3393..0000000
--- a/src/main/java/com/huewu/pla/sample/SampleActivity.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package com.huewu.pla.sample;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.widget.ArrayAdapter;
-import android.widget.TextView;
-
-import com.huewu.pla.R;
-import com.huewu.pla.lib.MultiColumnListView;
-import com.huewu.pla.lib.internal.PLA_AbsListView.LayoutParams;
-
-import java.util.Arrays;
-import java.util.Random;
-
-public class SampleActivity extends Activity {
-
- private class MySimpleAdapter extends ArrayAdapter {
-
- public MySimpleAdapter(Context context, int layoutRes) {
- super(context, layoutRes, android.R.id.text1);
- }
- }
-
- private MultiColumnListView mAdapterView = null;
- private MySimpleAdapter mAdapter = null;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.sample_act);
- //mAdapterView = (PLA_AdapterView) findViewById(R.id.list);
-
- mAdapterView = (MultiColumnListView) findViewById(R.id.list);
-
- {
- for( int i = 0; i < 3; ++i ){
- //add header view.
- TextView tv = new TextView(this);
- tv.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
- tv.setText("Hello Header!! ........................................................................");
- mAdapterView.addHeaderView(tv);
- }
- }
- {
- for( int i = 0; i < 3; ++i ){
- //add footer view.
- TextView tv = new TextView(this);
- tv.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
- tv.setText("Hello Footer!! ........................................................................");
- mAdapterView.addFooterView(tv);
- }
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(Menu.NONE, 1001, 0, "Load More Contents");
- menu.add(Menu.NONE, 1002, 0, "Launch Pull-To-Refresh Activity");
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
-
- switch(item.getItemId()){
- case 1001:
- {
- int startCount = mAdapter.getCount();
- for( int i = 0; i < 100; ++i){
- //generate 100 random items.
-
- StringBuilder builder = new StringBuilder();
- builder.append("Hello!![");
- builder.append(startCount + i);
- builder.append("] ");
-
- char[] chars = new char[mRand.nextInt(100)];
- Arrays.fill(chars, '1');
- builder.append(chars);
- mAdapter.add(builder.toString());
- }
- }
- break;
- case 1002:
- {
- Intent intent = new Intent(this, PullToRefreshSampleActivity.class);
- startActivity(intent);
- }
- break;
- }
- return true;
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- initAdapter();
- mAdapterView.setAdapter(mAdapter);
- //mAdapterView.setAdapter(mAdapter);
- }
-
- private Random mRand = new Random();
- private void initAdapter() {
- mAdapter = new MySimpleAdapter(this, R.layout.sample_item);
-
- for( int i = 0; i < 30; ++i){
- //generate 30 random items.
-
- StringBuilder builder = new StringBuilder();
- builder.append("Hello!![");
- builder.append(i);
- builder.append("] ");
-
- char[] chars = new char[mRand.nextInt(500)];
- Arrays.fill(chars, '1');
- builder.append(chars);
- mAdapter.add(builder.toString());
- }
-
- }
-
-}//end of class