How exactly does the ripple effect source code work? [closed] - java

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I recently found the following code:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
public class RippleViewCreator extends FrameLayout {
private float duration = 150;
private int frameRate = 15;
private float speed = 1;
private float radius = 0;
private Paint paint = new Paint();
private float endRadius = 0;
private float rippleX = 0;
private float rippleY = 0;
private int width = 0;
private int height = 0;
private Handler handler = new Handler();
private int touchAction;
public RippleViewCreator(Context context) {
this(context, null, 0);
}
public RippleViewCreator(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RippleViewCreator(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
if (isInEditMode())
return;
paint.setStyle(Paint.Style.FILL);
paint.setColor(getResources().getColor(R.color.control_highlight_color));
paint.setAntiAlias(true);
setWillNotDraw(true);
setDrawingCacheEnabled(true);
setClickable(true);
}
public static void addRippleToView(View v) {
ViewGroup parent = (ViewGroup) v.getParent();
int index = -1;
if (parent != null) {
index = parent.indexOfChild(v);
parent.removeView(v);
}
RippleViewCreator rippleViewCreator = new RippleViewCreator(v.getContext());
rippleViewCreator.setLayoutParams(v.getLayoutParams());
if (index == -1)
parent.addView(rippleViewCreator, index);
else
parent.addView(rippleViewCreator);
rippleViewCreator.addView(v);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
}
#Override
protected void dispatchDraw(#NonNull Canvas canvas) {
super.dispatchDraw(canvas);
if (radius > 0 && radius < endRadius) {
canvas.drawCircle(rippleX, rippleY, radius, paint);
if (touchAction == MotionEvent.ACTION_UP)
invalidate();
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return true;
}
#Override
public boolean onTouchEvent(#NonNull MotionEvent event) {
rippleX = event.getX();
rippleY = event.getY();
touchAction = event.getAction();
switch (event.getAction()) {
case MotionEvent.ACTION_UP: {
getParent().requestDisallowInterceptTouchEvent(false);
radius = 1;
endRadius = Math.max(Math.max(Math.max(width - rippleX, rippleX), rippleY), height - rippleY);
speed = endRadius / duration * frameRate;
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (radius < endRadius) {
radius += speed;
paint.setAlpha(90 - (int) (radius / endRadius * 90));
handler.postDelayed(this, frameRate);
} else if (getChildAt(0) != null) {
getChildAt(0).performClick();
}
}
}, frameRate);
break;
}
case MotionEvent.ACTION_CANCEL: {
getParent().requestDisallowInterceptTouchEvent(false);
break;
}
case MotionEvent.ACTION_DOWN: {
getParent().requestDisallowInterceptTouchEvent(true);
endRadius = Math.max(Math.max(Math.max(width - rippleX, rippleX), rippleY), height - rippleY);
paint.setAlpha(90);
radius = endRadius / 3;
invalidate();
return true;
}
case MotionEvent.ACTION_MOVE: {
if (rippleX < 0 || rippleX > width || rippleY < 0 || rippleY > height) {
getParent().requestDisallowInterceptTouchEvent(false);
touchAction = MotionEvent.ACTION_CANCEL;
break;
} else {
invalidate();
return true;
}
}
}
invalidate();
return false;
}
#Override
public final void addView(#NonNull View child, int index, ViewGroup.LayoutParams params) {
//limit one view
if (getChildCount() > 0) {
throw new IllegalStateException(this.getClass().toString() + " can only have one child.");
}
super.addView(child, index, params);
}
}
Can anyone explain to me how exactly does this ripple effect code work because I been trying to understand it for at least a day now and I still can't understand how it exactly work.
Precisely I don't understand:
public static void addRippleToView(View v) {
ViewGroup parent = (ViewGroup) v.getParent();
int index = -1;
if (parent != null) {
index = parent.indexOfChild(v);
parent.removeView(v);
}
RippleViewCreator rippleViewCreator = new RippleViewCreator(v.getContext());
rippleViewCreator.setLayoutParams(v.getLayoutParams());
if (index == -1)
parent.addView(rippleViewCreator, index);
else
parent.addView(rippleViewCreator);
rippleViewCreator.addView(v);
}

I don't know if this class actually works, but if does, it's guess like this:
This custom view subclasses FrameLayout, which means it expects to have a child view. So you would declare it in your view XML and give it a child to place within its bounds.
When it receives a touch event in onTouchEVent, it draws an animated ripple effect, which would appear behind the view it contains.
The developer may also tell this RippleViewCreator to render a ripple effect behind a specific view it contains by calling addRippleToView. It looks like this method removes the targeted child view from the view hierarchy, wraps it in yet another RippleViewCreator, and adds that back into its own view hierarchy for a ripple animation.
Crazy stuff, man.

Related

how to change text direction in OtpEditText?

