RecyclerView horizontal scrolling to left - java

I'm making an app where I'm using RecyclerView with SnapHelper class to make a scrollable horizontal card stack. I have implemented that successfully however my problem is that the cards swipe to right direction and I want to swipe the cards on left direction.
I did some research on Stack Overflow and found 1 and 2
But I still couldn't figure out how to set the scrolling position to left instead of right.
I also found that FindSnapView method passes the scroll position and direction maybe I'm wrong but below code might be the solution
#Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
if (layoutManager instanceof LadderLayoutManager) {
int pos = ((LadderLayoutManager) layoutManager)
.getFixedScrollPosition(mDirection, mDirection != 0 ? 0.8f : 0.5f);
mDirection = 0;
if (pos != RecyclerView.NO_POSITION) {
return layoutManager.findViewByPosition(pos);
}
}
return null;
}
My main class
public class MainActivity extends AppCompatActivity {
LadderLayoutManager llm;
RecyclerView rcv;
HSAdapter adapter;
int scrollToPosition;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
llm = new LadderLayoutManager(1.5f, 0.85f, LadderLayoutManager.HORIZONTAL).
setChildDecorateHelper(new LadderLayoutManager
.DefaultChildDecorateHelper(getResources().getDimension(R.dimen.item_max_elevation)));
llm.setChildPeekSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
30, getResources().getDisplayMetrics()));
llm.setMaxItemLayoutCount(5);
rcv = (RecyclerView) findViewById(R.id.rcv);
rcv.setLayoutManager(llm);
new LadderSimpleSnapHelper().attachToRecyclerView(rcv);
adapter = new HSAdapter();
rcv.setAdapter(adapter);
final SeekBar sb = (SeekBar) findViewById(R.id.sb);
}
My code for custom LayoutManager
public class LadderLayoutManager extends RecyclerView.LayoutManager implements RecyclerView.SmoothScroller.ScrollVectorProvider {
private static final int INVALIDATE_SCROLL_OFFSET = Integer.MAX_VALUE;
private static final float DEFAULT_CHILD_LAYOUT_OFFSET = 0.2f;
public static final int UNLIMITED = 0;
public static final int VERTICAL = 1;
public static final int HORIZONTAL = 0;
private boolean mCheckedChildSize;
private int[] mChildSize;
private int mChildPeekSize;
private int mChildPeekSizeInput;
private boolean mReverse;
private int mScrollOffset = INVALIDATE_SCROLL_OFFSET;
private float mItemHeightWidthRatio;
private float mScale;
private int mChildCount;
private float mVanishOffset = 0;
private Interpolator mInterpolator;
private int mOrientation;
private ChildDecorateHelper mDecorateHelper;
private int mMaxItemLayoutCount;
public LadderLayoutManager(float itemHeightWidthRatio) {
this(itemHeightWidthRatio, 0.9f, VERTICAL);
}
public LadderLayoutManager(float itemHeightWidthRatio, float scale, int orientation) {
this.mItemHeightWidthRatio = itemHeightWidthRatio;
this.mOrientation = orientation;
this.mScale = scale;
this.mChildSize = new int[2];
this.mInterpolator = new DecelerateInterpolator();
}
#Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(mChildSize[0], mChildSize[1]);
}
public LadderLayoutManager setChildDecorateHelper(ChildDecorateHelper layoutHelper) {
mDecorateHelper = layoutHelper;
return this;
}
public void setMaxItemLayoutCount(int count) {
mMaxItemLayoutCount = Math.max(2, count);
if (getChildCount() > 0) {
requestLayout();
}
}
public void setVanishOffset(float offset) {
mVanishOffset = offset;
if (getChildCount() > 0) {
requestLayout();
}
}
public void setChildPeekSize(int childPeekSize) {
mChildPeekSizeInput = childPeekSize;
mCheckedChildSize = false;
if (getChildCount() > 0) {
requestLayout();
}
}
public void setItemHeightWidthRatio(float itemHeightWidthRatio) {
mItemHeightWidthRatio = itemHeightWidthRatio;
mCheckedChildSize = false;
if (getChildCount() > 0) {
requestLayout();
}
}
public void setReverse(boolean reverse) {
if (mReverse != reverse) {
mReverse = reverse;
if (getChildCount() > 0) {
requestLayout();
}
}
}
public boolean isReverse() {
return mReverse;
}
public int getFixedScrollPosition(int direction, float fixValue) {
if (mCheckedChildSize) {
if (mScrollOffset % mChildSize[mOrientation] == 0) {
return RecyclerView.NO_POSITION;
}
float position = mScrollOffset * 1.0f / mChildSize[mOrientation];
return convert2AdapterPosition((int) (direction > 0 ? position + fixValue : position + (1 - fixValue)) - 1);
}
return RecyclerView.NO_POSITION;
}
#Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
super.onMeasure(recycler, state, widthSpec, heightSpec);
mCheckedChildSize = false;
}
#Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (state.getItemCount() == 0) {
removeAndRecycleAllViews(recycler);
return;
}
if (!mCheckedChildSize) {
if (mOrientation == VERTICAL) {
mChildSize[0] = getHorizontalSpace();
mChildSize[1] = (int) (mItemHeightWidthRatio * mChildSize[0]);
} else {
mChildSize[1] = getVerticalSpace();
mChildSize[0] = (int) (mChildSize[1] / mItemHeightWidthRatio);
}
mChildPeekSize = mChildPeekSizeInput == 0 ?
(int) (mChildSize[mOrientation] * DEFAULT_CHILD_LAYOUT_OFFSET) : mChildPeekSizeInput;
mCheckedChildSize = true;
}
int itemCount = getItemCount();
if (mReverse) {
mScrollOffset += (itemCount - mChildCount) * mChildSize[mOrientation];
}
mChildCount = itemCount;
mScrollOffset = makeScrollOffsetWithinRange(mScrollOffset);
fill(recycler);
}
public void fill(RecyclerView.Recycler recycler) {
int bottomItemPosition = (int) Math.floor(mScrollOffset / mChildSize[mOrientation]);//>=1
int bottomItemVisibleSize = mScrollOffset % mChildSize[mOrientation];
final float offsetPercent = mInterpolator.getInterpolation(
bottomItemVisibleSize * 1.0f / mChildSize[mOrientation]);//[0,1)
final int space = mOrientation == VERTICAL ? getVerticalSpace() : getHorizontalSpace();
ArrayList<ItemLayoutInfo> layoutInfos = new ArrayList<>();
for (int i = bottomItemPosition - 1, j = 1, remainSpace = space - mChildSize[mOrientation];
i >= 0; i--, j++) {
double maxOffset = mChildPeekSize * Math.pow(mScale, j);
int start = (int) (remainSpace - offsetPercent * maxOffset);
ItemLayoutInfo info = new ItemLayoutInfo(start,
(float) (Math.pow(mScale, j - 1) * (1 - offsetPercent * (1 - mScale))),
offsetPercent,
start * 1.0f / space
);
layoutInfos.add(0, info);
if (mMaxItemLayoutCount != UNLIMITED && j == mMaxItemLayoutCount - 1) {
if (offsetPercent != 0) {
info.start = remainSpace;
info.positionOffsetPercent = 0;
info.layoutPercent = remainSpace / space;
info.scaleXY = (float) Math.pow(mScale, j - 1);
}
break;
}
remainSpace -= maxOffset;
if (remainSpace <= 0) {
info.start = (int) (remainSpace + maxOffset);
info.positionOffsetPercent = 0;
info.layoutPercent = info.start / space;
info.scaleXY = (float) Math.pow(mScale, j - 1);
break;
}
}
if (bottomItemPosition < mChildCount) {
final int start = space - bottomItemVisibleSize;
layoutInfos.add(new ItemLayoutInfo(start, 1.0f,
bottomItemVisibleSize * 1.0f / mChildSize[mOrientation], start * 1.0f / space).
setIsBottom());
} else {
bottomItemPosition -= 1;
}
int layoutCount = layoutInfos.size();
final int startPos = bottomItemPosition - (layoutCount - 1);
final int endPos = bottomItemPosition;
final int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
View childView = getChildAt(i);
int pos = convert2LayoutPosition(getPosition(childView));
if (pos > endPos || pos < startPos) {
removeAndRecycleView(childView, recycler);
}
}
detachAndScrapAttachedViews(recycler);
for (int i = 0; i < layoutCount; i++) {
fillChild(recycler.getViewForPosition(convert2AdapterPosition(startPos + i)), layoutInfos.get(i));
}
}
private void fillChild(View view, ItemLayoutInfo layoutInfo) {
addView(view);
measureChildWithExactlySize(view);
final int scaleFix = (int) (mChildSize[mOrientation] * (1 - layoutInfo.scaleXY) / 2);
final float gap = (mOrientation == VERTICAL ? getHorizontalSpace() : getVerticalSpace())
- mChildSize[(mOrientation + 1) % 2] * layoutInfo.scaleXY;
if (mOrientation == VERTICAL) {
int left = (int) (getPaddingLeft() + (gap * 0.5 * mVanishOffset));
layoutDecoratedWithMargins(view, left, layoutInfo.start - scaleFix
, left + mChildSize[0], layoutInfo.start + mChildSize[1] - scaleFix);
} else {
int top = (int) (getPaddingTop() + (gap * 0.5 * mVanishOffset));
layoutDecoratedWithMargins(view, layoutInfo.start - scaleFix, top
, layoutInfo.start + mChildSize[0] - scaleFix, top + mChildSize[1]);
}
ViewCompat.setScaleX(view, layoutInfo.scaleXY);
ViewCompat.setScaleY(view, layoutInfo.scaleXY);
if (mDecorateHelper != null) {
mDecorateHelper.decorateChild(view, layoutInfo.positionOffsetPercent, layoutInfo.layoutPercent, layoutInfo.isBottom);
}
}
private void measureChildWithExactlySize(View child) {
RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
final int widthSpec = View.MeasureSpec.makeMeasureSpec(
mChildSize[0] - lp.leftMargin - lp.rightMargin, View.MeasureSpec.EXACTLY);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(
mChildSize[1] - lp.topMargin - lp.bottomMargin, View.MeasureSpec.EXACTLY);
child.measure(widthSpec, heightSpec);
}
private int makeScrollOffsetWithinRange(int scrollOffset) {
return Math.min(Math.max(mChildSize[mOrientation], scrollOffset), mChildCount * mChildSize[mOrientation]);
}
#Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
int pendingScrollOffset = mScrollOffset + dy;
mScrollOffset = makeScrollOffsetWithinRange(pendingScrollOffset);
fill(recycler);
return mScrollOffset - pendingScrollOffset + dy;
}
#Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
int pendingScrollOffset = mScrollOffset + dx;
mScrollOffset = makeScrollOffsetWithinRange(pendingScrollOffset);
fill(recycler);
return mScrollOffset - pendingScrollOffset + dx;
}
#Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
final LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
#Override
public int calculateDyToMakeVisible(final View view, final int snapPreference) {
if (mOrientation == VERTICAL) {
return -calculateDistanceToPosition(getPosition(view));
}
return 0;
}
#Override
public int calculateDxToMakeVisible(final View view, final int snapPreference) {
if (mOrientation == HORIZONTAL) {
return -calculateDistanceToPosition(getPosition(view));
}
return 0;
}
};
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
public int calculateDistanceToPosition(int targetPos) {
int pendingScrollOffset = mChildSize[mOrientation] * (convert2LayoutPosition(targetPos) + 1);
return pendingScrollOffset - mScrollOffset;
}
#Override
public void scrollToPosition(int position) {
if (position > 0 && position < mChildCount) {
mScrollOffset = mChildSize[mOrientation] * (convert2LayoutPosition(position) + 1);
requestLayout();
}
}
#Override
public boolean canScrollVertically() {
return mOrientation == VERTICAL;
}
#Override
public boolean canScrollHorizontally() {
return mOrientation == HORIZONTAL;
}
public int convert2AdapterPosition(int layoutPosition) {
return mReverse ? mChildCount - 1 - layoutPosition : layoutPosition;
}
public int convert2LayoutPosition(int adapterPostion) {
return mReverse ? mChildCount - 1 - adapterPostion : adapterPostion;
}
public int getVerticalSpace() {
return getHeight() - getPaddingTop() - getPaddingBottom();
}
public int getHorizontalSpace() {
return getWidth() - getPaddingLeft() - getPaddingRight();
}
#Override
public PointF computeScrollVectorForPosition(int targetPosition) {
int pos = convert2LayoutPosition(targetPosition);
int scrollOffset = (pos + 1) * mChildSize[mOrientation];
return mOrientation == VERTICAL ? new PointF(0, Math.signum(scrollOffset - mScrollOffset))
: new PointF(Math.signum(scrollOffset - mScrollOffset), 0);
}
private static class ItemLayoutInfo {
float scaleXY;
float layoutPercent;
float positionOffsetPercent;
int start;
boolean isBottom;
ItemLayoutInfo(int top, float scale, float positonOffset, float percent) {
this.start = top;
this.scaleXY = scale;
this.positionOffsetPercent = positonOffset;
this.layoutPercent = percent;
}
ItemLayoutInfo setIsBottom() {
isBottom = true;
return this;
}
}
#Override
public Parcelable onSaveInstanceState() {
SavedState savedState = new SavedState();
savedState.scrollOffset = mScrollOffset;
savedState.reverse = mReverse;
savedState.vanishOffset = mVanishOffset;
savedState.scale = mScale;
savedState.childLayoutOffsetInput = mChildPeekSizeInput;
savedState.itemHeightWidthRatio = mItemHeightWidthRatio;
savedState.orientation = mOrientation;
return savedState;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof SavedState) {
SavedState s = (SavedState) state;
mScrollOffset = s.scrollOffset;
mReverse = s.reverse;
mVanishOffset = s.vanishOffset;
mScale = s.scale;
mChildPeekSizeInput = s.childLayoutOffsetInput;
mItemHeightWidthRatio = s.itemHeightWidthRatio;
mOrientation = s.orientation;
requestLayout();
}
}
public static class SavedState implements Parcelable {
int scrollOffset, childLayoutOffsetInput, orientation;
float itemHeightWidthRatio, scale, elevation, vanishOffset;
boolean reverse;
public SavedState() {
}
SavedState(LadderLayoutManager.SavedState other) {
scrollOffset = other.scrollOffset;
childLayoutOffsetInput = other.childLayoutOffsetInput;
orientation = other.orientation;
itemHeightWidthRatio = other.itemHeightWidthRatio;
scale = other.scale;
elevation = other.elevation;
vanishOffset = other.vanishOffset;
reverse = other.reverse;
}
SavedState(Parcel in) {
scrollOffset = in.readInt();
childLayoutOffsetInput = in.readInt();
orientation = in.readInt();
itemHeightWidthRatio = in.readFloat();
scale = in.readFloat();
elevation = in.readFloat();
vanishOffset = in.readFloat();
reverse = in.readInt() == 1;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(scrollOffset);
dest.writeInt(childLayoutOffsetInput);
dest.writeInt(orientation);
dest.writeFloat(itemHeightWidthRatio);
dest.writeFloat(scale);
dest.writeFloat(elevation);
dest.writeFloat(vanishOffset);
dest.writeInt(reverse ? 1 : 0);
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<SavedState> CREATOR
= new Creator<SavedState>() {
#Override
public LadderLayoutManager.SavedState createFromParcel(Parcel in) {
return new LadderLayoutManager.SavedState(in);
}
#Override
public LadderLayoutManager.SavedState[] newArray(int size) {
return new LadderLayoutManager.SavedState[size];
}
};
}
public interface ChildDecorateHelper {
void decorateChild(View child, float posOffsetPercent, float layoutPercent, boolean isBottom);
}
public static class DefaultChildDecorateHelper implements ChildDecorateHelper {
private float mElevation;
public DefaultChildDecorateHelper(float maxElevation) {
mElevation = maxElevation;
}
#Override
public void decorateChild(View child, float posOffsetPercent, float layoutPercent, boolean isBottom) {
ViewCompat.setElevation(child, (float) (layoutPercent * mElevation * 0.7 + mElevation * 0.3));
}
}
}
Here is my code for snaphelper
public class LadderSimpleSnapHelper extends SnapHelper {
private int mDirection;
//int position = layoutManager.getPosition(centerView);
#Override
public int[] calculateDistanceToFinalSnap(
#NonNull RecyclerView.LayoutManager layoutManager, #NonNull View targetView) {
if (layoutManager instanceof LadderLayoutManager) {
int[] out = new int[2];
if (layoutManager.canScrollHorizontally()) {
out[0] = ((LadderLayoutManager) layoutManager).calculateDistanceToPosition(
layoutManager.getPosition(targetView));
out[1] = 0;
} else {
out[0] = 0;
out[1] = ((LadderLayoutManager) layoutManager).calculateDistanceToPosition(
layoutManager.getPosition(targetView));
}
return out;
}
return null;
}
#Override
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX,
int velocityY) {
if (layoutManager.canScrollHorizontally()) {
mDirection = velocityX;
} else {
mDirection = velocityY;
}
return RecyclerView.NO_POSITION;
}
#Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
if (layoutManager instanceof LadderLayoutManager) {
int pos = ((LadderLayoutManager) layoutManager).getFixedScrollPosition(
mDirection, mDirection != 0 ? 0.8f : 0.5f);
mDirection = 0;
if (pos != RecyclerView.NO_POSITION) {
return layoutManager.findViewByPosition(pos);
}
}
return null;
}
}

try below code :
mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true));
mRecyclerView.setReverseLayout(true);

Use the code below:
mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true));

in xml:
right to left
android:layoutDirection="rtl"
left to right
android:layoutDirection="ltr"

Related

RecyclerView ItemTouchHelper BOTH Buttons on Swipe

Following this question
RecyclerView ItemTouchHelper Buttons on Swipe
(NOT THE SAME). I would like to add buttons on each side.
The official answer from that link is for ONE side only.
What I have tried:
public abstract class SwipeHelper extends ItemTouchHelper.SimpleCallback {
public static final int BUTTON_WIDTH = 125;
private RecyclerView recyclerView;
private List<UnderlayButton> buttons;
private GestureDetector gestureDetector;
private GestureDetector gestureDetector2;
private int swipedPos = -1;
private float swipeThreshold = 0.5f;
private Map<Integer, List<UnderlayButton>> buttonsBuffer;
private Queue<Integer> recoverQueue;
private GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener(){
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
for (UnderlayButton button : buttons){
if(button.onClick(e.getX(), e.getY()))
break;
}
return true;
}
};
private GestureDetector.SimpleOnGestureListener gestureListener2 = new GestureDetector.SimpleOnGestureListener(){
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
for (UnderlayButton button : buttons){
if(button.onClick2(e.getX(), e.getY()))
break;
}
return true;
}
};
private View.OnTouchListener onTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent e) {
if (swipedPos < 0) return false;
Point point = new Point((int) e.getRawX(), (int) e.getRawY());
RecyclerView.ViewHolder swipedViewHolder = recyclerView.findViewHolderForAdapterPosition(swipedPos);
View swipedItem = swipedViewHolder.itemView;
Rect rect = new Rect();
swipedItem.getGlobalVisibleRect(rect);
if (e.getAction() == MotionEvent.ACTION_DOWN || e.getAction() == MotionEvent.ACTION_UP ||e.getAction() == MotionEvent.ACTION_MOVE) {
if (rect.top < point.y && rect.bottom > point.y) {
gestureDetector.onTouchEvent(e);
gestureDetector2.onTouchEvent(e);
}
//else if(rect.top > point.y && rect.bottom < point.y){ }
else {
recoverQueue.add(swipedPos);
swipedPos = -1;
recoverSwipedItem();
}
}
return false;
}
};
public SwipeHelper(Context context, RecyclerView recyclerView) {
super(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
this.recyclerView = recyclerView;
this.buttons = new ArrayList<>();
this.gestureDetector = new GestureDetector(context, gestureListener);
this.gestureDetector2 = new GestureDetector(context, gestureListener2);
this.recyclerView.setOnTouchListener(onTouchListener);
buttonsBuffer = new HashMap<>();
recoverQueue = new LinkedList<Integer>(){
#Override
public boolean add(Integer o) {
if (contains(o))
return false;
else
return super.add(o);
}
};
attachSwipe();
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int pos = viewHolder.getAdapterPosition();
if (swipedPos != pos)
recoverQueue.add(swipedPos);
swipedPos = pos;
if (buttonsBuffer.containsKey(swipedPos))
buttons = buttonsBuffer.get(swipedPos);
else
buttons.clear();
buttonsBuffer.clear();
swipeThreshold = 0.5f * buttons.size() * BUTTON_WIDTH;
recoverSwipedItem();
}
#Override
public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
return swipeThreshold;
}
#Override
public float getSwipeEscapeVelocity(float defaultValue) {
return 0.1f * defaultValue;
}
#Override
public float getSwipeVelocityThreshold(float defaultValue) {
return 5.0f * defaultValue;
}
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
int pos = viewHolder.getAdapterPosition();
float translationX = dX;
View itemView = viewHolder.itemView;
if (pos < 0){
swipedPos = pos;
return;
}
if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE){
if(dX < 0) {
List<UnderlayButton> buffer = new ArrayList<>();
if (!buttonsBuffer.containsKey(pos)){
instantiateUnderlayButton(viewHolder, buffer);
buttonsBuffer.put(pos, buffer);
}
else {
buffer = buttonsBuffer.get(pos);
}
translationX = dX * buffer.size() * BUTTON_WIDTH / itemView.getWidth();
drawButtons(c, itemView, buffer, pos, translationX);
}
if(dX > 0) {
List<UnderlayButton> buffer = new ArrayList<>();
if (!buttonsBuffer.containsKey(pos)){
instantiateUnderlayButton(viewHolder, buffer);
buttonsBuffer.put(pos, buffer);
}
else {
buffer = buttonsBuffer.get(pos);
}
translationX = dX * buffer.size() * BUTTON_WIDTH / itemView.getWidth();
drawButtons(c, itemView, buffer, pos, translationX);
}
}
super.onChildDraw(c, recyclerView, viewHolder, translationX, dY, actionState, isCurrentlyActive);
}
private synchronized void recoverSwipedItem(){
while (!recoverQueue.isEmpty()){
int pos = recoverQueue.poll();
if (pos > -1) {
recyclerView.getAdapter().notifyItemChanged(pos);
}
}
}
private void drawButtons(Canvas c, View itemView, List<UnderlayButton> buffer, int pos, float dX) {
float right = itemView.getRight();
float left = itemView.getLeft();
float dButtonWidth = (-1) * dX / buffer.size();
for (UnderlayButton button : buffer) {
if (dX < 0) {
left = right - dButtonWidth;
button.onDraw(
c,
new RectF(
left,
itemView.getTop(),
right,
itemView.getBottom()
),
pos, dX
);
right = left;
} else if (dX > 0) {
right = left - dButtonWidth;
button.onDraw(c,
new RectF(
right,
itemView.getTop(),
left,
itemView.getBottom()
), pos, dX
);
left = right;
}
}
}
public void attachSwipe(){
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(this);
itemTouchHelper.attachToRecyclerView(recyclerView);
}
public abstract void instantiateUnderlayButton(RecyclerView.ViewHolder viewHolder, List<UnderlayButton> underlayButtons);
public static class UnderlayButton {
private String text;
private int imageResId;
private int color;
private UnderlayButtonClickListener clickListener;
private String text2;
private int imageResId2;
private int color2;
private UnderlayButtonClickListener2 clickListener2;
private RectF clickRegion;
private RectF clickRegion2;
private int pos;
public UnderlayButton(String text, int imageResId, int color, UnderlayButtonClickListener clickListener, String text2, int imageResId2, int color2, UnderlayButtonClickListener2 clickListener2) {
this.text = text;
this.imageResId = imageResId;
this.color = color;
this.clickListener = clickListener;
this.text2 = text2;
this.imageResId2 = imageResId2;
this.color2 = color2;
this.clickListener2 = clickListener2;
}
public boolean onClick(float x, float y){
if (clickRegion != null && clickRegion.contains(x, y)){
clickListener.onClick(pos);
return true;
}
return false;
}
public boolean onClick2(float x, float y){
if (clickRegion2 != null && clickRegion2.contains(x, y)){
clickListener2.onClick(pos);
return true;
}
return false;
}
public void onDraw(Canvas c, RectF rect, int pos, float dX) {
Paint p = new Paint();
// Draw background
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (dX < 0) {
//Boton derecho
c.drawRect(rect, p);
p.setColor(color);
}
else{
//Boton Izquierdo
p.setColor(color2);
c.drawRect(rect, p);
}
clickRegion = rect;
clickRegion2 = rect;
this.pos = pos;
}
}
}
public interface UnderlayButtonClickListener {
void onClick(int pos);
}
public interface UnderlayButtonClickListener2 {
void onClick(int pos);
}}
I tried adding more parameters to the "UnderlayButton" class because before I tried to use the two classes separately but it was throwing me problems with the OnClickListener.
So the best way I found was to add extra parameters in the "UnderlayButton" class. Still, it doesn't work for me.
Could someone help me please.