I'm using this class to create custom edit text but I can't change the direction of input text
Do you have any idea how can I change it?
I already tried textDirection and LayoutDirection but doesn't work
thanks in advance
(Building a PinEntryEditText in Android)
this is the class
package com.example.jorvajoor;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.Editable;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.View;
import androidx.appcompat.widget.AppCompatEditText;
public class OTPeditText extends AppCompatEditText {
private float mSpace = 24; //24 dp by default, space between the lines
private float mNumChars = 4;
private float mLineSpacing = 8; //8dp by default, height of the text from our lines
private int mMaxLength = 5;
private float mLineStroke = 2;
private Paint mLinesPaint;
private OnClickListener mClickListener;
public OTPeditText(Context context) {
super(context);
}
public OTPeditText(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public OTPeditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
float multi = context.getResources().getDisplayMetrics().density;
mLineStroke = multi * mLineStroke;
mLinesPaint = new Paint(getPaint());
mLinesPaint.setStrokeWidth(mLineStroke);
mLinesPaint.setColor(getResources().getColor(R.color.black));
setBackgroundResource(0);
mSpace = multi * mSpace; //convert to pixels for our density
mLineSpacing = multi * mLineSpacing; //convert to pixels for our density
mNumChars = mMaxLength;
super.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// When tapped, move cursor to end of text.
setSelection(getText().length());
if (mClickListener != null) {
mClickListener.onClick(v);
}
}
});
}
#Override
public void setOnClickListener(OnClickListener l) {
mClickListener = l;
}
#Override
public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) {
throw new RuntimeException("setCustomSelectionActionModeCallback() not supported.");
}
#Override
protected void onDraw(Canvas canvas) {
int availableWidth = getWidth() - getPaddingRight() - getPaddingLeft();
float mCharSize;
if (mSpace < 0) {
mCharSize = (availableWidth / (mNumChars * 2 - 1));
} else {
mCharSize = (availableWidth - (mSpace * (mNumChars - 1))) / mNumChars;
}
int startX = getPaddingLeft();
int bottom = getHeight() - getPaddingBottom();
//Text Width
Editable text = getText();
int textLength = text.length();
float[] textWidths = new float[textLength];
getPaint().getTextWidths(getText(), 0, textLength, textWidths);
for (int i = 0; i < mNumChars; i++) {
canvas.drawLine(startX, bottom, startX + mCharSize, bottom, mLinesPaint);
if (getText().length() > i) {
float middle = startX + mCharSize / 2;
canvas.drawText(text, i, i + 1, middle - textWidths[0] / 2, bottom - mLineSpacing, getPaint());
}
if (mSpace < 0) {
startX += mCharSize * 2;
} else {
startX += mCharSize + mSpace;
}
}
}
}
and this is how I use it in XML file
<com.example.jorvajoor.OTPeditText
android:id="#+id/word1"
android:layout_width="#dimen/_200sdp"
android:layout_height="wrap_content"
android:cursorVisible="true"
android:inputType="text"
android:gravity="center"
android:letterSpacing="#integer/material_motion_duration_short_1"
android:textIsSelectable="true"
android:imeOptions="actionDone"
android:textSize="20sp"/>
Add Manifest File.
<manifest ... >
...
<application ...
android:supportsRtl="true">
</application>
</manifest>
I couldn't figure it out
but I use PinEntryEditText instead and solve my problem easily
just need to add text direction

Children match_parent ignoring parent in custom view with viewdraghelper

I have been studying custom views recently and trying to make one specific example work out from a tutorial located here: http://flavienlaurent.com/blog/2013/08/28/each-navigation-drawer-hides-a-viewdraghelper/
I will add the relevant code to prevent information loss from dead link. The author of the tutorial explains how to create a ViewDragHelper similar to YouTube's android app player behavior and these are the codes he provided
activity_main.xml
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:tag="list"
/>
<com.example.vdh.YoutubeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/youtubeLayout"
android:orientation="vertical"
android:visibility="visible">
<TextView
android:id="#+id/viewHeader"
android:layout_width="match_parent"
android:layout_height="128dp"
android:fontFamily="sans-serif-thin"
android:textSize="25sp"
android:tag="text"
android:gravity="center"
android:textColor="#android:color/white"
android:background="#AD78CC"/>
<TextView
android:id="#+id/viewDesc"
android:tag="desc"
android:textSize="35sp"
android:gravity="center"
android:text="Loreum Loreum"
android:textColor="#android:color/white"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF00FF"/>
</com.example.vdh.YoutubeLayout>
YoutubeLayout.java
public class YoutubeLayout extends ViewGroup {
private final ViewDragHelper mDragHelper;
private View mHeaderView;
private View mDescView;
private float mInitialMotionX;
private float mInitialMotionY;
private int mDragRange;
private int mTop;
private float mDragOffset;
public YoutubeLayout(Context context) {
this(context, null);
}
public YoutubeLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
#Override
protected void onFinishInflate() {
mHeaderView = findViewById(R.id.viewHeader);
mDescView = findViewById(R.id.viewDesc);
}
public YoutubeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mDragHelper = ViewDragHelper.create(this, 1f, new DragHelperCallback());
}
public void maximize() {
smoothSlideTo(0f);
}
boolean smoothSlideTo(float slideOffset) {
final int topBound = getPaddingTop();
int y = (int) (topBound + slideOffset * mDragRange);
if (mDragHelper.smoothSlideViewTo(mHeaderView, mHeaderView.getLeft(), y)) {
ViewCompat.postInvalidateOnAnimation(this);
return true;
}
return false;
}
private class DragHelperCallback extends ViewDragHelper.Callback {
#Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mHeaderView;
}
#Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
mTop = top;
mDragOffset = (float) top / mDragRange;
mHeaderView.setPivotX(mHeaderView.getWidth());
mHeaderView.setPivotY(mHeaderView.getHeight());
mHeaderView.setScaleX(1 - mDragOffset / 2);
mHeaderView.setScaleY(1 - mDragOffset / 2);
mDescView.setAlpha(1 - mDragOffset);
requestLayout();
}
#Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
int top = getPaddingTop();
if (yvel > 0 || (yvel == 0 && mDragOffset > 0.5f)) {
top += mDragRange;
}
mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top);
}
#Override
public int getViewVerticalDragRange(View child) {
return mDragRange;
}
#Override
public int clampViewPositionVertical(View child, int top, int dy) {
final int topBound = getPaddingTop();
final int bottomBound = getHeight() - mHeaderView.getHeight() - mHeaderView.getPaddingBottom();
final int newTop = Math.min(Math.max(top, topBound), bottomBound);
return newTop;
}
}
#Override
public void computeScroll() {
if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
if (( action != MotionEvent.ACTION_DOWN)) {
mDragHelper.cancel();
return super.onInterceptTouchEvent(ev);
}
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mDragHelper.cancel();
return false;
}
final float x = ev.getX();
final float y = ev.getY();
boolean interceptTap = false;
switch (action) {
case MotionEvent.ACTION_DOWN: {
mInitialMotionX = x;
mInitialMotionY = y;
interceptTap = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
break;
}
case MotionEvent.ACTION_MOVE: {
final float adx = Math.abs(x - mInitialMotionX);
final float ady = Math.abs(y - mInitialMotionY);
final int slop = mDragHelper.getTouchSlop();
if (ady > slop && adx > ady) {
mDragHelper.cancel();
return false;
}
}
}
return mDragHelper.shouldInterceptTouchEvent(ev) || interceptTap;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
mDragHelper.processTouchEvent(ev);
final int action = ev.getAction();
final float x = ev.getX();
final float y = ev.getY();
boolean isHeaderViewUnder = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
switch (action & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
mInitialMotionX = x;
mInitialMotionY = y;
break;
}
case MotionEvent.ACTION_UP: {
final float dx = x - mInitialMotionX;
final float dy = y - mInitialMotionY;
final int slop = mDragHelper.getTouchSlop();
if (dx * dx + dy * dy < slop * slop && isHeaderViewUnder) {
if (mDragOffset == 0) {
smoothSlideTo(1f);
} else {
smoothSlideTo(0f);
}
}
break;
}
}
return isHeaderViewUnder && isViewHit(mHeaderView, (int) x, (int) y) || isViewHit(mDescView, (int) x, (int) y);
}
private boolean isViewHit(View view, int x, int y) {
int[] viewLocation = new int[2];
view.getLocationOnScreen(viewLocation);
int[] parentLocation = new int[2];
this.getLocationOnScreen(parentLocation);
int screenX = parentLocation[0] + x;
int screenY = parentLocation[1] + y;
return screenX >= viewLocation[0] && screenX < viewLocation[0] + view.getWidth() &&
screenY >= viewLocation[1] && screenY < viewLocation[1] + view.getHeight();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mDragRange = getHeight() - mHeaderView.getHeight();
mHeaderView.layout(
0,
mTop,
r,
mTop + mHeaderView.getMeasuredHeight());
mDescView.layout(
0,
mTop + mHeaderView.getMeasuredHeight(),
r,
mTop + b);
}
}
The author notes that onLayoutand onMeasure are badly written and I assume these (or one of them) might be the cause of a problem with one of the children.
For my objective, I replaced the mDescView with a Framelayout view containing the respective TextView. Both of them have their height set to match_parent and the parent (mDescView) does set its height correctly, but its children (TextView inside mDescView) ignore the parent height and stretch to fit their height equal to the screen height (or the custom view height, can't tell the difference). This is a problem because the mDescView children will never adjust their height correctly according to the parent through match_parent and I have been looking for a solution for days, but none was found and through research I couldn't find a reason for why this was happening.
This is the result of this problem, notice how the TextView height is bigger than its parent mDescView despite both having their height set to match_parent
So my problem is, how can I get the child (children) of that parent (or any parents) to match their parents' height like they should.
As an additional request, if possible, can anyone explain why the author thinks some of his methods are not the best/right ones and how they should be done correctly/better instead.
I have finally corrected this problem, although I am still unaware if this is the right way to do this. My solution was to set both views' height during onLayout
I have also set this to only run on the first time this method is called, so the specific statement only runs once and only during the first time (when firstRun is true)
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mDragRange = getHeight() - mHeaderView.getHeight();
if (firstRun) {
firstRun = false;
mDescView.getLayoutParams().height = getHeight() - mHeaderView.getMeasuredHeight();
}
mHeaderView.layout(
0,
mTop,
r,
mTop + mHeaderView.getMeasuredHeight());
mDescView.layout(
0,
mTop + mHeaderView.getMeasuredHeight(),
r,
mTop + b);
}

Drag and Drop Dynamic Listview Row Disappears When Adding Item to Listview Android

So I am trying to make a Listview you can drag and drop the rows. I got it to work ok, but when I add an item to the Listview it breaks (here is what the bug looks like: http://youtu.be/VYRYh0Vwq6o). What is causing this problem and how do I fix it? Thanks in advance.
Heres my code:
ItemsArrayAdapter.xml
package benawad.com.todolist.adapters;
import android.content.Context;
import android.graphics.Paint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.HashMap;
import benawad.com.todolist.NoteActivity;
import benawad.com.todolist.R;
public class ItemsArrayAdapter extends ArrayAdapter<String> {
public static final String TAG = ItemsArrayAdapter.class.getSimpleName();
Context mContext;
ArrayList<String> mArrayList;
boolean wantsSlash;
NoteActivity mNoteActivity;
EditText mNewItemText;
final int INVALID_ID = -1;
HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();
public ItemsArrayAdapter
(NoteActivity context, ArrayList<String> arrayList, boolean slashes){
super(context, R.layout.item_row,arrayList);
mContext = context;
mArrayList = arrayList;
mNoteActivity = context;
wantsSlash = slashes;
update();
}
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null){
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_row, parent, false);
holder = new ViewHolder();
holder.itemName = (TextView)convertView.findViewById(R.id.itemText);
for (int i = 0; i < mArrayList.size(); ++i) {
mIdMap.put(mArrayList.get(i), i);
}
holder.delete = (ImageView)convertView.findViewById(R.id.delete_item);
holder.edit = (ImageView)convertView.findViewById(R.id.edit_item);
convertView.setTag(holder);
}
else{
holder = (ViewHolder) convertView.getTag();
}
holder.itemName.setText(mArrayList.get(position));
if(wantsSlash){
holder.itemName.setPaintFlags(Paint.STRIKE_THRU_TEXT_FLAG);
}
holder.delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mNoteActivity.deleteItem(position);
}
});
holder.edit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mNoteActivity.editItem(position);
}
});
return convertView;
}
private static class ViewHolder {
TextView itemName;
ImageView edit;
ImageView delete;
}
#Override
public long getItemId(int position) {
Long idToReturn = (long) INVALID_ID;
if (position >= 0 && position <= mIdMap.size()-1) {
try {
String item = getItem(position);
idToReturn = (long) mIdMap.get(item);
}
catch(Exception e){}
}
return idToReturn;
}
// #Override
// public long getItemId(int position) {
// if (position < 0 || position >= mIdMap.size()) {
// return INVALID_ID;
// }
// String item = getItem(position);
// return mIdMap.get(item);
// }
public void update(){
for (int i = 0; i < mArrayList.size(); ++i) {
mIdMap.put(mArrayList.get(i), i);
}
}
#Override
public boolean hasStableIds() {
return true;
}
}
DynamicListView.java
package benawad.com.todolist;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import java.util.ArrayList;
import benawad.com.todolist.adapters.ItemsArrayAdapter;
public class DynamicListView extends ListView {
final static String TAG = DynamicListView.class.getSimpleName();
private final int SMOOTH_SCROLL_AMOUNT_AT_EDGE = 15;
private final int MOVE_DURATION = 150;
private final int LINE_THICKNESS = 15;
public ArrayList<String> mCheeseList;
private int mLastEventY = -1;
private int mDownY = -1;
private int mDownX = -1;
private int mTotalOffset = 0;
private boolean mCellIsMobile = false;
private boolean mIsMobileScrolling = false;
private int mSmoothScrollAmountAtEdge = 0;
private final int INVALID_ID = -1;
private long mAboveItemId = INVALID_ID;
private long mMobileItemId = INVALID_ID;
private long mBelowItemId = INVALID_ID;
private BitmapDrawable mHoverCell;
private Rect mHoverCellCurrentBounds;
private Rect mHoverCellOriginalBounds;
private final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
private boolean mIsWaitingForScrollFinish = false;
private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
public DynamicListView(Context context) {
super(context);
init(context);
}
public DynamicListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public DynamicListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public void init(Context context) {
setOnItemLongClickListener(mOnItemLongClickListener);
setOnScrollListener(mScrollListener);
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
mSmoothScrollAmountAtEdge = (int)(SMOOTH_SCROLL_AMOUNT_AT_EDGE / metrics.density);
}
/**
* Listens for long clicks on any mItems in the listview. When a cell has
* been selected, the hover cell is created and set up.
*/
private OnItemLongClickListener mOnItemLongClickListener =
new OnItemLongClickListener() {
public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int pos, long id) {
mTotalOffset = 0;
int position = pointToPosition(mDownX, mDownY);
int itemNum = position - getFirstVisiblePosition();
View selectedView = getChildAt(itemNum);
mMobileItemId = getAdapter().getItemId(position);
mHoverCell = getAndAddHoverView(selectedView);
selectedView.setVisibility(INVISIBLE);
mCellIsMobile = true;
updateNeighborViewsForID(mMobileItemId);
return true;
}
};
/**
* Creates the hover cell with the appropriate bitmap and of appropriate
* size. The hover cell's BitmapDrawable is drawn on top of the bitmap every
* single time an invalidate call is made.
*/
private BitmapDrawable getAndAddHoverView(View v) {
int w = v.getWidth();
int h = v.getHeight();
int top = v.getTop();
int left = v.getLeft();
Bitmap b = getBitmapWithBorder(v);
BitmapDrawable drawable = new BitmapDrawable(getResources(), b);
mHoverCellOriginalBounds = new Rect(left, top, left + w, top + h);
mHoverCellCurrentBounds = new Rect(mHoverCellOriginalBounds);
drawable.setBounds(mHoverCellCurrentBounds);
return drawable;
}
/** Draws a black border over the screenshot of the view passed in. */
private Bitmap getBitmapWithBorder(View v) {
Bitmap bitmap = getBitmapFromView(v);
Canvas can = new Canvas(bitmap);
Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(LINE_THICKNESS);
paint.setColor(Color.BLACK);
can.drawBitmap(bitmap, 0, 0, null);
can.drawRect(rect, paint);
return bitmap;
}
/** Returns a bitmap showing a screenshot of the view passed in. */
private Bitmap getBitmapFromView(View v) {
Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas (bitmap);
v.draw(canvas);
return bitmap;
}
/**
* Stores a reference to the views above and below the item currently
* corresponding to the hover cell. It is important to note that if this
* item is either at the top or bottom of the list, mAboveItemId or mBelowItemId
* may be invalid.
*/
private void updateNeighborViewsForID(long itemID) {
int position = getPositionForID(itemID);
ItemsArrayAdapter adapter = ((ItemsArrayAdapter)getAdapter());
mAboveItemId = adapter.getItemId(position - 1);
mBelowItemId = adapter.getItemId(position + 1);
}
/** Retrieves the view in the list corresponding to itemID */
public View getViewForID (long itemID) {
int firstVisiblePosition = getFirstVisiblePosition();
ItemsArrayAdapter adapter = ((ItemsArrayAdapter)getAdapter());
for(int i = 0; i < getChildCount(); i++) {
View v = getChildAt(i);
int position = firstVisiblePosition + i;
long id = adapter.getItemId(position);
if (id == itemID) {
return v;
}
}
return null;
}
/** Retrieves the position in the list corresponding to itemID */
public int getPositionForID (long itemID) {
View v = getViewForID(itemID);
if (v == null) {
return -1;
} else {
return getPositionForView(v);
}
}
/**
* dispatchDraw gets invoked when all the child views are about to be drawn.
* By overriding this method, the hover cell (BitmapDrawable) can be drawn
* over the listview's mItems whenever the listview is redrawn.
*/
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mHoverCell != null) {
mHoverCell.draw(canvas);
}
}
#Override
public boolean onTouchEvent (MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mDownX = (int)event.getX();
mDownY = (int)event.getY();
mActivePointerId = event.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE:
if (mActivePointerId == INVALID_POINTER_ID) {
break;
}
int pointerIndex = event.findPointerIndex(mActivePointerId);
mLastEventY = (int) event.getY(pointerIndex);
int deltaY = mLastEventY - mDownY;
if (mCellIsMobile) {
mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left,
mHoverCellOriginalBounds.top + deltaY + mTotalOffset);
mHoverCell.setBounds(mHoverCellCurrentBounds);
invalidate();
handleCellSwitch();
mIsMobileScrolling = false;
handleMobileCellScroll();
return false;
}
break;
case MotionEvent.ACTION_UP:
touchEventsEnded();
break;
case MotionEvent.ACTION_CANCEL:
touchEventsCancelled();
break;
case MotionEvent.ACTION_POINTER_UP:
/* If a multitouch event took place and the original touch dictating
* the movement of the hover cell has ended, then the dragging event
* ends and the hover cell is animated to its corresponding position
* in the listview. */
pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = event.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
touchEventsEnded();
}
break;
default:
break;
}
return super.onTouchEvent(event);
}
/**
* This method determines whether the hover cell has been shifted far enough
* to invoke a cell swap. If so, then the respective cell swap candidate is
* determined and the data set is changed. Upon posting a notification of the
* data set change, a layout is invoked to place the cells in the right place.
* Using a ViewTreeObserver and a corresponding OnPreDrawListener, we can
* offset the cell being swapped to where it previously was and then animate it to
* its new position.
*/
private void handleCellSwitch() {
final int deltaY = mLastEventY - mDownY;
int deltaYTotal = mHoverCellOriginalBounds.top + mTotalOffset + deltaY;
View belowView = getViewForID(mBelowItemId);
View mobileView = getViewForID(mMobileItemId);
View aboveView = getViewForID(mAboveItemId);
boolean isBelow = (belowView != null) && (deltaYTotal > belowView.getTop());
boolean isAbove = (aboveView != null) && (deltaYTotal < aboveView.getTop());
if (isBelow || isAbove) {
final long switchItemID = isBelow ? mBelowItemId : mAboveItemId;
View switchView = isBelow ? belowView : aboveView;
final int originalItem = getPositionForView(mobileView);
if (switchView == null) {
updateNeighborViewsForID(mMobileItemId);
return;
}
swapElements(mCheeseList, originalItem, getPositionForView(switchView));
((BaseAdapter) getAdapter()).notifyDataSetChanged();
mDownY = mLastEventY;
final int switchViewStartTop = switchView.getTop();
if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.KITKAT){
mobileView.setVisibility(View.VISIBLE);
switchView.setVisibility(View.INVISIBLE);
} else{
mobileView.setVisibility(View.INVISIBLE);
switchView.setVisibility(View.VISIBLE);
}
updateNeighborViewsForID(mMobileItemId);
final ViewTreeObserver observer = getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
observer.removeOnPreDrawListener(this);
View switchView = getViewForID(switchItemID);
mTotalOffset += deltaY;
int switchViewNewTop = switchView.getTop();
int delta = switchViewStartTop - switchViewNewTop;
switchView.setTranslationY(delta);
ObjectAnimator animator = ObjectAnimator.ofFloat(switchView,
View.TRANSLATION_Y, 0);
animator.setDuration(MOVE_DURATION);
animator.start();
return true;
}
});
}
}
private void swapElements(ArrayList arrayList, int indexOne, int indexTwo) {
Object temp = arrayList.get(indexOne);
arrayList.set(indexOne, arrayList.get(indexTwo));
arrayList.set(indexTwo, temp);
}
/**
* Resets all the appropriate fields to a default state while also animating
* the hover cell back to its correct location.
*/
private void touchEventsEnded () {
final View mobileView = getViewForID(mMobileItemId);
if (mCellIsMobile|| mIsWaitingForScrollFinish) {
mCellIsMobile = false;
mIsWaitingForScrollFinish = false;
mIsMobileScrolling = false;
mActivePointerId = INVALID_POINTER_ID;
// If the autoscroller has not completed scrolling, we need to wait for it to
// finish in order to determine the final location of where the hover cell
// should be animated to.
if (mScrollState != OnScrollListener.SCROLL_STATE_IDLE) {
mIsWaitingForScrollFinish = true;
return;
}
mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left, mobileView.getTop());
ObjectAnimator hoverViewAnimator = ObjectAnimator.ofObject(mHoverCell, "bounds",
sBoundEvaluator, mHoverCellCurrentBounds);
hoverViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
invalidate();
}
});
hoverViewAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
setEnabled(false);
}
#Override
public void onAnimationEnd(Animator animation) {
mAboveItemId = INVALID_ID;
mMobileItemId = INVALID_ID;
mBelowItemId = INVALID_ID;
mobileView.setVisibility(VISIBLE);
mHoverCell = null;
setEnabled(true);
invalidate();
}
});
hoverViewAnimator.start();
} else {
touchEventsCancelled();
}
}
/**
* Resets all the appropriate fields to a default state.
*/
private void touchEventsCancelled () {
View mobileView = getViewForID(mMobileItemId);
if (mCellIsMobile) {
mAboveItemId = INVALID_ID;
mMobileItemId = INVALID_ID;
mBelowItemId = INVALID_ID;
mobileView.setVisibility(VISIBLE);
mHoverCell = null;
invalidate();
}
mCellIsMobile = false;
mIsMobileScrolling = false;
mActivePointerId = INVALID_POINTER_ID;
}
/**
* This TypeEvaluator is used to animate the BitmapDrawable back to its
* final location when the user lifts his finger by modifying the
* BitmapDrawable's bounds.
*/
private final static TypeEvaluator<Rect> sBoundEvaluator = new TypeEvaluator<Rect>() {
public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
return new Rect(interpolate(startValue.left, endValue.left, fraction),
interpolate(startValue.top, endValue.top, fraction),
interpolate(startValue.right, endValue.right, fraction),
interpolate(startValue.bottom, endValue.bottom, fraction));
}
public int interpolate(int start, int end, float fraction) {
return (int)(start + fraction * (end - start));
}
};
/**
* Determines whether this listview is in a scrolling state invoked
* by the fact that the hover cell is out of the bounds of the listview;
*/
private void handleMobileCellScroll() {
mIsMobileScrolling = handleMobileCellScroll(mHoverCellCurrentBounds);
}
/**
* This method is in charge of determining if the hover cell is above
* or below the bounds of the listview. If so, the listview does an appropriate
* upward or downward smooth scroll so as to reveal new mItems.
*/
public boolean handleMobileCellScroll(Rect r) {
int offset = computeVerticalScrollOffset();
int height = getHeight();
int extent = computeVerticalScrollExtent();
int range = computeVerticalScrollRange();
int hoverViewTop = r.top;
int hoverHeight = r.height();
if (hoverViewTop <= 0 && offset > 0) {
smoothScrollBy(-mSmoothScrollAmountAtEdge, 0);
return true;
}
if (hoverViewTop + hoverHeight >= height && (offset + extent) < range) {
smoothScrollBy(mSmoothScrollAmountAtEdge, 0);
return true;
}
return false;
}
public void setCheeseList(ArrayList<String> cheeseList) {
mCheeseList = cheeseList;
}
/**
* This scroll listener is added to the listview in order to handle cell swapping
* when the cell is either at the top or bottom edge of the listview. If the hover
* cell is at either edge of the listview, the listview will begin scrolling. As
* scrolling takes place, the listview continuously checks if new cells became visible
* and determines whether they are potential candidates for a cell swap.
*/
private OnScrollListener mScrollListener = new OnScrollListener () {
private int mPreviousFirstVisibleItem = -1;
private int mPreviousVisibleItemCount = -1;
private int mCurrentFirstVisibleItem;
private int mCurrentVisibleItemCount;
private int mCurrentScrollState;
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
mCurrentFirstVisibleItem = firstVisibleItem;
mCurrentVisibleItemCount = visibleItemCount;
mPreviousFirstVisibleItem = (mPreviousFirstVisibleItem == -1) ? mCurrentFirstVisibleItem
: mPreviousFirstVisibleItem;
mPreviousVisibleItemCount = (mPreviousVisibleItemCount == -1) ? mCurrentVisibleItemCount
: mPreviousVisibleItemCount;
checkAndHandleFirstVisibleCellChange();
checkAndHandleLastVisibleCellChange();
mPreviousFirstVisibleItem = mCurrentFirstVisibleItem;
mPreviousVisibleItemCount = mCurrentVisibleItemCount;
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
mCurrentScrollState = scrollState;
mScrollState = scrollState;
isScrollCompleted();
}
/**
* This method is in charge of invoking 1 of 2 actions. Firstly, if the listview
* is in a state of scrolling invoked by the hover cell being outside the bounds
* of the listview, then this scrolling event is continued. Secondly, if the hover
* cell has already been released, this invokes the animation for the hover cell
* to return to its correct position after the listview has entered an idle scroll
* state.
*/
private void isScrollCompleted() {
if (mCurrentVisibleItemCount > 0 && mCurrentScrollState == SCROLL_STATE_IDLE) {
if (mCellIsMobile && mIsMobileScrolling) {
handleMobileCellScroll();
} else if (mIsWaitingForScrollFinish) {
touchEventsEnded();
}
}
}
/**
* Determines if the listview scrolled up enough to reveal a new cell at the
* top of the list. If so, then the appropriate parameters are updated.
*/
public void checkAndHandleFirstVisibleCellChange() {
if (mCurrentFirstVisibleItem != mPreviousFirstVisibleItem) {
if (mCellIsMobile && mMobileItemId != INVALID_ID) {
updateNeighborViewsForID(mMobileItemId);
handleCellSwitch();
}
}
}
/**
* Determines if the listview scrolled down enough to reveal a new cell at the
* bottom of the list. If so, then the appropriate parameters are updated.
*/
public void checkAndHandleLastVisibleCellChange() {
int currentLastVisibleItem = mCurrentFirstVisibleItem + mCurrentVisibleItemCount;
int previousLastVisibleItem = mPreviousFirstVisibleItem + mPreviousVisibleItemCount;
if (currentLastVisibleItem != previousLastVisibleItem) {
if (mCellIsMobile && mMobileItemId != INVALID_ID) {
updateNeighborViewsForID(mMobileItemId);
handleCellSwitch();
}
}
}
};
}
Ran out of characters here is NoteActivity.java on pastebin.com: http://pastebin.com/MgaFXHb6
If you need to see any other code let me know.

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.