Trouble getting order of Image Bitmap layers in Android correct

I have a piece of code that compares to images and places a marker on the difference. So far it works well, except the latest marker layer that is added always shows underneath all the older markers. I have the latest one as a yellow color and the older ones as red. When the difference is close to one of the red markers, the yellow marker shows behind those ones.
Is there anyone that can help me get the yellow (Latest marker) to appear on top?
This is my code so far:
public class CheckmarkActivity extends AppCompatActivity implements ZoomLayout.OnZoomableLayoutClickEventListener {
TextView tv;
RelativeLayout relativeLayout_work;
ImageView imageViewtest;
Bitmap prevBmp = null;
Timer t;
TimerTask task;
int time = 100;
float image_Width;
float image_Height;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_checkmark);
if (getResources().getBoolean(R.bool.is_tablet)) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
tv = findViewById(R.id.tv);
relativeLayout_work = findViewById(R.id.relativeLayout_work);
imageViewtest = findViewById(R.id.imageViewtest);
prevBmp = ViewcontrollerActivity.workSession.getLastScreenShot();
if (prevBmp == null || ViewcontrollerActivity.workSession.workScreenShot == null) {
setResult(Activity.RESULT_CANCELED);
finish();
}
startTimer();
}
// image compare
class TestAsync extends AsyncTask<Object, Integer, String>
{
String TAG = getClass().getSimpleName();
PointF p;
Bitmap test_3;
protected void onPreExecute (){
super.onPreExecute();
Log.d(TAG + " PreExceute","On pre Exceute......");
}
protected String doInBackground(Object...arg0) {
test_3 = ImageHelper.findDifference(CheckmarkActivity.this, prevBmp, ViewcontrollerActivity.workSession.workScreenShot);
p = ImageHelper.findShot(test_3);
time = 1;
return "You are at PostExecute";
}
protected void onProgressUpdate(Integer...a){
super.onProgressUpdate(a);
}
protected void onPostExecute(String result) {
super.onPostExecute(result);
addImageToImageview();
PointF np = Session.convertPointBitmap2View(p, relativeLayout_work, ViewcontrollerActivity.workSession.workScreenShot);
tv.setX(np.x - tv.getWidth() / 2);
tv.setY(np.y - tv.getHeight() / 2);
tv.setVisibility(View.VISIBLE);
// imageViewtest.setImageBitmap(test_3);
}
}
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
#Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i("OpenCV", "OpenCV loaded successfully");
new TestAsync().execute();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
#Override
protected void onResume() {
super.onResume();
if (!OpenCVLoader.initDebug()) {
Log.d("OpenCV", "Internal OpenCV library not found. Using OpenCV Manager for initialization");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
} else {
Log.d("OpenCV", "OpenCV library found inside package. Using it!");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
}
public static int[] getBitmapOffset(ImageView img, Boolean includeLayout) {
int[] offset = new int[2];
float[] values = new float[9];
Matrix m = img.getImageMatrix();
m.getValues(values);
offset[0] = (int) values[5];
offset[1] = (int) values[2];
if (includeLayout) {
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) img.getLayoutParams();
int paddingTop = (int) (img.getPaddingTop() );
int paddingLeft = (int) (img.getPaddingLeft() );
offset[0] += paddingTop + lp.topMargin;
offset[1] += paddingLeft + lp.leftMargin;
}
return offset;
}
public static int[] getBitmapPositionInsideImageView(ImageView imageView) {
int[] ret = new int[4];
if (imageView == null || imageView.getDrawable() == null)
return ret;
// Get image dimensions
// Get image matrix values and place them in an array
float[] f = new float[9];
imageView.getImageMatrix().getValues(f);
// Extract the scale values using the constants (if aspect ratio maintained, scaleX == scaleY)
final float scaleX = f[Matrix.MSCALE_X];
final float scaleY = f[Matrix.MSCALE_Y];
// Get the drawable (could also get the bitmap behind the drawable and getWidth/getHeight)
final Drawable d = imageView.getDrawable();
final int origW = d.getIntrinsicWidth();
final int origH = d.getIntrinsicHeight();
// Calculate the actual dimensions
final int actW = Math.round(origW * scaleX);
final int actH = Math.round(origH * scaleY);
ret[2] = actW;
ret[3] = actH;
// Get image position
// We assume that the image is centered into ImageView
int imgViewW = imageView.getWidth();
int imgViewH = imageView.getHeight();
int top = (int) (imgViewH - actH)/2;
int left = (int) (imgViewW - actW)/2;
ret[0] = left;
ret[1] = top;
return ret;
}
private void addImageToImageview(){
if (ViewcontrollerActivity.workSession.workScreenShot != null) {
imageViewtest.setImageBitmap(ViewcontrollerActivity.workSession.workScreenShot);
Log.d("width", String.valueOf(imageViewtest.getWidth()));
}
Resources r = getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, r.getDisplayMetrics());
for (int i = 0; i < ViewcontrollerActivity.workSession.getShotCount(); i++) {
PointF p = ViewcontrollerActivity.workSession.getPoint(i);
TextView t = new TextView(this);
t.setText("" + (i + 1));
RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams((int)px, (int)px);
relativeLayout_work.addView(t);
t.setLayoutParams(param);
t.setGravity(Gravity.CENTER);
t.setBackgroundResource(R.drawable.circle);
p = Session.convertPointBitmap2View(p, relativeLayout_work, ViewcontrollerActivity.workSession.workScreenShot);
t.setX(p.x);
t.setY(p.y);
t.setTag(10000 + i);
}
}
public void onConfirm(View v){
View vv = findViewById(R.id.relativeLayout_work);
PointF bp = Session.convertPointView2Bitmap(new PointF(tv.getX(), tv.getY()), relativeLayout_work, ViewcontrollerActivity.workSession.workScreenShot);
ViewcontrollerActivity.workSession.addNewShot(ViewcontrollerActivity.workSession.workScreenShot, bp);
setResult(Activity.RESULT_OK);
finish();
}
public void onCancel(View v){
setResult(Activity.RESULT_CANCELED);
finish();
}
#Override
public void onBackPressed() {
setResult(Activity.RESULT_CANCELED);
finish();
}
#Override
public void OnContentClickEvent(int action, float xR, float yR) {
int[] offset = new int[2];
int[] rect = new int[4];
offset = this.getBitmapOffset(imageViewtest, false);
int original_width = imageViewtest.getDrawable().getIntrinsicWidth();
int original_height = imageViewtest.getDrawable().getIntrinsicHeight();
rect = getBitmapPositionInsideImageView(imageViewtest);
Log.i("OffsetY", String.valueOf(offset[0]));
Log.i("OffsetX", String.valueOf(offset[1]));
Log.i( "0", String.valueOf(rect[0]));
Log.i( "1", String.valueOf(rect[1]));
Log.i( "2", String.valueOf(rect[2]));
Log.i( "3", String.valueOf(rect[3]));
if (xR > rect[0] && xR < rect[0] + rect[2] && yR > rect[1] && yR < rect[1] + rect[3]) {
tv.setX(xR - tv.getWidth() / 2);
tv.setY(yR - tv.getHeight() / 2);
}
// tv.setX(xR - tv.getWidth() / 2);
// tv.setY(yR - tv.getHeight() / 2);
}
public void onMoveButtonPressed(View v) {
ImageButton b = (ImageButton)v;
int mId = b.getId();
switch (mId) {
case R.id.imageButtonL:
tv.setX(tv.getX() - 1);
break;
case R.id.imageButtonR:
tv.setX(tv.getX() + 1);
break;
case R.id.imageButtonD:
tv.setY(tv.getY() + 1);
break;
case R.id.imageButtonU:
tv.setY(tv.getY() - 1);
break;
}
}
//timer change image
public void startTimer(){
t = new Timer();
task = new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (time == 1){
imageViewtest.setImageBitmap(ViewcontrollerActivity.workSession.workScreenShot);
// tv.setVisibility(View.VISIBLE);
tv.setText("" + (ViewcontrollerActivity.workSession.getShotCount() + 1));
t.cancel();
return;
}
if (time % 2 == 0) {
imageViewtest.setImageBitmap(prevBmp);
}
else if(time % 2 == 1){
imageViewtest.setImageBitmap(ViewcontrollerActivity.workSession.workScreenShot);
}
time --;
}
});
}
};
t.scheduleAtFixedRate(task, 0, 500);
}
}
You can give the z-order of the child view with addView() function.
void addView (View child, int index)
ex)
private void addImageToImageview(){
if (ViewcontrollerActivity.workSession.workScreenShot != null) {
imageViewtest.setImageBitmap(ViewcontrollerActivity.workSession.workScreenShot);
Log.d("width", String.valueOf(imageViewtest.getWidth()));
}
Resources r = getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, r.getDisplayMetrics());
int currChildrenCount = relativeLayout_work.getChildCount();
for (int i = 0; i < ViewcontrollerActivity.workSession.getShotCount(); i++) {
PointF p = ViewcontrollerActivity.workSession.getPoint(i);
TextView t = new TextView(this);
t.setText("" + (i + 1));
RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams((int)px, (int)px);
relativeLayout_work.addView(t, currChildrenCount+i); // You can control the order like this
t.setLayoutParams(param);
t.setGravity(Gravity.CENTER);
t.setBackgroundResource(R.drawable.circle);
p = Session.convertPointBitmap2View(p, relativeLayout_work, ViewcontrollerActivity.workSession.workScreenShot);
t.setX(p.x);
t.setY(p.y);
t.setTag(10000 + i);
}
}

RecyclerView make item span multiple rows [duplicate]

I have a collection of photos, and I'm using a RecyclerView to display them. I want to have the first element in my RecyclerView span 2 columns AND 2 rows:
I know I can span 2 columns with setSpanSizeLookup:
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3);
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
if (position == 0) {
return 2;
} else {
return 1;
}
}
});
but how can I also make the first item span 2 rows as well?
I have tried setting the first item's height to be different by inflating a different layout with double the height of the others, but that resulted in every item on the same row as the first item also being stretched to that height:
#Override
public ProfilePicViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView;
if (viewType == TYPE_MAIN_PHOTO) {
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_view_main_profile_photo, parent, false);
} else {
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_view_profile_photo, parent, false);
}
return new ProfilePicViewHolder(itemView);
}
You cannot achieve this behavior with GridLayoutManager, because it only supports spanning multiple columns.
Nick Butcher is currently implementing a custom SpannedGridLayoutManager that does exactly what you want. It allows you to span multiple rows and columns at the same time. The implementation is still WIP, but already works quite well.
SpannedGridLayoutManager.java
package io.plaidapp.ui.recyclerview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.PointF;
import android.graphics.Rect;
import android.support.annotation.Keep;
import android.support.annotation.NonNull;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
import io.plaidapp.R;
/**
* A {#link RecyclerView.LayoutManager} which displays a regular grid (i.e. all cells are the same
* size) and allows simultaneous row & column spanning.
*/
public class SpannedGridLayoutManager extends RecyclerView.LayoutManager {
private GridSpanLookup spanLookup;
private int columns = 1;
private float cellAspectRatio = 1f;
private int cellHeight;
private int[] cellBorders;
private int firstVisiblePosition;
private int lastVisiblePosition;
private int firstVisibleRow;
private int lastVisibleRow;
private boolean forceClearOffsets;
private SparseArray<GridCell> cells;
private List<Integer> firstChildPositionForRow; // key == row, val == first child position
private int totalRows;
private final Rect itemDecorationInsets = new Rect();
public SpannedGridLayoutManager(GridSpanLookup spanLookup, int columns, float cellAspectRatio) {
this.spanLookup = spanLookup;
this.columns = columns;
this.cellAspectRatio = cellAspectRatio;
setAutoMeasureEnabled(true);
}
#Keep /* XML constructor, see RecyclerView#createLayoutManager */
public SpannedGridLayoutManager(
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.SpannedGridLayoutManager, defStyleAttr, defStyleRes);
columns = a.getInt(R.styleable.SpannedGridLayoutManager_spanCount, 1);
parseAspectRatio(a.getString(R.styleable.SpannedGridLayoutManager_aspectRatio));
// TODO use this!
int orientation = a.getInt(
R.styleable.SpannedGridLayoutManager_android_orientation, RecyclerView.VERTICAL);
a.recycle();
setAutoMeasureEnabled(true);
}
public interface GridSpanLookup {
SpanInfo getSpanInfo(int position);
}
public void setSpanLookup(#NonNull GridSpanLookup spanLookup) {
this.spanLookup = spanLookup;
}
public static class SpanInfo {
public int columnSpan;
public int rowSpan;
public SpanInfo(int columnSpan, int rowSpan) {
this.columnSpan = columnSpan;
this.rowSpan = rowSpan;
}
public static final SpanInfo SINGLE_CELL = new SpanInfo(1, 1);
}
public static class LayoutParams extends RecyclerView.LayoutParams {
int columnSpan;
int rowSpan;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.MarginLayoutParams source) {
super(source);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(RecyclerView.LayoutParams source) {
super(source);
}
}
#Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
calculateWindowSize();
calculateCellPositions(recycler, state);
if (state.getItemCount() == 0) {
detachAndScrapAttachedViews(recycler);
firstVisibleRow = 0;
resetVisibleItemTracking();
return;
}
// TODO use orientationHelper
int startTop = getPaddingTop();
int scrollOffset = 0;
if (forceClearOffsets) { // see #scrollToPosition
startTop = -(firstVisibleRow * cellHeight);
forceClearOffsets = false;
} else if (getChildCount() != 0) {
scrollOffset = getDecoratedTop(getChildAt(0));
startTop = scrollOffset - (firstVisibleRow * cellHeight);
resetVisibleItemTracking();
}
detachAndScrapAttachedViews(recycler);
int row = firstVisibleRow;
int availableSpace = getHeight() - scrollOffset;
int lastItemPosition = state.getItemCount() - 1;
while (availableSpace > 0 && lastVisiblePosition < lastItemPosition) {
availableSpace -= layoutRow(row, startTop, recycler, state);
row = getNextSpannedRow(row);
}
layoutDisappearingViews(recycler, state, startTop);
}
#Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
#Override
public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
return new LayoutParams(c, attrs);
}
#Override
public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
if (lp instanceof ViewGroup.MarginLayoutParams) {
return new LayoutParams((ViewGroup.MarginLayoutParams) lp);
} else {
return new LayoutParams(lp);
}
}
#Override
public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
return lp instanceof LayoutParams;
}
#Override
public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) {
removeAllViews();
reset();
}
#Override
public boolean supportsPredictiveItemAnimations() {
return true;
}
#Override
public boolean canScrollVertically() {
return true;
}
#Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state){
if (getChildCount() == 0 || dy == 0) return 0;
int scrolled;
int top = getDecoratedTop(getChildAt(0));
if (dy < 0) { // scrolling content down
if (firstVisibleRow == 0) { // at top of content
int scrollRange = -(getPaddingTop() - top);
scrolled = Math.max(dy, scrollRange);
} else {
scrolled = dy;
}
if (top - scrolled >= 0) { // new top row came on screen
int newRow = firstVisibleRow - 1;
if (newRow >= 0) {
int startOffset = top - (firstVisibleRow * cellHeight);
layoutRow(newRow, startOffset, recycler, state);
}
}
int firstPositionOfLastRow = getFirstPositionInSpannedRow(lastVisibleRow);
int lastRowTop = getDecoratedTop(
getChildAt(firstPositionOfLastRow - firstVisiblePosition));
if (lastRowTop - scrolled > getHeight()) { // last spanned row scrolled out
recycleRow(lastVisibleRow, recycler, state);
}
} else { // scrolling content up
int bottom = getDecoratedBottom(getChildAt(getChildCount() - 1));
if (lastVisiblePosition == getItemCount() - 1) { // is at end of content
int scrollRange = Math.max(bottom - getHeight() + getPaddingBottom(), 0);
scrolled = Math.min(dy, scrollRange);
} else {
scrolled = dy;
}
if ((bottom - scrolled) < getHeight()) { // new row scrolled in
int nextRow = lastVisibleRow + 1;
if (nextRow < getSpannedRowCount()) {
int startOffset = top - (firstVisibleRow * cellHeight);
layoutRow(nextRow, startOffset, recycler, state);
}
}
int lastPositionInRow = getLastPositionInSpannedRow(firstVisibleRow, state);
int bottomOfFirstRow =
getDecoratedBottom(getChildAt(lastPositionInRow - firstVisiblePosition));
if (bottomOfFirstRow - scrolled < 0) { // first spanned row scrolled out
recycleRow(firstVisibleRow, recycler, state);
}
}
offsetChildrenVertical(-scrolled);
return scrolled;
}
#Override
public void scrollToPosition(int position) {
if (position >= getItemCount()) position = getItemCount() - 1;
firstVisibleRow = getRowIndex(position);
resetVisibleItemTracking();
forceClearOffsets = true;
removeAllViews();
requestLayout();
}
#Override
public void smoothScrollToPosition(
RecyclerView recyclerView, RecyclerView.State state, int position) {
if (position >= getItemCount()) position = getItemCount() - 1;
LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()) {
#Override
public PointF computeScrollVectorForPosition(int targetPosition) {
final int rowOffset = getRowIndex(targetPosition) - firstVisibleRow;
return new PointF(0, rowOffset * cellHeight);
}
};
scroller.setTargetPosition(position);
startSmoothScroll(scroller);
}
#Override
public int computeVerticalScrollRange(RecyclerView.State state) {
// TODO update this to incrementally calculate
if (firstChildPositionForRow == null) return 0;
return getSpannedRowCount() * cellHeight + getPaddingTop() + getPaddingBottom();
}
#Override
public int computeVerticalScrollExtent(RecyclerView.State state) {
return getHeight();
}
#Override
public int computeVerticalScrollOffset(RecyclerView.State state) {
if (getChildCount() == 0) return 0;
return getPaddingTop() + (firstVisibleRow * cellHeight) - getDecoratedTop(getChildAt(0));
}
#Override
public View findViewByPosition(int position) {
if (position < firstVisiblePosition || position > lastVisiblePosition) return null;
return getChildAt(position - firstVisiblePosition);
}
public int getFirstVisibleItemPosition() {
return firstVisiblePosition;
}
private static class GridCell {
final int row;
final int rowSpan;
final int column;
final int columnSpan;
GridCell(int row, int rowSpan, int column, int columnSpan) {
this.row = row;
this.rowSpan = rowSpan;
this.column = column;
this.columnSpan = columnSpan;
}
}
/**
* This is the main layout algorithm, iterates over all items and places them into [column, row]
* cell positions. Stores this layout info for use later on. Also records the adapter position
* that each row starts at.
* <p>
* Note that if a row is spanned, then the row start position is recorded as the first cell of
* the row that the spanned cell starts in. This is to ensure that we have sufficient contiguous
* views to layout/draw a spanned row.
*/
private void calculateCellPositions(RecyclerView.Recycler recycler, RecyclerView.State state) {
final int itemCount = state.getItemCount();
cells = new SparseArray<>(itemCount);
firstChildPositionForRow = new ArrayList<>();
int row = 0;
int column = 0;
recordSpannedRowStartPosition(row, column);
int[] rowHWM = new int[columns]; // row high water mark (per column)
for (int position = 0; position < itemCount; position++) {
SpanInfo spanInfo;
int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(position);
if (adapterPosition != RecyclerView.NO_POSITION) {
spanInfo = spanLookup.getSpanInfo(adapterPosition);
} else {
// item removed from adapter, retrieve its previous span info
// as we can't get from the lookup (adapter)
spanInfo = getSpanInfoFromAttachedView(position);
}
if (spanInfo.columnSpan > columns) {
spanInfo.columnSpan = columns; // or should we throw?
}
// check horizontal space at current position else start a new row
// note that this may leave gaps in the grid; we don't backtrack to try and fit
// subsequent cells into gaps. We place the responsibility on the adapter to provide
// continuous data i.e. that would not span column boundaries to avoid gaps.
if (column + spanInfo.columnSpan > columns) {
row++;
recordSpannedRowStartPosition(row, position);
column = 0;
}
// check if this cell is already filled (by previous spanning cell)
while (rowHWM[column] > row) {
column++;
if (column + spanInfo.columnSpan > columns) {
row++;
recordSpannedRowStartPosition(row, position);
column = 0;
}
}
// by this point, cell should fit at [column, row]
cells.put(position, new GridCell(row, spanInfo.rowSpan, column, spanInfo.columnSpan));
// update the high water mark book-keeping
for (int columnsSpanned = 0; columnsSpanned < spanInfo.columnSpan; columnsSpanned++) {
rowHWM[column + columnsSpanned] = row + spanInfo.rowSpan;
}
// if we're spanning rows then record the 'first child position' as the first item
// *in the row the spanned item starts*. i.e. the position might not actually sit
// within the row but it is the earliest position we need to render in order to fill
// the requested row.
if (spanInfo.rowSpan > 1) {
int rowStartPosition = getFirstPositionInSpannedRow(row);
for (int rowsSpanned = 1; rowsSpanned < spanInfo.rowSpan; rowsSpanned++) {
int spannedRow = row + rowsSpanned;
recordSpannedRowStartPosition(spannedRow, rowStartPosition);
}
}
// increment the current position
column += spanInfo.columnSpan;
}
totalRows = rowHWM[0];
for (int i = 1; i < rowHWM.length; i++) {
if (rowHWM[i] > totalRows) {
totalRows = rowHWM[i];
}
}
}
private SpanInfo getSpanInfoFromAttachedView(int position) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (position == getPosition(child)) {
LayoutParams lp = (LayoutParams) child.getLayoutParams();
return new SpanInfo(lp.columnSpan, lp.rowSpan);
}
}
// errrrr?
return SpanInfo.SINGLE_CELL;
}
private void recordSpannedRowStartPosition(final int rowIndex, final int position) {
if (getSpannedRowCount() < (rowIndex + 1)) {
firstChildPositionForRow.add(position);
}
}
private int getRowIndex(final int position) {
return position < cells.size() ? cells.get(position).row : -1;
}
private int getSpannedRowCount() {
return firstChildPositionForRow.size();
}
private int getNextSpannedRow(int rowIndex) {
int firstPositionInRow = getFirstPositionInSpannedRow(rowIndex);
int nextRow = rowIndex + 1;
while (nextRow < getSpannedRowCount()
&& getFirstPositionInSpannedRow(nextRow) == firstPositionInRow) {
nextRow++;
}
return nextRow;
}
private int getFirstPositionInSpannedRow(int rowIndex) {
return firstChildPositionForRow.get(rowIndex);
}
private int getLastPositionInSpannedRow(final int rowIndex, RecyclerView.State state) {
int nextRow = getNextSpannedRow(rowIndex);
return (nextRow != getSpannedRowCount()) ? // check if reached boundary
getFirstPositionInSpannedRow(nextRow) - 1
: state.getItemCount() - 1;
}
/**
* Lay out a given 'row'. We might actually add more that one row if the requested row contains
* a row-spanning cell. Returns the pixel height of the rows laid out.
* <p>
* To simplify logic & book-keeping, views are attached in adapter order, that is child 0 will
* always be the earliest position displayed etc.
*/
private int layoutRow(
int rowIndex, int startTop, RecyclerView.Recycler recycler, RecyclerView.State state) {
int firstPositionInRow = getFirstPositionInSpannedRow(rowIndex);
int lastPositionInRow = getLastPositionInSpannedRow(rowIndex, state);
boolean containsRemovedItems = false;
int insertPosition = (rowIndex < firstVisibleRow) ? 0 : getChildCount();
for (int position = firstPositionInRow;
position <= lastPositionInRow;
position++, insertPosition++) {
View view = recycler.getViewForPosition(position);
LayoutParams lp = (LayoutParams) view.getLayoutParams();
containsRemovedItems |= lp.isItemRemoved();
GridCell cell = cells.get(position);
addView(view, insertPosition);
// TODO use orientation helper
int wSpec = getChildMeasureSpec(
cellBorders[cell.column + cell.columnSpan] - cellBorders[cell.column],
View.MeasureSpec.EXACTLY, 0, lp.width, false);
int hSpec = getChildMeasureSpec(cell.rowSpan * cellHeight,
View.MeasureSpec.EXACTLY, 0, lp.height, true);
measureChildWithDecorationsAndMargin(view, wSpec, hSpec);
int left = cellBorders[cell.column] + lp.leftMargin;
int top = startTop + (cell.row * cellHeight) + lp.topMargin;
int right = left + getDecoratedMeasuredWidth(view);
int bottom = top + getDecoratedMeasuredHeight(view);
layoutDecorated(view, left, top, right, bottom);
lp.columnSpan = cell.columnSpan;
lp.rowSpan = cell.rowSpan;
}
if (firstPositionInRow < firstVisiblePosition) {
firstVisiblePosition = firstPositionInRow;
firstVisibleRow = getRowIndex(firstVisiblePosition);
}
if (lastPositionInRow > lastVisiblePosition) {
lastVisiblePosition = lastPositionInRow;
lastVisibleRow = getRowIndex(lastVisiblePosition);
}
if (containsRemovedItems) return 0; // don't consume space for rows with disappearing items
GridCell first = cells.get(firstPositionInRow);
GridCell last = cells.get(lastPositionInRow);
return (last.row + last.rowSpan - first.row) * cellHeight;
}
/**
* Remove and recycle all items in this 'row'. If the row includes a row-spanning cell then all
* cells in the spanned rows will be removed.
*/
private void recycleRow(
int rowIndex, RecyclerView.Recycler recycler, RecyclerView.State state) {
int firstPositionInRow = getFirstPositionInSpannedRow(rowIndex);
int lastPositionInRow = getLastPositionInSpannedRow(rowIndex, state);
int toRemove = lastPositionInRow;
while (toRemove >= firstPositionInRow) {
int index = toRemove - firstVisiblePosition;
removeAndRecycleViewAt(index, recycler);
toRemove--;
}
if (rowIndex == firstVisibleRow) {
firstVisiblePosition = lastPositionInRow + 1;
firstVisibleRow = getRowIndex(firstVisiblePosition);
}
if (rowIndex == lastVisibleRow) {
lastVisiblePosition = firstPositionInRow - 1;
lastVisibleRow = getRowIndex(lastVisiblePosition);
}
}
private void layoutDisappearingViews(
RecyclerView.Recycler recycler, RecyclerView.State state, int startTop) {
// TODO
}
private void calculateWindowSize() {
// TODO use OrientationHelper#getTotalSpace
int cellWidth =
(int) Math.floor((getWidth() - getPaddingLeft() - getPaddingRight()) / columns);
cellHeight = (int) Math.floor(cellWidth * (1f / cellAspectRatio));
calculateCellBorders();
}
private void reset() {
cells = null;
firstChildPositionForRow = null;
firstVisiblePosition = 0;
firstVisibleRow = 0;
lastVisiblePosition = 0;
lastVisibleRow = 0;
cellHeight = 0;
forceClearOffsets = false;
}
private void resetVisibleItemTracking() {
// maintain the firstVisibleRow but reset other state vars
// TODO make orientation agnostic
int minimumVisibleRow = getMinimumFirstVisibleRow();
if (firstVisibleRow > minimumVisibleRow) firstVisibleRow = minimumVisibleRow;
firstVisiblePosition = getFirstPositionInSpannedRow(firstVisibleRow);
lastVisibleRow = firstVisibleRow;
lastVisiblePosition = firstVisiblePosition;
}
private int getMinimumFirstVisibleRow() {
int maxDisplayedRows = (int) Math.ceil((float) getHeight() / cellHeight) + 1;
if (totalRows < maxDisplayedRows) return 0;
int minFirstRow = totalRows - maxDisplayedRows;
// adjust to spanned rows
return getRowIndex(getFirstPositionInSpannedRow(minFirstRow));
}
/* Adapted from GridLayoutManager */
private void calculateCellBorders() {
cellBorders = new int[columns + 1];
int totalSpace = getWidth() - getPaddingLeft() - getPaddingRight();
int consumedPixels = getPaddingLeft();
cellBorders[0] = consumedPixels;
int sizePerSpan = totalSpace / columns;
int sizePerSpanRemainder = totalSpace % columns;
int additionalSize = 0;
for (int i = 1; i <= columns; i++) {
int itemSize = sizePerSpan;
additionalSize += sizePerSpanRemainder;
if (additionalSize > 0 && (columns - additionalSize) < sizePerSpanRemainder) {
itemSize += 1;
additionalSize -= columns;
}
consumedPixels += itemSize;
cellBorders[i] = consumedPixels;
}
}
private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec) {
calculateItemDecorationsForChild(child, itemDecorationInsets);
RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + itemDecorationInsets.left,
lp.rightMargin + itemDecorationInsets.right);
heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + itemDecorationInsets.top,
lp.bottomMargin + itemDecorationInsets.bottom);
child.measure(widthSpec, heightSpec);
}
private int updateSpecWithExtra(int spec, int startInset, int endInset) {
if (startInset == 0 && endInset == 0) {
return spec;
}
int mode = View.MeasureSpec.getMode(spec);
if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) {
return View.MeasureSpec.makeMeasureSpec(
View.MeasureSpec.getSize(spec) - startInset - endInset, mode);
}
return spec;
}
/* Adapted from ConstraintLayout */
private void parseAspectRatio(String aspect) {
if (aspect != null) {
int colonIndex = aspect.indexOf(':');
if (colonIndex >= 0 && colonIndex < aspect.length() - 1) {
String nominator = aspect.substring(0, colonIndex);
String denominator = aspect.substring(colonIndex + 1);
if (nominator.length() > 0 && denominator.length() > 0) {
try {
float nominatorValue = Float.parseFloat(nominator);
float denominatorValue = Float.parseFloat(denominator);
if (nominatorValue > 0 && denominatorValue > 0) {
cellAspectRatio = Math.abs(nominatorValue / denominatorValue);
return;
}
} catch (NumberFormatException e) {
// Ignore
}
}
}
}
throw new IllegalArgumentException("Could not parse aspect ratio: '" + aspect + "'");
}
}
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SpannedGridLayoutManager">
<attr name="android:orientation" />
<attr name="spanCount" />
<attr name="aspectRatio" format="string" />
</declare-styleable>
</resources>
The code is also available here.
Example usage
The code requires RecyclerView 23.2.0 or higher.
So add the following line to your build.gradle, if you didn't already do so.
dependencies {
compile 'com.android.support:recyclerview-v7:24.2.1'
}
To achieve the layout shown in the initial post, we define the LayoutManager as follows
recyclerView.setLayoutManager(new SpannedGridLayoutManager(
new SpannedGridLayoutManager.GridSpanLookup() {
#Override
public SpannedGridLayoutManager.SpanInfo getSpanInfo(int position) {
if (position == 0) {
return new SpannedGridLayoutManager.SpanInfo(2, 2);
} else {
return new SpannedGridLayoutManager.SpanInfo(1, 1);
}
}
},
3 /* Three columns */,
1f /* We want our items to be 1:1 ratio */));
You can use SpannedGridLayoutManager library wrote by Arasthel in here
This is the result
You can achieve this behavior by using RecycleView for rows only, with ViewHolder for each row. So you will have RowViewHolder for simple rows and something like DoubleRowViewHolder for custom layout that will have 3 items, just the way you want.