Animating list items that are partially hidden or at the end of the currently visible views in a ListView

I'm currently implementing my own custom listview. I idea is that a longclick on a list item will scale the surrounding visible views to a smaller value scale and leave the the current view normal, whilst expanding a view below the current selected view. Once the user touches anywhere around the selected view, it will scale everything back to normal.
Unfortunately, there's an issue whereby the last view maybe partially hidden during the scale animation and so doesn't scale back to normal. So when you try to gain focus to the rest of the Listview by touching outside the selected list item, it remains a smaller scale.
I've tried a few approaches, from using ViewTreeObserver to storing the positions of the list items to make sure that they get scaled, but nothing has worked. Below is my custom listview:
public class ZoomListView extends ListView implements AdapterView.OnItemLongClickListener {
private static final String TAG = ZoomListView.class.getName();
private int _xPos;
private int _yPos;
private int _pointerId;
private Rect _viewBounds;
private boolean _isZoomed;
private OnItemClickListener _listner;
private OnItemFocused _onItemFocusedLis;
private int _expandingViewHeight = 0;
private int _previousFocusedViewHeight;
public interface OnItemFocused {
/**
* This interface can be used to be notified when a particular item should be disabled, or is currently not focused
* #param position
*/
public void onItemOutOfFocus(int position, boolean status_);
public View onItemFocused(View focusedView_, int listViewPosition_, long uniqueId_);
}
public ZoomListView(Context context_) {
super(context_);
init(context_);
}
public ZoomListView(Context context_, AttributeSet attrs) {
super(context_, attrs);
init(context_);
}
public ZoomListView(Context context_, AttributeSet attrs, int defStyle) {
super(context_, attrs, defStyle);
init(context_);
}
#Override
public void setOnItemClickListener(OnItemClickListener listener) {
if(!(listener == null)){
_listner = listener;
}
super.setOnItemClickListener(listener);
}
private void init(Context context_){
setOnItemLongClickListener(this);
}
public void setOnItemDisableListener(OnItemFocused listener_){
_onItemFocusedLis = listener_;
}
private void scaleChildViews(long rowId_, int itemPos_, float scale, boolean shouldEnable){
if (_isZoomed) {
getParent().requestDisallowInterceptTouchEvent(true);
}
int firstVisiblePosition = getFirstVisiblePosition();
int pos = pointToPosition(_xPos, _yPos);
int positionOrg = pos - firstVisiblePosition;
scaleAllVisibleViews(positionOrg, scale, shouldEnable);
}
private void scaleAllVisibleViews(final int clickedItemPosition_, final float scale_, final boolean shouldEnable_) {
Animation scaleAnimation;
if(_isZoomed){
scaleAnimation = getZoomAnimation(1f, 0.8f, 1f, 0.8f);
}else{
scaleAnimation = getZoomAnimation(0.8f, 1f, 0.8f, 1f);
}
int firstVisiblePosition = getFirstVisiblePosition();
int count = getChildCount();
for (int i = 0; i < count; i++) {
int pos = i;
if (_isZoomed) {
if (getAdapter().getItemId(clickedItemPosition_) != getAdapter().getItemId(pos)) {
scaleView(pos, scale_, shouldEnable_, scaleAnimation);
}else{
displayExpandingView(pos, clickedItemPosition_);
}
} else {
View view = getChildAt(pos);
View viewToShow = _onItemFocusedLis.onItemFocused(view, pos, getAdapter().getItemId(clickedItemPosition_));
if(viewToShow != null){
viewToShow.setVisibility(GONE);
}
scaleView(pos, scale_, shouldEnable_, scaleAnimation);
}
}
}
private void displayExpandingView(int position_, int clickedItemPosition_){
View view = getChildAt(position_);
if(view != null){
Log.v(TAG, "view is valid");
View viewToShow = _onItemFocusedLis.onItemFocused(view, position_, getAdapter().getItemId(clickedItemPosition_));
viewToShow.setVisibility(VISIBLE);
Animation flip = new CyclicFlipAnimation(50f);
flip.setDuration(500);
viewToShow.startAnimation(flip);
if(_expandingViewHeight <= 0){
viewToShow.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
_expandingViewHeight = viewToShow.getMeasuredHeight();
Log.v(TAG, "expanding view hieght is + " + _expandingViewHeight);
}
}
}
private Animation getZoomAnimation(float fromX_, float toX_, float fromY_, float toY_){
Animation scaleAnimation = new ScaleAnimation(
fromX_, toX_,
fromY_, toY_,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setFillAfter(true);
scaleAnimation.setDuration(500);
return scaleAnimation;
}
private void scaleView(int position_, float scale_, boolean shouldEnable_, Animation animation_){
View view = getChildAt(position_);
if (view != null) {
view.startAnimation(animation_);
if (_onItemFocusedLis != null) {
_onItemFocusedLis.onItemOutOfFocus(position_, shouldEnable_);
}
}
}
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
_isZoomed = true;
scaleChildViews(l, i, 0.8f, false);
return true;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
_xPos = (int) event.getX();
_yPos = (int) event.getY();
_pointerId = event.getPointerId(0);
if (_isZoomed) {
if (!_viewBounds.contains(_xPos, _yPos)) {
_isZoomed = false;
scaleChildViews(1, 1, 1f, true);
}
return false;
}
int position = pointToPosition(_xPos, _yPos);
int childNum = (position != INVALID_POSITION) ? position - getFirstVisiblePosition() : -1;
View itemView = (childNum >= 0) ? getChildAt(childNum) : null;
if (itemView != null) {
_viewBounds = getChildViewRect(this, itemView);
}
break;
}
return super.onTouchEvent(event);
}
private Rect getChildViewRect(View parentView, View childView) {
final Rect childRect = new Rect(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom() + _expandingViewHeight);
if (parentView == childView) {
return childRect;
}
ViewGroup parent = (ViewGroup) childView.getParent();
while (parent != parentView) {
childRect.offset(parent.getLeft(), parent.getTop());
childView = parent;
parent = (ViewGroup) childView.getParent();
}
return childRect;
}
}
Any idea's on what maybe wrong, thanks.
To fix this issue I ended up adding a ACTION_MOVE case statement in my onTouch method. So as long as the list items were focused again, when the list performed a move i.e. scrolled the list item, the remaining list items are restored to their original size. To achieve this you need to maintain a map/collection of id's that correspond to your list items and iterate over them once the user scrolls.
Heres the full somwhat functiong implementation:
package com.sun.tweetfiltrr.zoomlistview;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import com.sun.tweetfiltrr.animation.CyclicFlipAnimation;
import java.util.HashMap;
import java.util.Map;
/**
*/
public class ZoomListView extends ListView implements AdapterView.OnItemLongClickListener {
private static final String TAG = ZoomListView.class.getName();
private int _xPos;
private int _yPos;
private int _pointerId;
private Rect _viewBounds;
private boolean _isZoomed;
private OnItemClickListener _listner;
private OnItemFocused _onItemFocusedLis;
private int _expandingViewHeight = 0;
private int _previousFocusedViewHeight;
private OnScrollListener _onScrollListener;
private boolean _shouldPerformScrollAnimation;
final private Map<Long, PropertyHolder> _itemIDToProperty = new HashMap<Long, PropertyHolder>();
private long _currentFocusedId;
public interface OnItemFocused {
/**
* This interface can be used to be notified when a particular item should be disabled, or is currently not focused
* #param position
*/
public void onItemScaleOut(int position, View view, boolean status_);
public void onItemRestore(int position, View view, boolean status_);
public View onItemFocused(View focusedView_, int listViewPosition_, long uniqueId_);
}
public ZoomListView(Context context_) {
super(context_);
init(context_);
}
public ZoomListView(Context context_, AttributeSet attrs) {
super(context_, attrs);
init(context_);
}
public ZoomListView(Context context_, AttributeSet attrs, int defStyle) {
super(context_, attrs, defStyle);
init(context_);
}
private void init(Context context_){
setOnItemLongClickListener(this);
}
public void setOnItemDisableListener(OnItemFocused listener_){
_onItemFocusedLis = listener_;
}
private void scaleChildViews(long rowId_, int itemPos_, float scale, boolean shouldEnable){
if (_isZoomed) {
getParent().requestDisallowInterceptTouchEvent(true);
}
scaleAllVisibleViews(shouldEnable);
}
private void scaleAllVisibleViews(final boolean shouldEnable_) {
final ListAdapter adapter = getAdapter();
Animation scaleAnimation;
if(_isZoomed){
scaleAnimation = getZoomAnimation(1f, 0.6f, 1f, 0.6f);
}else{
scaleAnimation = getZoomAnimation(0.6f, 1f, 0.6f, 1f);
}
int count = getChildCount();
for (int i = 0; i < count; i++) {
applyAnimation(i, adapter, shouldEnable_, scaleAnimation);
}
}
private void applyAnimation(int position_, ListAdapter adapter_, boolean shouldEnable_, Animation animation_){
if (_isZoomed) {
if (_currentFocusedId != adapter_.getItemId(position_)) {
scaleView(position_, shouldEnable_, animation_);
}else{
displayExpandingView(position_, adapter_);
}
}else{
if(_currentFocusedId != getAdapter().getItemId(position_)){
scaleView(position_, shouldEnable_, animation_);
}else{
View view = getChildAt(position_);
View viewToShow = _onItemFocusedLis.onItemFocused(view, position_, getAdapter().getItemId(position_));
if(viewToShow != null){
viewToShow.setVisibility(GONE);
}
}
_itemIDToProperty.remove(getAdapter().getItemId(position_));
}
}
private void displayExpandingView(int position_, ListAdapter adapter_){
View view = getChildAt(position_);
if(view != null){
View viewToShow = _onItemFocusedLis.onItemFocused(view, position_, adapter_.getItemId(position_));
viewToShow.setVisibility(VISIBLE);
Animation flip = new CyclicFlipAnimation(60f);
flip.setDuration(1000);
viewToShow.startAnimation(flip);
if(_expandingViewHeight <= 0){
viewToShow.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
_expandingViewHeight = viewToShow.getMeasuredHeight();
}
}
}
private Animation getZoomAnimation(float fromX_, float toX_, float fromY_, float toY_){
Animation scaleAnimation = new ScaleAnimation(
fromX_, toX_,
fromY_, toY_,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.ABSOLUTE, 0.7f);
scaleAnimation.setFillAfter(true);
scaleAnimation.setDuration(500);
return scaleAnimation;
}
private void scaleView(int position_, boolean shouldEnable_, Animation animationScale_ ){
View view = getChildAt(position_);
ListAdapter adapter = getAdapter();
long id = adapter.getItemId(position_);
view.startAnimation(animationScale_);
PropertyHolder holder = _itemIDToProperty.get(id);
if (holder == null) {
holder = new PropertyHolder((int) view.getTop(), (int) (view.getBottom()));
_itemIDToProperty.put(id, holder);
}
int h = view.getHeight();
if (_isZoomed) {
view.animate().translationYBy((h * 0.5f)).setDuration(500).start();
if (_onItemFocusedLis != null) {
_onItemFocusedLis.onItemScaleOut(position_, view, shouldEnable_);
}
} else {
view.animate().translationYBy(-(h * 0.5f )).setDuration(500).start();
if (_onItemFocusedLis != null) {
_onItemFocusedLis.onItemRestore(position_, view, shouldEnable_);
}
}
}
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
_isZoomed = true;
int firstVisiblePosition = getFirstVisiblePosition();
int pos = pointToPosition(_xPos, _yPos);
int positionOrg = pos - firstVisiblePosition;
_currentFocusedId = getAdapter().getItemId(positionOrg);
scaleChildViews(l, i, 0.8f, false);
return true;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
_xPos = (int) event.getX();
_yPos = (int) event.getY();
_pointerId = event.getPointerId(0);
if (_isZoomed) {
if (!_viewBounds.contains(_xPos, _yPos)) {
_isZoomed = false;
scaleChildViews(1, 1, 1f, true);
}
return false;
}
int position = pointToPosition(_xPos, _yPos);
int childNum = (position != INVALID_POSITION) ? position - getFirstVisiblePosition() : -1;
View itemView = (childNum >= 0) ? getChildAt(childNum) : null;
if (itemView != null) {
_viewBounds = getChildViewRect(this, itemView);
}
break;
case MotionEvent.ACTION_MOVE:
if (!_isZoomed) {
animateRemaining();
}
break;
}
return super.onTouchEvent(event);
}
private void animateRemaining(){
if(!_itemIDToProperty.isEmpty()){
for(int i = 0; i < getChildCount(); i++){
long id = getAdapter().getItemId(i);
PropertyHolder n = _itemIDToProperty.get(id);
if(n != null){
if(_currentFocusedId != getAdapter().getItemId(i)){
scaleView(i, true, getZoomAnimation(0.6f, 1, 0.6f, 1f));
}else{
Log.v(TAG, "not translating for " + getAdapter().getItemId(i)+ " for id " + _currentFocusedId);
}
_itemIDToProperty.remove(id);
}
}
}
}
private Rect getChildViewRect(View parentView, View childView) {
final Rect childRect = new Rect(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom() + _expandingViewHeight);
if (parentView == childView) {
return childRect;
}
ViewGroup parent = (ViewGroup) childView.getParent();
while (parent != parentView) {
childRect.offset(parent.getLeft(), parent.getTop());
childView = parent;
parent = (ViewGroup) childView.getParent();
}
return childRect;
}
private class PropertyHolder{
int _top;
int _bot;
private PropertyHolder(int top_, int bot_){
_top = top_;
_bot = bot_;
}
}
}
It's far from perfect but its a starting point.

Categories

Resources