Android: Starting intent from onClick on canvas

I recently started using a canvas over a layout because I could have animations in the background. However, I want to start a new activity after the user clicks a buttons. Here is the code so far...
public class GFX extends Activity implements View.OnTouchListener {
MyBringBack ourSurfaceView;
Paint title, play, options;
float x, y, sX, sY, fX, fY, dX, dY, aniX, aniY, scaledX, scaledY, changingRedX, changingYellowX, changingPurpleX, changingGreenX, whereIsRedXY, whereIsYellowXY, whereIsGreenXY, whereIsPurpleXY;
int whereIsRedY, redXThing, whereIsYellowY, whereIsGreenY, whereIsPurpleY, yellowXThing, greenXThing, purpleXThing, firstRun;
Bitmap green, yellow, red, purple, plus, redFixed, yellowFixed, greenFixed, purpleFixed, titleTest, playTest, playFixed;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ourSurfaceView = new MyBringBack(this);
ourSurfaceView.setOnTouchListener(this);
x = 0;
y = 0;
sX = 0;
sY = 0;
fX = 0;
fY = 0;
Context ctx;
firstRun = 0;
dX = dY = aniX = aniY = scaledX = scaledY = redXThing = 0;
ctx = this;
green = BitmapFactory.decodeResource(getResources(), R.drawable.greenhairedpotatoblack);
yellow = BitmapFactory.decodeResource(getResources(), R.drawable.yellowhairedpotatoblack);
red = BitmapFactory.decodeResource(getResources(), R.drawable.redhairedpotatoblack);
purple = BitmapFactory.decodeResource(getResources(), R.drawable.purplehairedpotatoblack);
plus = BitmapFactory.decodeResource(getResources(), R.drawable.plus);
titleTest = BitmapFactory.decodeResource(getResources(), R.drawable.title);
playTest = BitmapFactory.decodeResource(getResources(), R.drawable.play);
setContentView(ourSurfaceView);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_gfx, menu);
return true;
}
#Override
protected void onPause() {
super.onPause();
ourSurfaceView.pause();
}
#Override
protected void onResume() {
super.onResume();
ourSurfaceView.resume();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
try {
Thread.sleep(1000/60);
} catch (InterruptedException e) {
e.printStackTrace();
}
x = event.getX();
y = event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//if(y > play.getTextBounds("Play", );)
break;
}
return true;
}
public class MyBringBack extends SurfaceView implements Runnable {
SurfaceHolder ourHolder;
Thread ourThread = null;
boolean isRunning = false;
public MyBringBack(Context context) {
super(context);
ourHolder = getHolder();
}
public void pause(){
isRunning = false;
while(true){
try {
ourThread.join();
} catch (InterruptedException e){
e.printStackTrace();
}
break;
}
ourThread = null;
}
public void resume(){
isRunning = true;
ourThread = new Thread(this);
ourThread.start();
}
#Override
public void run() {
while(isRunning){
if(!ourHolder.getSurface().isValid())
continue;
Canvas canvas = ourHolder.lockCanvas();
canvas.drawColor(Color.BLACK);
if(firstRun == 0){
changingRedX = 0 - red.getWidth();
yellowXThing = 1;
changingYellowX = canvas.getWidth();
greenXThing = 1;
firstRun = 1;
title = new Paint();
title.setColor(Color.parseColor("#0889ec"));
title.setTextSize(180);
title.setTextAlign(Paint.Align.CENTER);
title.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
play = new Paint();
play.setTextSize(140);
play.setColor(Color.WHITE);
play.setTextAlign(Paint.Align.CENTER);
options = new Paint();
options.setTextSize(140);
options.setColor(Color.WHITE);
options.setTextAlign(Paint.Align.CENTER);
playFixed = getResizedBitmap(playTest, canvas.getWidth()/5, canvas.getHeight()/3);
redFixed = getResizedBitmap(red, canvas.getWidth()/6, (int)canvas.getWidth()/6 * (red.getHeight()/red.getWidth()));
yellowFixed = getResizedBitmap(yellow, canvas.getWidth()/6, (int)canvas.getWidth()/6 * (yellow.getHeight()/yellow.getWidth()));
greenFixed = getResizedBitmap(green, canvas.getWidth()/6, (int)canvas.getWidth()/6 * (green.getHeight()/green.getWidth()));
purpleFixed = getResizedBitmap(purple, canvas.getWidth()/6, (int)canvas.getWidth()/6 * (purple.getHeight()/purple.getWidth()));
}
if((x>(canvas.getWidth()/2)-(playFixed.getWidth()/2)&&(x<(canvas.getWidth()/2)-(playFixed.getWidth()/2) + playFixed.getWidth()))
&& ((y > 4*(canvas.getHeight()/9))&& (y<4*(canvas.getHeight()/9)+playFixed.getHeight()))){
**** HERE IS WHERE I WANT TO START THE NEW INTENT ****
}
if(whereIsRedY == 0){
whereIsRedXY = (int) (Math.random() * canvas.getHeight()) - 20;
whereIsRedY = 1;
}
if(whereIsYellowY == 0){
whereIsYellowXY = (int) (Math.random() * canvas.getHeight()) - 20;
whereIsYellowY = 1;
}
if(whereIsGreenY == 0){
whereIsGreenXY = (int) (Math.random() * canvas.getWidth()) - 20;
whereIsGreenY = 1;
}
if(whereIsPurpleY == 0){
whereIsPurpleXY = (int) (Math.random() * canvas.getWidth()) - 20;
whereIsPurpleY = 1;
}
if((changingRedX > canvas.getWidth() + redFixed.getWidth()) && redXThing == 0){
changingRedX = canvas.getWidth() + 3*(redFixed.getWidth());
whereIsRedY = 0;
redXThing = 1;
}
if((changingRedX < 0 - redFixed.getWidth()) && redXThing == 1){
changingRedX = 0 - 4*(redFixed.getWidth());
whereIsRedY = 0;
redXThing = 0;
}
if(redXThing == 0) {
changingRedX += 10;
}
if(redXThing == 1) {
changingRedX -= 10;
}
if((changingYellowX > canvas.getWidth() + yellowFixed.getWidth()) && yellowXThing == 0){
changingYellowX = canvas.getWidth() + 3*(yellowFixed.getWidth());
whereIsYellowY = 0;
yellowXThing = 1;
}
if((changingYellowX < 0 - yellowFixed.getWidth()) && yellowXThing == 1){
changingYellowX = 0 - 4*(yellowFixed.getWidth());
whereIsYellowY = 0;
yellowXThing = 0;
}
if(yellowXThing == 0) {
changingYellowX += 13;
}
if(yellowXThing == 1) {
changingYellowX -= 13;
}
if((changingGreenX > canvas.getHeight() + greenFixed.getHeight()) && greenXThing == 0){
changingGreenX = canvas.getHeight() + 3*(greenFixed.getHeight());
whereIsGreenY = 0;
greenXThing = 1;
}
if((changingGreenX < 0 - greenFixed.getHeight()) && greenXThing == 1){
changingGreenX = 0 - 4*(greenFixed.getHeight());
whereIsGreenY = 0;
greenXThing = 0;
}
if(greenXThing == 0) {
changingGreenX += 8;
}
if(greenXThing == 1) {
changingGreenX -= 8;
}
if((changingPurpleX > canvas.getHeight() + purpleFixed.getHeight()) && purpleXThing == 0){
changingPurpleX = canvas.getHeight() + 3*(purpleFixed.getHeight());
whereIsPurpleY = 0;
purpleXThing = 1;
}
if((changingPurpleX < 0 - purpleFixed.getHeight()) && purpleXThing == 1){
changingPurpleX = 0 - 4*(purpleFixed.getHeight());
whereIsPurpleY = 0;
purpleXThing = 0;
}
if(purpleXThing == 0) {
changingPurpleX += 15;
}
if(purpleXThing == 1) {
changingPurpleX -= 15;
}
canvas.drawBitmap(redFixed, changingRedX, whereIsRedXY, null);
canvas.drawBitmap(yellowFixed, changingYellowX, whereIsYellowXY, null);
canvas.drawBitmap(purpleFixed, whereIsPurpleXY, changingPurpleX, null);
canvas.drawBitmap(greenFixed, whereIsGreenXY, changingGreenX, null);
canvas.drawText("Mustache Me", (canvas.getWidth() ) / 2, (canvas.getHeight()/3), title);
//canvas.drawText("Play", canvas.getWidth()/2, canvas.getHeight()/2 + 40, play);
canvas.drawBitmap(playFixed, (canvas.getWidth()/2)-(playFixed.getWidth()/2), 4*(canvas.getHeight()/9), null);
canvas.drawText("Options", canvas.getWidth()/2, 2*(canvas.getHeight()/3), options);
ourHolder.unlockCanvasAndPost(canvas);
}
}
}
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth)
{
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;
}
}
I labeled in my code where I want to intent to start, yet I keep getting the error: "Cannot resolve constructor Intetn(.....)"
The code I tried was this:
Intent i = new Intent(this, WhichGameActivity.class);
Thanks for any help :)
Cheers.
Use this:
new Intent(GFX.this,WhichGameActivity.class);
You can't just use this because you're inside a inner class, thus this belongs to the inner class context.
Pay attention to what Docs say about Intent constructor:
public Intent (Context packageContext, Class cls)
Create an intent for a specific component. ...
Parameters
packageContext A Context of the application package implementing this
class. cls The component class that is to be used for the
intent.
Your class MyBringBack is not a Context,so you can not use this code:
Intent i = new Intent(this, WhichGameActivity.class);
You have to store context that is received in constructor of MyBringBack as a field for example "myContext" and then use it in new Intent() like this:
Intent i = new Intent(myContext, WhichGameActivity.class);

Listview with pull to refresh Scroll down to last item when adding header-view

I have created one pull to refresh listview now when I scroll or pull little bit then I have to add header view.
here my custom listview class
package com.app.refreshableList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.app.xxxxxx.R;
import com.google.android.gms.internal.el;
public class RefreshableListView extends ListView {
Boolean isScrool = false;
private View mHeaderContainer = null;
private View mHeaderView = null;
private ImageView mArrow = null;
private ProgressBar mProgress = null;
private TextView mText = null;
private float mY = 0;
private float mHistoricalY = 0;
private int mHistoricalTop = 0;
private int mInitialHeight = 0;
private boolean mFlag = false;
private boolean mArrowUp = false;
private boolean mIsRefreshing = false;
private int mHeaderHeight = 0;
private OnRefreshListener mListener = null;
private static final int REFRESH = 0;
private static final int NORMAL = 1;
private static final int HEADER_HEIGHT_DP = 62;
private static final String TAG = RefreshableListView.class.getSimpleName();
private ListViewObserver mObserver;
private View mTrackedChild;
private int mTrackedChildPrevPosition;
private int mTrackedChildPrevTop;
OnTouchListener touch;
View vHeader;
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mTrackedChild == null) {
if (getChildCount() > 0) {
mTrackedChild = getChildInTheMiddle();
mTrackedChildPrevTop = mTrackedChild.getTop();
mTrackedChildPrevPosition = getPositionForView(mTrackedChild);
}
} else {
boolean childIsSafeToTrack = mTrackedChild.getParent() == this
&& getPositionForView(mTrackedChild) == mTrackedChildPrevPosition;
if (childIsSafeToTrack) {
int top = mTrackedChild.getTop();
if (mObserver != null) {
float deltaY = top - mTrackedChildPrevTop;
mObserver.onScroll(deltaY);
}
mTrackedChildPrevTop = top;
} else {
mTrackedChild = null;
}
}
}
private View getChildInTheMiddle() {
return getChildAt(getChildCount() / 2);
}
public void setObserver(ListViewObserver observer) {
mObserver = observer;
}
public RefreshableListView(final Context context) {
super(context);
initialize();
}
public RefreshableListView(final Context context, final AttributeSet attrs) {
super(context, attrs);
initialize();
}
public RefreshableListView(final Context context, final AttributeSet attrs,
final int defStyle) {
super(context, attrs, defStyle);
initialize();
}
public void setOnRefreshListener(final OnRefreshListener l) {
mListener = l;
}
#Override
public void setOnTouchListener(OnTouchListener l) {
// TODO Auto-generated method stub
super.setOnTouchListener(l);
}
public void completeRefreshing() {
mProgress.setVisibility(View.INVISIBLE);
mArrow.setVisibility(View.VISIBLE);
mHandler.sendMessage(mHandler.obtainMessage(NORMAL, mHeaderHeight, 0));
mIsRefreshing = false;
invalidateViews();
}
#Override
public boolean onInterceptTouchEvent(final MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mHandler.removeMessages(REFRESH);
mHandler.removeMessages(NORMAL);
mY = mHistoricalY = ev.getY();
if (mHeaderContainer.getLayoutParams() != null) {
mInitialHeight = mHeaderContainer.getLayoutParams().height;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
#Override
public boolean onTouchEvent(final MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
mHistoricalTop = getChildAt(0).getTop();
break;
case MotionEvent.ACTION_UP:
if (!mIsRefreshing) {
if (mArrowUp) {
startRefreshing();
mHandler.sendMessage(mHandler.obtainMessage(REFRESH,
(int) (ev.getY() - mY) / 2 + mInitialHeight, 0));
} else {
if (getChildAt(0).getTop() == 0) {
mHandler.sendMessage(mHandler.obtainMessage(NORMAL,
(int) (ev.getY() - mY) / 2 + mInitialHeight, 0));
}
}
} else {
mHandler.sendMessage(mHandler.obtainMessage(REFRESH,
(int) (ev.getY() - mY) / 2 + mInitialHeight, 0));
}
mFlag = false;
break;
}
return super.onTouchEvent(ev);
}
#Override
public boolean dispatchTouchEvent(final MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_MOVE
&& getFirstVisiblePosition() == 0) {
float direction = ev.getY() - mHistoricalY;
int height = (int) (ev.getY() - mY) / 2 + mInitialHeight;
if (height < 0) {
height = 0;
}
float deltaY = Math.abs(mY - ev.getY());
ViewConfiguration config = ViewConfiguration.get(getContext());
if (deltaY > config.getScaledTouchSlop()) {
// Scrolling downward
if (direction > 0) {
// Refresh bar is extended if top pixel of the first item is
// visible
if (getChildAt(0) != null) {
if (getChildAt(0).getTop() == 0) {
if (mHistoricalTop < 0) {
// mY = ev.getY(); // TODO works without
// this?mHistoricalTop = 0;
}
// if (isScrool == true) {
//
// } else {
// isScrool = true;
// addHeaderView(vHeader);
//
// // Animation anim = AnimationUtils.loadAnimation(
// // getContext(), R.anim.bounce_animation);
// // startAnimation(anim);
//
// smoothScrollToPosition(getChildAt(0).getTop());
// // bottom_layout.setVisibility(View.VISIBLE);
// }
// Extends refresh bar
/*****
* commented by me on 10-09-2014
*/
setHeaderHeight(height);
// Stop list scroll to prevent the list from
// overscrolling
ev.setAction(MotionEvent.ACTION_CANCEL);
mFlag = false;
}
}
} else if (direction < 0) {
// Scrolling upward
// Refresh bar is shortened if top pixel of the first item
// is
// visible
if (getChildAt(0) != null) {
if (getChildAt(0).getTop() == 0) {
setHeaderHeight(height);
// If scroll reaches top of the list, list scroll is
// enabled
if (getChildAt(1) != null
&& getChildAt(1).getTop() <= 1 && !mFlag) {
ev.setAction(MotionEvent.ACTION_DOWN);
mFlag = true;
}
}
}
}
}
mHistoricalY = ev.getY();
}
try {
return super.dispatchTouchEvent(ev);
} catch (Exception e) {
return false;
}
}
#Override
public boolean performItemClick(final View view, final int position,
final long id) {
if (position == 0) {
// This is the refresh header element
return true;
} else {
return super.performItemClick(view, position - 1, id);
}
}
private void initialize() {
LayoutInflater inflator = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
vHeader = inflator.inflate(R.layout.search_header, null);
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mHeaderContainer = inflater.inflate(R.layout.refreshable_list_header,
null);
mHeaderView = mHeaderContainer
.findViewById(R.id.refreshable_list_header);
mArrow = (ImageView) mHeaderContainer
.findViewById(R.id.refreshable_list_arrow);
mProgress = (ProgressBar) mHeaderContainer
.findViewById(R.id.refreshable_list_progress);
mText = (TextView) mHeaderContainer
.findViewById(R.id.refreshable_list_text);
addHeaderView(mHeaderContainer);
mHeaderHeight = (int) (HEADER_HEIGHT_DP * getContext().getResources()
.getDisplayMetrics().density);
setHeaderHeight(0);
}
private void setHeaderHeight(final int height) {
if (height <= 1) {
mHeaderView.setVisibility(View.GONE);
} else {
mHeaderView.setVisibility(View.VISIBLE);
}
// Extends refresh bar
LayoutParams lp = (LayoutParams) mHeaderContainer.getLayoutParams();
if (lp == null) {
lp = new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT);
}
lp.height = height;
mHeaderContainer.setLayoutParams(lp);
// Refresh bar shows up from bottom to top
LinearLayout.LayoutParams headerLp = (LinearLayout.LayoutParams) mHeaderView
.getLayoutParams();
if (headerLp == null) {
headerLp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT);
}
headerLp.topMargin = -mHeaderHeight + height;
mHeaderView.setLayoutParams(headerLp);
if (!mIsRefreshing) {
// If scroll reaches the trigger line, start refreshing
if (height > mHeaderHeight && !mArrowUp) {
mArrow.startAnimation(AnimationUtils.loadAnimation(
getContext(), R.anim.rotate));
mText.setText("Release to update");
rotateArrow();
mArrowUp = true;
} else if (height < mHeaderHeight && mArrowUp) {
mArrow.startAnimation(AnimationUtils.loadAnimation(
getContext(), R.anim.rotate));
mText.setText("Pull down to update");
rotateArrow();
mArrowUp = false;
}else {
if (isScrool == true) {
} else {
isScrool = true;
addHeaderView(vHeader);
// Animation anim = AnimationUtils.loadAnimation(
// getContext(), R.anim.bounce_animation);
// startAnimation(anim);
smoothScrollToPosition(getChildAt(0).getTop());
// bottom_layout.setVisibility(View.VISIBLE);
}
}
}
}
private void rotateArrow() {
Drawable drawable = mArrow.getDrawable();
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.save();
canvas.rotate(180.0f, canvas.getWidth() / 2.0f,
canvas.getHeight() / 2.0f);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
drawable.draw(canvas);
canvas.restore();
mArrow.setImageBitmap(bitmap);
}
private void startRefreshing() {
mArrow.setVisibility(View.INVISIBLE);
mProgress.setVisibility(View.VISIBLE);
mText.setText("Loading...");
mIsRefreshing = true;
if (mListener != null) {
mListener.onRefresh(this);
}
}
private final Handler mHandler = new Handler() {
#Override
public void handleMessage(final Message msg) {
super.handleMessage(msg);
int limit = 0;
switch (msg.what) {
case REFRESH:
limit = mHeaderHeight;
break;
case NORMAL:
limit = 0;
break;
}
// Elastic scrolling
if (msg.arg1 >= limit) {
setHeaderHeight(msg.arg1);
int displacement = (msg.arg1 - limit) / 10;
if (displacement == 0) {
mHandler.sendMessage(mHandler.obtainMessage(msg.what,
msg.arg1 - 1, 0));
} else {
mHandler.sendMessage(mHandler.obtainMessage(msg.what,
msg.arg1 - displacement, 0));
}
}
}
};
public interface OnRefreshListener {
public void onRefresh(RefreshableListView listView);
}
public static interface ListViewObserver {
public void onScroll(float deltaY);
}
}
here in method dispatchTouchEvent(final MotionEvent ev) i have make one condition
if (isScrool == true) {
} else {
isScrool = true;
addHeaderView(vHeader);
// Animation anim =
// AnimationUtils.loadAnimation(
// getContext(), R.anim.bounce_animation);
// startAnimation(anim);
smoothScrollToPosition(getChildAt(0).getTop());
// bottom_layout.setVisibility(View.VISIBLE);
}
here addHeaderView(vHeader);
adds header but my listview scroll down to last item can anyone could tell me what's happening here?
Try to call listView.setSelectionAfterHeaderView();. This will scroll ListView to top.

Categories

Resources