Drag and Drop listview with images and text in android - android

I want to implement drag and drop of list-items in my application. I followed this, it works well for lists containing text only. I integrated lazy-list with images along with drag and drop. But, while I drag the item, the dragged item goes to actual position. The item below the dragged item gets removed from the list. After I take my fingers off, dragged item doesn't move to the place I wish. What is wrong with this code?
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.listviewdragginganimation.DynamicListView
android:id="#+id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"/>
<Button
android:id="#+id/button1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Clear Cache"/>
</LinearLayout>
DynamicListView.Java:
package com.listviewdragginganimation;
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.util.Log;
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;
public class DynamicListView extends ListView {
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);
}
private AdapterView.OnItemLongClickListener mOnItemLongClickListener = new AdapterView.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;
}
};
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;
}
private void updateNeighborViewsForID(long itemID) {
int position = getPositionForID(itemID);
LazyAdapter adapter = ((LazyAdapter) 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();
LazyAdapter adapter = ((LazyAdapter) 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);
}
}
#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();
// Log.v("handle switch 1","1");
handleCellSwitch();
// Log.v("handle switch 2","2");
mIsMobileScrolling = false;
handleMobileCellScroll();
return false;
}
break;
case MotionEvent.ACTION_UP:
touchEventsEnded();
break;
case MotionEvent.ACTION_CANCEL:
touchEventsCancelled();
break;
case MotionEvent.ACTION_POINTER_UP:
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);
}
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;
}
Log.v("swapElements switch 1", "1");
Log.v("swapElements originalItem 1", originalItem + "");
Log.v("swapElements switchView 1", "" + switchView);
setCheeseList(MainActivity.mCheeseList);
Log.v("mCheeseList item", mCheeseList.get(originalItem));
swapElements(mCheeseList, originalItem,
getPositionForView(switchView));
Log.v("swapElements switch 2", "2");
// ((BaseAdapter) getAdapter()).notifyDataSetChanged();
MainActivity.adapter.notifyDataSetChanged();
mDownY = mLastEventY;
final int switchViewStartTop = switchView.getTop();
mobileView.setVisibility(View.VISIBLE);
switchView.setVisibility(View.INVISIBLE);
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<String> arrayList, int indexOne,
int indexTwo) {
Log.v("index one", indexOne + "");
Log.v("index two", indexTwo + "");
Log.v("temp", arrayList.get(indexOne));
String 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));
}
};
private void handleMobileCellScroll() {
mIsMobileScrolling = handleMobileCellScroll(mHoverCellCurrentBounds);
}
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;
}
private AbsListView.OnScrollListener mScrollListener = new AbsListView.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();
}
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();
}
}
}
};
}
LazyAdapter.java:
package com.listviewdragginganimation;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class LazyAdapter extends BaseAdapter {
private Activity activity;
private ArrayList<String> data=new ArrayList<String>();
private static LayoutInflater inflater=null;
public ImageLoader imageLoader;
public LazyAdapter(MainActivity a, ArrayList<String> mCheeseList) {
// TODO Auto-generated constructor stub
activity = a;
data=mCheeseList;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
imageLoader=new ImageLoader(activity.getApplicationContext());
}
public int getCount() {
return data.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
if(convertView==null)
vi = inflater.inflate(R.layout.item, null);
TextView text=(TextView)vi.findViewById(R.id.text);;
ImageView image=(ImageView)vi.findViewById(R.id.image);
text.setText("item "+position);
imageLoader.DisplayImage(data.get(position), image);
return vi;
}
}
MainActivity.java:
package com.listviewdragginganimation;
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ListView;
public class MainActivity extends Activity {
ListView list;
static LazyAdapter adapter;
static ArrayList<String>mCheeseList = new ArrayList<String>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
for (int i = 0; i < mStrings.length; ++i) {
mCheeseList.add(mStrings[i]);
}
list=(ListView)findViewById(R.id.list);
adapter=new LazyAdapter(this, mCheeseList);
list.setAdapter(adapter);
Button b=(Button)findViewById(R.id.button1);
b.setOnClickListener(listener);
}
#Override
public void onDestroy()
{
list.setAdapter(null);
super.onDestroy();
}
public OnClickListener listener=new OnClickListener(){
#Override
public void onClick(View arg0) {
adapter.imageLoader.clearCache();
adapter.notifyDataSetChanged();
}
};
private String[] mStrings={
"http://a3.twimg.com/profile_images/670625317/aam-logo-v3-twitter.png",
"http://a3.twimg.com/profile_images/740897825/AndroidCast-350_normal.png",
"http://a3.twimg.com/profile_images/121630227/Droid_normal.jpg",
"http://a1.twimg.com/profile_images/957149154/twitterhalf_normal.jpg",
"http://a1.twimg.com/profile_images/97470808/icon_normal.png",
"http://a3.twimg.com/profile_images/511790713/AG.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/Android_Biz_Man_normal.png",
"http://a3.twimg.com/profile_images/72774055/AndroidHomme-LOGO_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/AndroidPlanet_normal.png",
};
}

I found this particular code https://github.com/bauerca/drag-sort-listview in this url and in it I was able to add images also...

I'm also trying to make a custom layout. Added an image (from a 'drawable' folder) to 'layout/text_view.xml' (inserted in a LinearLayout). Changed StableArrayAdapter to extend BaseAdapter and rewrote getView (I think you can also make this with ArrayAdapter like https://stackoverflow.com/a/30270370/2914140). Then applied some changes for Lollipop from ListViewDraggingAnimation broken on Android 5 Lollipop and https://www.youtube.com/all_comments?v=_BZIvjMgH-Q. At least, static images are dragged along with text. When I change an ImageView to a WebVew, there are many errors and bugs.

Related

Select random item in CardView

In this picture, you can see a CardView where each item is displayed one at a time. I have implemented a touch functionality that scrolls through the dataset. I would now like to implement the functionality of choosing an item at random. Here is where the CardView is set:
package com.chiemy.cardview.view;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ListAdapter;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.view.ViewHelper;
import com.nineoldandroids.view.ViewPropertyAnimator;
/**
* #author chiemy
*
*/
public class CardView extends FrameLayout {
private static final int ITEM_SPACE = 40;
private static final int DEF_MAX_VISIBLE = 4;
private int mMaxVisible = DEF_MAX_VISIBLE;
private int itemSpace = ITEM_SPACE;
private float mTouchSlop;
private ListAdapter mListAdapter;
private int mNextAdapterPosition;
private SparseArray<View> viewHolder = new SparseArray<View>();
private OnCardClickListener mListener;
private int topPosition;
private Rect topRect;
public interface OnCardClickListener {
void onCardClick(View view, int position);
}
public CardView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public CardView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CardView(Context context) {
super(context);
init();
}
private void init() {
topRect = new Rect();
ViewConfiguration con = ViewConfiguration.get(getContext());
mTouchSlop = con.getScaledTouchSlop();
}
public void setMaxVisibleCount(int count) {
mMaxVisible = count;
}
public int getMaxVisibleCount() {
return mMaxVisible;
}
public void setItemSpace(int itemSpace) {
this.itemSpace = itemSpace;
}
public int getItemSpace() {
return itemSpace;
}
public ListAdapter getAdapter() {
return mListAdapter;
}
public void setAdapter(ListAdapter adapter) {
if (mListAdapter != null) {
mListAdapter.unregisterDataSetObserver(mDataSetObserver);
}
mNextAdapterPosition = 0;
mListAdapter = adapter;
adapter.registerDataSetObserver(mDataSetObserver);
removeAllViews();
ensureFull();
}
public void setOnCardClickListener(OnCardClickListener listener) {
mListener = listener;
}
private void ensureFull() {
while (mNextAdapterPosition < mListAdapter.getCount()
&& getChildCount() < mMaxVisible) {
int index = mNextAdapterPosition % mMaxVisible;
View convertView = viewHolder.get(index);
final View view = mListAdapter.getView(mNextAdapterPosition,
convertView, this);
view.setOnClickListener(null);
viewHolder.put(index, view);
index = Math.min(mNextAdapterPosition, mMaxVisible - 1);
ViewHelper.setScaleX(view,((mMaxVisible - index - 1) / (float) mMaxVisible) * 0.2f + 0.8f);
int topMargin = (mMaxVisible - index - 1) * itemSpace;
ViewHelper.setTranslationY(view, topMargin);
ViewHelper.setAlpha(view, mNextAdapterPosition == 0 ? 1 : 0.5f);
LayoutParams params = (LayoutParams) view.getLayoutParams();
if (params == null) {
params = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
}
addViewInLayout(view, 0, params);
mNextAdapterPosition += 1;
}
// requestLayout();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int childCount = getChildCount();
int maxHeight = 0;
int maxWidth = 0;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
this.measureChild(child, widthMeasureSpec, heightMeasureSpec);
int height = child.getMeasuredHeight();
int width = child.getMeasuredWidth();
if (height > maxHeight) {
maxHeight = height;
}
if (width > maxWidth) {
maxWidth = width;
}
}
int desireWidth = widthSize;
int desireHeight = heightSize;
if (widthMode == MeasureSpec.AT_MOST) {
desireWidth = maxWidth + getPaddingLeft() + getPaddingRight();
}
if (heightMode == MeasureSpec.AT_MOST) {
desireHeight = maxHeight + (mMaxVisible - 1) * itemSpace + getPaddingTop() + getPaddingBottom();
}
setMeasuredDimension(desireWidth, desireHeight);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
View topView = getChildAt(getChildCount() - 1);
if (topView != null) {
topView.setOnClickListener(listener);
}
}
float downX, downY;
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (goDown()) {
downY = -1;
}
break;
}
return super.onTouchEvent(event);
}
/**
* 下移所有视图
*/
private boolean goDown() {
final View topView = getChildAt(getChildCount() - 1);
if(!topView.isEnabled()){
return false;
}
// topView.getHitRect(topRect); 在4.3以前有bug,用以下方法代替
topRect = getHitRect(topRect, topView);
// 如果按下的位置不在顶部视图上,则不移动
if (!topRect.contains((int) downX, (int) downY)) {
return false;
}
topView.setEnabled(false);
ViewPropertyAnimator anim = ViewPropertyAnimator
.animate(topView)
.translationY(
ViewHelper.getTranslationY(topView)
+ topView.getHeight()).alpha(0).scaleX(1)
.setListener(null).setDuration(200);
anim.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
topView.setEnabled(true);
removeView(topView);
ensureFull();
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View view = getChildAt(i);
float scaleX = ViewHelper.getScaleX(view)
+ ((float) 1 / mMaxVisible) * 0.2f;
float tranlateY = ViewHelper.getTranslationY(view)
+ itemSpace;
if (i == count - 1) {
bringToTop(view);
} else {
if ((count == mMaxVisible && i != 0)
|| count < mMaxVisible) {
ViewPropertyAnimator
.animate(view)
.translationY(tranlateY)
.setInterpolator(
new AccelerateInterpolator())
.setListener(null).scaleX(scaleX)
.setDuration(200);
}
}
}
}
});
return true;
}
private void bringToTop(final View view) {
topPosition++;
float scaleX = ViewHelper.getScaleX(view) + ((float) 1 / mMaxVisible)
* 0.2f;
float tranlateY = ViewHelper.getTranslationY(view) + itemSpace;
ViewPropertyAnimator.animate(view).translationY(tranlateY)
.scaleX(scaleX).setDuration(200).alpha(1)
.setInterpolator(new AccelerateInterpolator());
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
float currentY = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = ev.getX();
downY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
float distance = currentY - downY;
if (distance > mTouchSlop) {
return true;
}
break;
}
return false;
}
public static Rect getHitRect(Rect rect, View child) {
rect.left = child.getLeft();
rect.right = child.getRight();
rect.top = (int) (child.getTop() + ViewHelper.getTranslationY(child));
rect.bottom = (int) (child.getBottom() + ViewHelper
.getTranslationY(child));
return rect;
}
private final DataSetObserver mDataSetObserver = new DataSetObserver() {
#Override
public void onChanged() {
super.onChanged();
}
#Override
public void onInvalidated() {
super.onInvalidated();
}
};
private OnClickListener listener = new OnClickListener() {
#Override
public void onClick(View v) {
if (mListener != null) {
mListener.onCardClick(v, topPosition);
}
}
};
}
Here is my CardView adapter class:
package com.chiemy.cardview.view;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.chiemy.cardview.R;
public abstract class CardAdapter<T> extends BaseCardAdapter {
private final Context mContext;
private ArrayList<T> mData;
public CardAdapter(Context context) {
mContext = context;
mData = new ArrayList<T>();
}
public CardAdapter(Context context, Collection<? extends T> items) {
mContext = context;
mData = new ArrayList<T>(items);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
FrameLayout wrapper = (FrameLayout) convertView;
View cardView;
View convertedCardView;
if (wrapper == null) {
wrapper = new FrameLayout(mContext);
wrapper.setBackgroundResource(R.drawable.card_background_shadow);
cardView = getCardView(position, null, wrapper);
wrapper.addView(cardView);
} else {
cardView = wrapper.getChildAt(0);
convertedCardView = getCardView(position, cardView, wrapper);
wrapper.removeView(cardView);
wrapper.addView(convertedCardView);
if (convertedCardView != cardView) {
}
}
return wrapper;
}
protected abstract View getCardView(int position, View convertView, ViewGroup parent);
public void addAll(List<T> items){
mData.addAll(items);
}
#Override
public T getItem(int position) {
return mData.get(position);
}
#Override
public int getCount() {
return mData.size();
}
#Override
public long getItemId(int position) {
return getItem(position).hashCode();
}
public Context getContext() {
return mContext;
}
public void clear(){
if(mData != null){
mData.clear();
}
}
}
Any suggestions on how to achieve this?
To randomise an ArrayList<>, in your case, this would be the shuffling solution, you can use this code:
long seed = System.nanoTime();
Collections.shuffle(list_of_items, new Random(seed));
You will need to set the ArrayList<> back to the CardView and your items will be randomly shuffled. To retrieve a random item, visit this page to learn about getting a random item from an ArrayList<>. See how you go and ask for more help if needed.

Unable to set visibility of any relative layout in a View Group in Android

I am using a floating action button in my activity. I have created a related layout with a black opacity background. I want it to appear when the floating action button is clicked. The relative layout will appear behind the action button to make it look prominent.
Here is my xml layout
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:fab="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.paaltao.activity.HomeActivity">
<include
android:id="#+id/app_bar"
layout="#layout/app_bar" />
<it.neokree.materialtabs.MaterialTabHost
android:id="#+id/materialTabHost"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_below="#+id/app_bar"
app:accentColor="#color/greenMaterial"
app:hasIcons="true"
app:primaryColor="#color/primaryColor"
app:textColor="#color/white" />
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/materialTabHost" />
<RelativeLayout
android:id="#+id/white_opacity"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/materialTabHost"
android:background="#color/black80"
android:visibility="gone" />
<com.paaltao.classes.FloatingActionsMenu
android:id="#+id/multiple_actions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_gravity="end"
fab:fab_addButtonColorPressed="#color/white_pressed"
fab:fab_addButtonPlusIconColor="#color/white"
fab:fab_labelStyle="#style/menu_labels_style"
app:fab_addButtonColorNormal="#color/primaryColor">
<com.paaltao.classes.FloatingActionButton
android:id="#+id/action_b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_colorNormal="#color/white"
fab:fab_colorPressed="#color/white_pressed"
fab:fab_title="Action B" />
</com.paaltao.classes.FloatingActionsMenu>
</RelativeLayout>
I am basically trying to set visibility of the relative layout to View.VISIBLE in the floating action menu class which extends a view group. Now I am getting null pointer exception when I am setting the visibility as View.VISIBLE in the button's onClick event.
Here is my Java Code :
package com.paaltao.classes;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.ColorRes;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.paaltao.R;
import java.util.zip.Inflater;
public class FloatingActionsMenu extends ViewGroup {
public static final int EXPAND_UP = 0;
public static final int EXPAND_DOWN = 1;
public static final int EXPAND_LEFT = 2;
public static final int EXPAND_RIGHT = 3;
private static final int ANIMATION_DURATION = 300;
private static final float COLLAPSED_PLUS_ROTATION = 0f;
private static final float EXPANDED_PLUS_ROTATION = 90f + 45f;
private int mAddButtonPlusColor;
private int mAddButtonColorNormal;
private int mAddButtonColorPressed;
private int mAddButtonSize;
private boolean mAddButtonStrokeVisible;
private int mExpandDirection;
private int mButtonSpacing;
private int mLabelsMargin;
private int mLabelsVerticalOffset;
private boolean mExpanded;
private AnimatorSet mExpandAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION);
private AnimatorSet mCollapseAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION);
private AddFloatingActionButton mAddButton;
private RotatingDrawable mRotatingDrawable;
private int mMaxButtonWidth;
private int mMaxButtonHeight;
private int mLabelsStyle;
private int mButtonsCount;
private OnFloatingActionsMenuUpdateListener mListener;
private RelativeLayout whiteOverlay;
public interface OnFloatingActionsMenuUpdateListener {
void onMenuExpanded();
void onMenuCollapsed();
}
public FloatingActionsMenu(Context context) {
this(context, null);
}
public FloatingActionsMenu(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public FloatingActionsMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attributeSet) {
mButtonSpacing = (int) (getResources().getDimension(R.dimen.fab_actions_spacing) - getResources().getDimension(R.dimen.fab_shadow_radius) - getResources().getDimension(R.dimen.fab_shadow_offset));
mLabelsMargin = getResources().getDimensionPixelSize(R.dimen.fab_labels_margin);
mLabelsVerticalOffset = getResources().getDimensionPixelSize(R.dimen.fab_shadow_offset);
TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.FloatingActionsMenu, 0, 0);
mAddButtonPlusColor = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonPlusIconColor, getColor(android.R.color.white));
mAddButtonColorNormal = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonColorNormal, getColor(android.R.color.holo_blue_dark));
mAddButtonColorPressed = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonColorPressed, getColor(android.R.color.holo_blue_light));
mAddButtonSize = attr.getInt(R.styleable.FloatingActionsMenu_fab_addButtonSize, FloatingActionButton.SIZE_NORMAL);
mAddButtonStrokeVisible = attr.getBoolean(R.styleable.FloatingActionsMenu_fab_addButtonStrokeVisible, true);
mExpandDirection = attr.getInt(R.styleable.FloatingActionsMenu_fab_expandDirection, EXPAND_UP);
mLabelsStyle = attr.getResourceId(R.styleable.FloatingActionsMenu_fab_labelStyle, 0);
attr.recycle();
if (mLabelsStyle != 0 && expandsHorizontally()) {
throw new IllegalStateException("Action labels in horizontal expand orientation is not supported.");
}
createAddButton(context);
}
public void setOnFloatingActionsMenuUpdateListener(OnFloatingActionsMenuUpdateListener listener) {
mListener = listener;
}
private boolean expandsHorizontally() {
return mExpandDirection == EXPAND_LEFT || mExpandDirection == EXPAND_RIGHT;
}
private static class RotatingDrawable extends LayerDrawable {
public RotatingDrawable(Drawable drawable) {
super(new Drawable[] { drawable });
}
private float mRotation;
#SuppressWarnings("UnusedDeclaration")
public float getRotation() {
return mRotation;
}
#SuppressWarnings("UnusedDeclaration")
public void setRotation(float rotation) {
mRotation = rotation;
invalidateSelf();
}
#Override
public void draw(Canvas canvas) {
canvas.save();
canvas.rotate(mRotation, getBounds().centerX(), getBounds().centerY());
super.draw(canvas);
canvas.restore();
}
}
private void createAddButton(Context context) {
mAddButton = new AddFloatingActionButton(context) {
#Override
void updateBackground() {
mPlusColor = mAddButtonPlusColor;
mColorNormal = mAddButtonColorNormal;
mColorPressed = mAddButtonColorPressed;
mStrokeVisible = mAddButtonStrokeVisible;
super.updateBackground();
}
#Override
Drawable getIconDrawable() {
final RotatingDrawable rotatingDrawable = new RotatingDrawable(super.getIconDrawable());
mRotatingDrawable = rotatingDrawable;
final OvershootInterpolator interpolator = new OvershootInterpolator();
final ObjectAnimator collapseAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", EXPANDED_PLUS_ROTATION, COLLAPSED_PLUS_ROTATION);
final ObjectAnimator expandAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", COLLAPSED_PLUS_ROTATION, EXPANDED_PLUS_ROTATION);
collapseAnimator.setInterpolator(interpolator);
expandAnimator.setInterpolator(interpolator);
mExpandAnimation.play(expandAnimator);
mCollapseAnimation.play(collapseAnimator);
return rotatingDrawable;
}
};
mAddButton.setId(R.id.fab_expand_menu_button);
mAddButton.setSize(mAddButtonSize);
mAddButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
toggle();
// updateBackground();
}
});
addView(mAddButton, super.generateDefaultLayoutParams());
}
// public void updateBackground(){
// whiteOverlay = (RelativeLayout)findViewById(R.id.white_opacity);
// whiteOverlay.setVisibility(View.VISIBLE);
// }
public void addButton(FloatingActionButton button) {
addView(button, mButtonsCount - 1);
mButtonsCount++;
if (mLabelsStyle != 0) {
createLabels();
}
}
public void removeButton(FloatingActionButton button) {
removeView(button.getLabelView());
removeView(button);
mButtonsCount--;
}
private int getColor(#ColorRes int id) {
return getResources().getColor(id);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
int width = 0;
int height = 0;
mMaxButtonWidth = 0;
mMaxButtonHeight = 0;
int maxLabelWidth = 0;
for (int i = 0; i < mButtonsCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
switch (mExpandDirection) {
case EXPAND_UP:
case EXPAND_DOWN:
mMaxButtonWidth = Math.max(mMaxButtonWidth, child.getMeasuredWidth());
height += child.getMeasuredHeight();
break;
case EXPAND_LEFT:
case EXPAND_RIGHT:
width += child.getMeasuredWidth();
mMaxButtonHeight = Math.max(mMaxButtonHeight, child.getMeasuredHeight());
break;
}
if (!expandsHorizontally()) {
TextView label = (TextView) child.getTag(R.id.fab_label);
if (label != null) {
maxLabelWidth = Math.max(maxLabelWidth, label.getMeasuredWidth());
}
}
}
if (!expandsHorizontally()) {
width = mMaxButtonWidth + (maxLabelWidth > 0 ? maxLabelWidth + mLabelsMargin : 0);
} else {
height = mMaxButtonHeight;
}
switch (mExpandDirection) {
case EXPAND_UP:
case EXPAND_DOWN:
height += mButtonSpacing * (getChildCount() - 1);
height = adjustForOvershoot(height);
break;
case EXPAND_LEFT:
case EXPAND_RIGHT:
width += mButtonSpacing * (getChildCount() - 1);
width = adjustForOvershoot(width);
break;
}
setMeasuredDimension(width, height);
}
private int adjustForOvershoot(int dimension) {
return dimension * 12 / 10;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
switch (mExpandDirection) {
case EXPAND_UP:
case EXPAND_DOWN:
boolean expandUp = mExpandDirection == EXPAND_UP;
int addButtonY = expandUp ? b - t - mAddButton.getMeasuredHeight() : 0;
// Ensure mAddButton is centered on the line where the buttons should be
int addButtonLeft = r - l - mMaxButtonWidth + (mMaxButtonWidth - mAddButton.getMeasuredWidth()) / 2;
mAddButton.layout(addButtonLeft, addButtonY, addButtonLeft + mAddButton.getMeasuredWidth(), addButtonY + mAddButton.getMeasuredHeight());
int labelsRight = r - l - mMaxButtonWidth - mLabelsMargin;
int nextY = expandUp ?
addButtonY - mButtonSpacing :
addButtonY + mAddButton.getMeasuredHeight() + mButtonSpacing;
for (int i = mButtonsCount - 1; i >= 0; i--) {
final View child = getChildAt(i);
if (child == mAddButton || child.getVisibility() == GONE) continue;
int childX = addButtonLeft + (mAddButton.getMeasuredWidth() - child.getMeasuredWidth()) / 2;
int childY = expandUp ? nextY - child.getMeasuredHeight() : nextY;
child.layout(childX, childY, childX + child.getMeasuredWidth(), childY + child.getMeasuredHeight());
float collapsedTranslation = addButtonY - childY;
float expandedTranslation = 0f;
child.setTranslationY(mExpanded ? expandedTranslation : collapsedTranslation);
child.setAlpha(mExpanded ? 1f : 0f);
LayoutParams params = (LayoutParams) child.getLayoutParams();
params.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation);
params.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation);
params.setAnimationsTarget(child);
View label = (View) child.getTag(R.id.fab_label);
if (label != null) {
int labelLeft = labelsRight - label.getMeasuredWidth();
int labelTop = childY - mLabelsVerticalOffset + (child.getMeasuredHeight() - label.getMeasuredHeight()) / 2;
label.layout(labelLeft, labelTop, labelsRight, labelTop + label.getMeasuredHeight());
label.setTranslationY(mExpanded ? expandedTranslation : collapsedTranslation);
label.setAlpha(mExpanded ? 1f : 0f);
LayoutParams labelParams = (LayoutParams) label.getLayoutParams();
labelParams.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation);
labelParams.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation);
labelParams.setAnimationsTarget(label);
}
nextY = expandUp ?
childY - mButtonSpacing :
childY + child.getMeasuredHeight() + mButtonSpacing;
}
break;
case EXPAND_LEFT:
case EXPAND_RIGHT:
boolean expandLeft = mExpandDirection == EXPAND_LEFT;
int addButtonX = expandLeft ? r - l - mAddButton.getMeasuredWidth() : 0;
// Ensure mAddButton is centered on the line where the buttons should be
int addButtonTop = b - t - mMaxButtonHeight + (mMaxButtonHeight - mAddButton.getMeasuredHeight()) / 2;
mAddButton.layout(addButtonX, addButtonTop, addButtonX + mAddButton.getMeasuredWidth(), addButtonTop + mAddButton.getMeasuredHeight());
int nextX = expandLeft ?
addButtonX - mButtonSpacing :
addButtonX + mAddButton.getMeasuredWidth() + mButtonSpacing;
for (int i = mButtonsCount - 1; i >= 0; i--) {
final View child = getChildAt(i);
if (child == mAddButton || child.getVisibility() == GONE) continue;
int childX = expandLeft ? nextX - child.getMeasuredWidth() : nextX;
int childY = addButtonTop + (mAddButton.getMeasuredHeight() - child.getMeasuredHeight()) / 2;
child.layout(childX, childY, childX + child.getMeasuredWidth(), childY + child.getMeasuredHeight());
float collapsedTranslation = addButtonX - childX;
float expandedTranslation = 0f;
child.setTranslationX(mExpanded ? expandedTranslation : collapsedTranslation);
child.setAlpha(mExpanded ? 1f : 0f);
LayoutParams params = (LayoutParams) child.getLayoutParams();
params.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation);
params.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation);
params.setAnimationsTarget(child);
nextX = expandLeft ?
childX - mButtonSpacing :
childX + child.getMeasuredWidth() + mButtonSpacing;
}
break;
}
}
#Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(super.generateDefaultLayoutParams());
}
#Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(super.generateLayoutParams(attrs));
}
#Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(super.generateLayoutParams(p));
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return super.checkLayoutParams(p);
}
private static Interpolator sExpandInterpolator = new OvershootInterpolator();
private static Interpolator sCollapseInterpolator = new DecelerateInterpolator(3f);
private static Interpolator sAlphaExpandInterpolator = new DecelerateInterpolator();
private class LayoutParams extends ViewGroup.LayoutParams {
private ObjectAnimator mExpandDir = new ObjectAnimator();
private ObjectAnimator mExpandAlpha = new ObjectAnimator();
private ObjectAnimator mCollapseDir = new ObjectAnimator();
private ObjectAnimator mCollapseAlpha = new ObjectAnimator();
private boolean animationsSetToPlay;
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
mExpandDir.setInterpolator(sExpandInterpolator);
mExpandAlpha.setInterpolator(sAlphaExpandInterpolator);
mCollapseDir.setInterpolator(sCollapseInterpolator);
mCollapseAlpha.setInterpolator(sCollapseInterpolator);
mCollapseAlpha.setProperty(View.ALPHA);
mCollapseAlpha.setFloatValues(1f, 0f);
mExpandAlpha.setProperty(View.ALPHA);
mExpandAlpha.setFloatValues(0f, 1f);
switch (mExpandDirection) {
case EXPAND_UP:
case EXPAND_DOWN:
mCollapseDir.setProperty(View.TRANSLATION_Y);
mExpandDir.setProperty(View.TRANSLATION_Y);
break;
case EXPAND_LEFT:
case EXPAND_RIGHT:
mCollapseDir.setProperty(View.TRANSLATION_X);
mExpandDir.setProperty(View.TRANSLATION_X);
break;
}
}
public void setAnimationsTarget(View view) {
mCollapseAlpha.setTarget(view);
mCollapseDir.setTarget(view);
mExpandAlpha.setTarget(view);
mExpandDir.setTarget(view);
// Now that the animations have targets, set them to be played
if (!animationsSetToPlay) {
mCollapseAnimation.play(mCollapseAlpha);
mCollapseAnimation.play(mCollapseDir);
mExpandAnimation.play(mExpandAlpha);
mExpandAnimation.play(mExpandDir);
animationsSetToPlay = true;
}
}
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
bringChildToFront(mAddButton);
mButtonsCount = getChildCount();
if (mLabelsStyle != 0) {
createLabels();
}
}
private void createLabels() {
Context context = new ContextThemeWrapper(getContext(), mLabelsStyle);
for (int i = 0; i < mButtonsCount; i++) {
FloatingActionButton button = (FloatingActionButton) getChildAt(i);
String title = button.getTitle();
if (button == mAddButton || title == null ||
button.getTag(R.id.fab_label) != null) continue;
TextView label = new TextView(context);
label.setText(button.getTitle());
addView(label);
button.setTag(R.id.fab_label, label);
}
}
public void collapse() {
if (mExpanded) {
mExpanded = false;
mCollapseAnimation.start();
mExpandAnimation.cancel();
if (mListener != null) {
mListener.onMenuCollapsed();
}
}
}
public void toggle() {
if (mExpanded) {
collapse();
} else {
expand();
}
}
public void expand() {
if (!mExpanded) {
mExpanded = true;
mCollapseAnimation.cancel();
mExpandAnimation.start();
if (mListener != null) {
mListener.onMenuExpanded();
}
}
}
public boolean isExpanded() {
return mExpanded;
}
#Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState savedState = new SavedState(superState);
savedState.mExpanded = mExpanded;
return savedState;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof SavedState) {
SavedState savedState = (SavedState) state;
mExpanded = savedState.mExpanded;
if (mRotatingDrawable != null) {
mRotatingDrawable.setRotation(mExpanded ? EXPANDED_PLUS_ROTATION : COLLAPSED_PLUS_ROTATION);
}
super.onRestoreInstanceState(savedState.getSuperState());
} else {
super.onRestoreInstanceState(state);
}
}
public static class SavedState extends BaseSavedState {
public boolean mExpanded;
public SavedState(Parcelable parcel) {
super(parcel);
}
private SavedState(Parcel in) {
super(in);
mExpanded = in.readInt() == 1;
}
#Override
public void writeToParcel(#NonNull Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(mExpanded ? 1 : 0);
}
public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
#Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
#Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
Please Help.
If I understand correctly, the code causing the problem is the one commented, otherwise this line :
whiteOverlay.setVisibility(View.VISIBLE);
You are trying to access your RelativeLayout (R.id.white_opacity) through your FloatingActionsMenu view :
whiteOverlay = (RelativeLayout)findViewById(R.id.white_opacity);
But R.id.white_opacity is a member of your HomeActivity view.
So it's normal that this line returns a null pointer since it doesn't have any member named like this.
Try do find a way to access your HomeActivity view and then call "findViewById()" on it.
If "context" is indeed this HomeActivity, then pass the argument to the updateBackground() method (or set context as a class member) and try somthing like that :
whiteOverlay = ((Activity)context).findViewById(R.id.white_opacity);
Regards.

Reorderable ListView via drag and drop

There are several questions regarding this topic, but they are all very old, from 2010:
Reorder elements of ListView by dragging
reordering of listview items by drag and drop using android
And also, I checked this DevBytes video out:
https://www.youtube.com/watch?v=_BZIvjMgH-Q
The problem with this video is that it just allows to use long press on items to drag, but does not allow to have a "grip" or a "dragging handle".
What is the correct way to acihieve this?
EDIT
I proceeded and tried to use the DevBytes approach, with the following code:
package com.autrilla.shoppinglist;
import android.animation.*;
import android.content.Context;
import android.graphics.*;
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 com.autrilla.shoppinglist.events.DragEvent;
import com.google.common.eventbus.Subscribe;
import com.squareup.otto.Bus;
import hugo.weaving.DebugLog;
import javax.inject.Inject;
import java.util.ArrayList;
public class DynamicListView extends ListView {
private final int SMOOTH_SCROLL_AMOUNT_AT_EDGE = 15;
private final int MOVE_DURATION = 150;
private final int LINE_THICKNESS = 15;
public ArrayList mList;
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;
#Inject
Bus bus;
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) {
ShoppingListApplication.inject(context, this);
bus.register(this);
setOnItemLongClickListener(mOnItemLongClickListener);
setOnScrollListener(mScrollListener);
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
mSmoothScrollAmountAtEdge = (int)(SMOOTH_SCROLL_AMOUNT_AT_EDGE / metrics.density);
}
#Subscribe
#DebugLog
public void onStartDrag(DragEvent event) {
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);
}
/**
* Listens for long clicks on any items in the listview. When a cell has
* been selected, the hover cell is created and set up.
*/
private AdapterView.OnItemLongClickListener mOnItemLongClickListener =
new AdapterView.OnItemLongClickListener() {
#DebugLog
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);
BaseAdapter adapter = ((BaseAdapter)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();
BaseAdapter adapter = ((BaseAdapter)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 items 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(mList, originalItem, getPositionForView(switchView));
((BaseAdapter) getAdapter()).notifyDataSetChanged();
mDownY = mLastEventY;
final int switchViewStartTop = switchView.getTop();
mobileView.setVisibility(View.VISIBLE);
switchView.setVisibility(View.INVISIBLE);
updateNeighborViewsForID(mMobileItemId);
final ViewTreeObserver observer = getViewTreeObserver();
assert observer != null;
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 items.
*/
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 setList(ArrayList list) {
mList = list;
}
/**
* 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 AbsListView.OnScrollListener mScrollListener = new AbsListView.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();
}
}
}
};
}
And on my Adapter:
#Override
public View getView(final int position, View view, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (view == null)
view = inflater.inflate(R.layout.store_list_item, parent, false);
ButterKnife.inject(this, view);
storeName.setText(mShoppingLists.get(position).getName());
editButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final FragmentManager manager = ((Activity) mContext).getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
ShoppingListEditFragment shoppingListEditFragment =
ShoppingListEditFragment.newInstance(mShoppingLists.get(position).getId());
transaction.replace(R.id.container, shoppingListEditFragment, "shoppinglistedit");
transaction.addToBackStack(null);
transaction.commit();
}
});
handle.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
bus.post(new DragEvent());
return true;
}
});
return view;
}
But the ListView does not scroll. Why is this?
Check this library: https://github.com/fada21/HydraListAndroid
They resolve several ListView stuff like add/remove (of one item) animations and one of the things is the option for drag and drop.
I was facing the same problem some weeks ago, and at least I can recommend this library which is a fork of the popular drag-sort-listview, but is still maintained.
I would have commented, as this is not worth any bounty... but my reputation is not high enough.

Android Drag and Drop functionality

Is there some way to implement drag and drop functionality in API 8 Android?
Looked here, but it appears after API 11.
I need to drag view from one LinearLayout to another. Ho can i implement this on API 8?
Try like this
import android.app.Activity;
import android.content.ClipData;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.DragEvent;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnDragListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class MainActivity extends Activity {
ListView source = null;
TextView target1 = null;
TextView target2 = null;
TextView target3 = null;
String[] listItems = {"Samsung", "Apple", "Google", "Nokia"};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
source = (ListView)findViewById(R.id.dragSource);
target1 = (TextView) findViewById(R.id.dragTarget1);
target2 = (TextView) findViewById(R.id.dragTarget2);
target3 = (TextView) findViewById(R.id.dragTarget3);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, listItems);
source.setAdapter(adapter);
// Start Drag
source.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
String item = listItems[position];
ClipData data = ClipData.newPlainText("DragIt", item);
source.startDrag(data, new MyShadowBuilder(view), null, 0);
}
});
// Handle Drag
target1.setOnDragListener(new MyDragListener());
target2.setOnDragListener(new MyDragListener());
target3.setOnDragListener(new MyDragListener());
}
// Drag Shadow
private class MyShadowBuilder extends DragShadowBuilder {
public MyShadowBuilder(View v) {
super(v);
}
#Override
public void onDrawShadow(Canvas canvas) {
// Set Drag image background or anything you want
int width = getView().getWidth();
int height = getView().getHeight();
Paint paint = new Paint();
paint.setColor(0x55858585);
canvas.drawRect(new Rect(0, 0, width, height), paint);
super.onDrawShadow(canvas);
}
#Override
public void onProvideShadowMetrics(Point shadowSize,
Point shadowTouchPoint) {
int width = getView().getWidth();
int height = getView().getHeight();
shadowSize.set(width, height);
shadowTouchPoint.set(width/2, height);
}
}
// Drag Listener
private class MyDragListener implements OnDragListener {
private final int DEFAULT_BG_COLOR = 0xFF858585;
private final int HIGHLIGHT_BG_COLOR = 0xFF0000FF;
#Override
public boolean onDrag(View v, DragEvent event) {
if(event.getAction() == DragEvent.ACTION_DRAG_ENTERED) {
v.setBackgroundColor(HIGHLIGHT_BG_COLOR);
}
else if(event.getAction() == DragEvent.ACTION_DRAG_EXITED) {
v.setBackgroundColor(DEFAULT_BG_COLOR);
}
else if(event.getAction() == DragEvent.ACTION_DROP) {
// Perform drop
ClipData clip = event.getClipData();
ClipData.Item item = clip.getItemAt(0);
String text = item.getText().toString();
((TextView) v).setText(text);
v.setBackgroundColor(DEFAULT_BG_COLOR);
}
// Send true to listen All Drag Events.
return true;
}
}
}
XML is like:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="161dp"
android:layout_height="fill_parent"
android:background="#FF858585"
android:orientation="vertical" >
<TextView
android:id="#+id/dragTarget1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="0.04"
android:text="Drop Here"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/dragTarget2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="0.04"
android:text="Drop Here"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/dragTarget3"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="0.04"
android:text="Drop Here"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/dragSource"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
Try another approach by using WindowManager :
package com.example.cooldraganddrop;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ImageView;
public class CoolDragAndDropGridView extends SpanVariableGridView implements `View.OnTouchListener {`
private static final int ITEM_HOVER_DELAY = 450;
private int mDragPointX;
private int mDragPointY;
private int mDragOffsetX;
private int mDragOffsetY;
private int mDragPosition = AdapterView.INVALID_POSITION;
private int mDropPosition = AdapterView.INVALID_POSITION;
private int mCurrentPosition = AdapterView.INVALID_POSITION;
private Runnable mDelayedOnDragRunnable = null;
ScrollingStrategy mScrollingStrategy = null;
WindowManager mWindowManager = null;
WindowManager.LayoutParams mWindowParams = null;
private ImageView mDragImageView = null;
private boolean mDragAndDropStarted = false;
private DragAndDropListener mDragAndDropListener = null;
private OnTrackTouchEventsListener mOnTrackTouchEventsListener = null;
public static interface OnTrackTouchEventsListener {
void trackTouchEvents(final MotionEvent motionEvent);
};
public static interface DragAndDropListener {
void onDragItem(int from);
void onDraggingItem(int from, int to);
void onDropItem(int from, int to);
boolean isDragAndDropEnabled(int position);
}
public CoolDragAndDropGridView(Context context) {
super(context);
initialize();
}
public CoolDragAndDropGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
public CoolDragAndDropGridView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
private void initialize() {
setOnTouchListener(this);
setChildrenDrawingOrderEnabled(true);
}
public void startDragAndDrop() {
mDragAndDropStarted = true;
}
public void setDragAndDropListener(DragAndDropListener dragAndDropListener) {
mDragAndDropListener = dragAndDropListener;
}
private void destroyDragImageView() {
if (mDragImageView != null) {
mWindowManager.removeView(mDragImageView);
BitmapDrawable bitmapDrawable = (BitmapDrawable) mDragImageView.getDrawable();
if (bitmapDrawable != null) {
final Bitmap bitmap = bitmapDrawable.getBitmap();
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
}
mDragImageView.setImageDrawable(null);
mDragImageView = null;
}
}
private ImageView createDragImageView(final View v, final int x, final int y) {
v.destroyDrawingCache();
v.setDrawingCacheEnabled(true);
Bitmap bm = Bitmap.createBitmap(v.getDrawingCache());
mDragPointX = x - v.getLeft();
mDragPointY = y - v.getTop();
mWindowParams = new WindowManager.LayoutParams();
mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
mWindowParams.x = x - mDragPointX + mDragOffsetX;
mWindowParams.y = y - mDragPointY + mDragOffsetY;
mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
mWindowParams.format = PixelFormat.TRANSLUCENT;
mWindowParams.alpha = 0.7f;
mWindowParams.windowAnimations = 0;
ImageView iv = new ImageView(getContext());
iv.setBackgroundColor(Color.parseColor("#ff555555"));
iv.setImageBitmap(bm);
mWindowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);// "window"
mWindowManager.addView(iv, mWindowParams);
return iv;
}
private void startDrag(final int x, final int y) {
final View v = getChildAt(mDragPosition);
destroyDragImageView();
mDragImageView = createDragImageView(v, x, y);
v.setVisibility(View.INVISIBLE);
if (mDragAndDropListener != null) {
mDragAndDropListener.onDragItem(mDragPosition);
}
}
#Override
protected int getChildDrawingOrder(int childCount, int i) {
if (mCurrentPosition == -1)
return i;
else if (i == childCount - 1)
return mCurrentPosition;
else if (i >= mCurrentPosition)
return i + 1;
return i;
}
private void onDrop() {
destroyDragImageView();
removeCallbacks(mDelayedOnDragRunnable);
View v = getChildAt(mDropPosition);
v.setVisibility(View.VISIBLE);
v.clearAnimation();
if (mDragAndDropListener != null && mDropPosition != AdapterView.INVALID_POSITION) {
mDragAndDropListener.onDropItem(mDragPosition, mDropPosition);
}
mDragPosition = mDropPosition = mCurrentPosition = AdapterView.INVALID_POSITION;
mDragAndDropStarted = false;
}
public void setScrollingStrategy(ScrollingStrategy scrollingStrategy) {
mScrollingStrategy = scrollingStrategy;
}
private void onDrag(final int x, final int y) {
if (mScrollingStrategy != null && mScrollingStrategy.performScrolling(x, y, this)) {
removeCallbacks(mDelayedOnDragRunnable);
return;
}
final int tempDropPosition = pointToPosition(mCurrentPosition, x, y);
if (mDragAndDropListener != null && mDropPosition != tempDropPosition && tempDropPosition != AdapterView.INVALID_POSITION) {
removeCallbacks(mDelayedOnDragRunnable);
if (mDragAndDropListener.isDragAndDropEnabled(tempDropPosition)) {
mDropPosition = tempDropPosition;
mDelayedOnDragRunnable = new Runnable() {
#Override
public void run() {
mDragAndDropListener.onDraggingItem(mCurrentPosition, tempDropPosition);
performDragAndDropSwapping(mCurrentPosition, tempDropPosition);
final int nextDropPosition = pointToPosition(tempDropPosition, x, y);
if (nextDropPosition == AdapterView.INVALID_POSITION) {
mCurrentPosition = mDropPosition = tempDropPosition;
}
}
};
postDelayed(mDelayedOnDragRunnable, ITEM_HOVER_DELAY);
} else {
mDropPosition = mDragPosition;
}
}
if (mDragImageView != null) {
mWindowParams.x = x - mDragPointX + mDragOffsetX;
mWindowParams.y = y - mDragPointY + mDragOffsetY;
mWindowManager.updateViewLayout(mDragImageView, mWindowParams);
}
}
public void setOnTrackTouchEventListener(OnTrackTouchEventsListener onTrackTouchEventsListener) {
mOnTrackTouchEventsListener = onTrackTouchEventsListener;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mOnTrackTouchEventsListener != null) {
mOnTrackTouchEventsListener.trackTouchEvents(event);
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if (mDragAndDropListener != null && mDragAndDropStarted) {
mDragAndDropStarted = false;
getParent().requestDisallowInterceptTouchEvent(true);
return launchDragAndDrop(event);
}
break;
default:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mDragAndDropStarted = false;
getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return super.onInterceptTouchEvent(event);
}
private boolean launchDragAndDrop(final MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
mCurrentPosition = mDragPosition = mDropPosition = pointToPosition(mDragPosition, x, y);
if (mDragPosition != AdapterView.INVALID_POSITION && mDragAndDropListener.isDragAndDropEnabled(mDragPosition)) {
mDragOffsetX = (int) (event.getRawX() - x);
mDragOffsetY = (int) (event.getRawY() - y);
startDrag(x, y);
return true;
}
return false;
}
#Override
public boolean onTouch(View view, MotionEvent event) {
if (mDragPosition != AdapterView.INVALID_POSITION && mDragImageView != null) {
final int x = (int) event.getX();
final int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
mDragOffsetX = (int) (event.getRawX() - x);
mDragOffsetY = (int) (event.getRawY() - y);
onDrag(x, y);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
onDrop();
resetLongClickTransition();
getParent().requestDisallowInterceptTouchEvent(false);
return false;
default:
}
return true;
}
return false;
}
}
Sample using this approach of a drag and drop is here

Implement Swipe in ListView

I want to implement Swipe in ListView like in Samsung Android Phones calling function
I have a list shown in below image :
Now when I swipe right side at some distance of swipe from left i.e 25% distance it just changed background, like call function in Samsung device or like SwipeListView:
need solution for this.
try this code :
main.xml file
<ListView
android:id="#+id/listView1"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
raw.xml
<TextView
android:id="#+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:gravity="left"
android:text="ABC"
android:textSize="22sp" />
<LinearLayout
android:id="#+id/delete_lay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#55ff0000"
android:visibility="gone" >
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
android:src="#drawable/ic_menu_delete"
android:visibility="visible" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
android:gravity="left"
android:text="Deleting..."
android:textColor="#ff0000"
android:textSize="22sp"
android:textStyle="bold" />
</LinearLayout>
MainActivity.java
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import de.timroes.swipetodismiss.SwipeDismissList;
import de.timroes.swipetodismiss.SwipeDismissList.SwipeDirection;
import de.timroes.swipetodismiss.SwipeDismissList.UndoMode;
import de.timroes.swipetodismiss.SwipeDismissList.Undoable;
public class MainActivity extends Activity {
ArrayList<String> listData;
ListView listView;
ArrayAdapter<String> adapter;
SwipeListAdapter listAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.listView1);
listData = new ArrayList<String>();
listData.add("item 1");
listData.add("item 2");
listData.add("item 3");
listData.add("item 4");
listData.add("item 5");
listData.add("item 6");
listData.add("item 7");
listData.add("item 8");
listData.add("item 9");
listData.add("item 10");
listData.add("item 11");
listData.add("item 12");
listData.add("item 13");
listData.add("item 14");
listData.add("item 15");
listData.add("item 16");
listData.add("item 17");
listAdapter = new SwipeListAdapter(MainActivity.this);
listView.setAdapter(listAdapter);
listAdapter.notifyDataSetChanged();
SwipeDismissList.OnDismissCallback callback = new SwipeDismissList.OnDismissCallback() {
#Override
public Undoable onDismiss(AbsListView listView, int position) {
View view = (View) listView.getChildAt(0);
view.animate().alpha(1).setDuration(200).translationX(10);
listAdapter.remove(position);
return null;
}
};
UndoMode mode = SwipeDismissList.UndoMode.SINGLE_UNDO;
SwipeDismissList swipeList = new SwipeDismissList(listView, callback,
mode);
swipeList.setSwipeDirection(SwipeDirection.END);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
class SwipeListAdapter extends BaseAdapter {
Context mContext;
public SwipeListAdapter(Context context) {
this.mContext = context;
}
#Override
public int getCount() {
return listData.size();
}
#Override
public Object getItem(int position) {
return listData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
private void remove(int pos) {
listData.remove(pos);
notifyDataSetChanged();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
try {
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.raw, null);
}
TextView textView = (TextView) convertView
.findViewById(R.id.name);
textView.setText(listData.get(position));
RelativeLayout layout = (RelativeLayout) convertView
.findViewById(R.id.parentLayout);
LayoutParams params = new LayoutParams(
LayoutParams.MATCH_PARENT, 50);
layout.setLayoutParams(params);
} catch (Exception e) {
e.printStackTrace();
}
return convertView;
}
}
}
SwipeDismissList.java
import static com.nineoldandroids.view.ViewHelper.setAlpha;
import static com.nineoldandroids.view.ViewHelper.setTranslationX;
import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.ValueAnimator;
public final class SwipeDismissList implements View.OnTouchListener {
// Cached ViewConfiguration and system-wide constant values
private int mSlop;
private int mMinFlingVelocity;
private int mMaxFlingVelocity;
private long mAnimationTime;
// Fixed properties
private AbsListView mListView;
private OnDismissCallback mCallback;
private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero
// Transient properties
private SortedSet<PendingDismissData> mPendingDismisses = new TreeSet<PendingDismissData>();
private int mDismissAnimationRefCount = 0;
private float mDownX;
private boolean mSwiping;
private VelocityTracker mVelocityTracker;
private int mDownPosition;
private View mDownView;
private boolean mPaused;
private float mDensity;
private boolean mSwipeDisabled;
private UndoMode mMode;
private List<Undoable> mUndoActions;
private Handler mHandler;
private PopupWindow mUndoPopup;
private TextView mUndoText;
private Button mUndoButton;
private SwipeDirection mSwipeDirection = SwipeDirection.BOTH;
private int mAutoHideDelay = 5000;
private String mDeleteString = "Item deleted";
private String mDeleteMultipleString = "%d items deleted";
private boolean mTouchBeforeAutoHide = true;
private int mDelayedMsgId;
View frontView;
View backView;
RelativeLayout layout ;
public enum UndoMode {
SINGLE_UNDO,
MULTI_UNDO,
COLLAPSED_UNDO
};
public enum SwipeDirection {
BOTH,
START,
END
}
public interface OnDismissCallback {
Undoable onDismiss(AbsListView listView, int position);
}
public abstract static class Undoable {
public String getTitle() {
return null;
}
public abstract void undo();
public void discard() { }
}
public SwipeDismissList(AbsListView listView, OnDismissCallback callback) {
this(listView, callback, UndoMode.SINGLE_UNDO);
}
public SwipeDismissList(AbsListView listView, OnDismissCallback callback, UndoMode mode) {
if(listView == null) {
throw new IllegalArgumentException("listview must not be null.");
}
mHandler = new HideUndoPopupHandler();
mListView = listView;
mCallback = callback;
mMode = mode;
ViewConfiguration vc = ViewConfiguration.get(listView.getContext());
mSlop = vc.getScaledTouchSlop();
mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
mAnimationTime = listView.getContext().getResources().getInteger(
android.R.integer.config_shortAnimTime);
mDensity = mListView.getResources().getDisplayMetrics().density;
// -- Load undo popup --
LayoutInflater inflater = (LayoutInflater) mListView.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.undo_popup, null);
mUndoButton = (Button)v.findViewById(R.id.undo);
mUndoButton.setOnClickListener(new UndoHandler());
mUndoButton.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// If user tabs "undo" button, reset delay time to remove popup
mDelayedMsgId++;
return false;
}
});
mUndoText = (TextView)v.findViewById(R.id.text);
mUndoPopup = new PopupWindow(v);
mUndoPopup.setAnimationStyle(R.style.fade_animation);
// Get scren width in dp and set width respectively
int xdensity = (int)(mListView.getContext().getResources().getDisplayMetrics().widthPixels / mDensity);
if(xdensity < 300) {
mUndoPopup.setWidth((int)(mDensity * 280));
} else if(xdensity < 350) {
mUndoPopup.setWidth((int)(mDensity * 300));
} else if(xdensity < 500) {
mUndoPopup.setWidth((int)(mDensity * 330));
} else {
mUndoPopup.setWidth((int)(mDensity * 450));
}
mUndoPopup.setHeight((int)(mDensity * 56));
// -- END Load undo popu --
listView.setOnTouchListener(this);
listView.setOnScrollListener(this.makeScrollListener());
switch(mode) {
case SINGLE_UNDO:
mUndoActions = new ArrayList<Undoable>(1);
break;
default:
mUndoActions = new ArrayList<Undoable>(10);
break;
}
}
private void setEnabled(boolean enabled) {
mPaused = !enabled;
}
public void setAutoHideDelay(int delay) {
mAutoHideDelay = delay;
}
public void setSwipeDirection(SwipeDirection direction) {
mSwipeDirection = direction;
}
public void setUndoString(String msg) {
mDeleteString = msg;
}
public void setUndoMultipleString(String msg) {
mDeleteMultipleString = msg;
}
public void setRequireTouchBeforeDismiss(boolean require) {
mTouchBeforeAutoHide = require;
}
public void discardUndo() {
for(Undoable undoable : mUndoActions) {
undoable.discard();
}
mUndoActions.clear();
mUndoPopup.dismiss();
}
private AbsListView.OnScrollListener makeScrollListener() {
return new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
}
#Override
public void onScroll(AbsListView absListView, int i, int i1, int i2) {
}
};
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (this.mSwipeDisabled) {
return false;
}
if (mViewWidth < 2) {
mViewWidth = mListView.getWidth();
}
switch (motionEvent.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
if (mPaused) {
return false;
}
// TODO: ensure this is a finger, and set a flag
// Find the child view that was touched (perform a hit test)
Rect rect = new Rect();
int childCount = mListView.getChildCount();
int[] listViewCoords = new int[2];
mListView.getLocationOnScreen(listViewCoords);
int x = (int) motionEvent.getRawX() - listViewCoords[0];
int y = (int) motionEvent.getRawY() - listViewCoords[1];
View child;
for (int i = 0; i < childCount; i++) {
child = mListView.getChildAt(i);
child.getHitRect(rect);
if (rect.contains(x, y)) {
mDownView = child;
break;
}
}
if (mDownView != null) {
mDownX = motionEvent.getRawX();
mDownPosition = mListView.getPositionForView(mDownView);
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(motionEvent);
}
view.onTouchEvent(motionEvent);
return true;
}
case MotionEvent.ACTION_UP: {
if (mVelocityTracker == null) {
break;
}
float deltaX = motionEvent.getRawX() - mDownX;
mVelocityTracker.addMovement(motionEvent);
mVelocityTracker.computeCurrentVelocity(1000);
float velocityX = Math.abs(mVelocityTracker.getXVelocity());
float velocityY = Math.abs(mVelocityTracker.getYVelocity());
boolean dismiss = false;
boolean dismissRight = false;
if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) {
dismiss = true;
dismissRight = deltaX > 0;
} else if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity
&& velocityY < velocityX && mSwiping && isDirectionValid(mVelocityTracker.getXVelocity())
&& deltaX >= mViewWidth * 0.2f) {
dismiss = true;
dismissRight = mVelocityTracker.getXVelocity() > 0;
}
if (dismiss) {
// dismiss
final View downView = frontView; // mDownView gets null'd before animation ends
final int downPosition = mDownPosition;
++mDismissAnimationRefCount;
animate(frontView)
.translationX(dismissRight ? mViewWidth : -mViewWidth)
.alpha(0)
.setDuration(mAnimationTime)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
performDismiss(downView, downPosition);
}
});
} else {
// cancel
animate(frontView)
.translationX(0)
.alpha(1)
.setDuration(mAnimationTime)
.setListener(null);
}
mVelocityTracker = null;
mDownX = 0;
mDownView = null;
mDownPosition = ListView.INVALID_POSITION;
mSwiping = false;
if(backView != null) {
backView.setVisibility(View.GONE);
frontView.setVisibility(View.VISIBLE);
setTranslationX(frontView, 0);
layout = null;
backView = null;
frontView = null;
}
break;
}
case MotionEvent.ACTION_MOVE: {
if(mTouchBeforeAutoHide && mUndoPopup.isShowing()) {
// Send a delayed message to hide popup
mHandler.sendMessageDelayed(mHandler.obtainMessage(mDelayedMsgId),
mAutoHideDelay);
}
if (mVelocityTracker == null || mPaused) {
break;
}
mVelocityTracker.addMovement(motionEvent);
float deltaX = motionEvent.getRawX() - mDownX;
// Only start swipe in correct direction
if(isDirectionValid(deltaX)) {
if (Math.abs(deltaX) > mSlop) {
mSwiping = true;
mListView.requestDisallowInterceptTouchEvent(true);
// Cancel ListView's touch (un-highlighting the item)
MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL
| (motionEvent.getActionIndex()
<< MotionEvent.ACTION_POINTER_INDEX_SHIFT));
mListView.onTouchEvent(cancelEvent);
}
} else {
mDownX = motionEvent.getRawX();
deltaX = 0;
}
if(layout == null) {
layout = (RelativeLayout) mDownView;
frontView = layout.getChildAt(0);
backView = layout.getChildAt(1);
}
if(deltaX > 50) {
backView.setVisibility(View.VISIBLE);
}
if (mSwiping) {
setTranslationX(frontView, deltaX);
setAlpha(frontView, Math.max(0f, Math.min(1f,
1f - 2f * Math.abs(deltaX) / mViewWidth)));
return true;
}
break;
}
}
return false;
}
private boolean isDirectionValid(float deltaX) {
int rtlSign = 1;
// On API level 17 and above, check if we are in a Right-To-Left layout
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
if(mListView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
rtlSign = -1;
}
}
// Check if swipe has been done in the corret direction
switch(mSwipeDirection) {
default:
case BOTH:
return true;
case START:
return rtlSign * deltaX < 0;
case END:
return rtlSign * deltaX > 0;
}
}
class PendingDismissData implements Comparable<PendingDismissData> {
public int position;
public View view;
public PendingDismissData(int position, View view) {
this.position = position;
this.view = view;
}
#Override
public int compareTo(PendingDismissData other) {
// Sort by descending position
return other.position - position;
}
}
private void performDismiss(final View dismissView, final int dismissPosition) {
// Animate the dismissed list item to zero-height and fire the dismiss callback when
// all dismissed list item animations have completed. This triggers layout on each animation
// frame; in the future we may want to do something smarter and more performant.
final ViewGroup.LayoutParams lp = dismissView.getLayoutParams();
final int originalHeight = dismissView.getHeight();
ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime);
animator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
--mDismissAnimationRefCount;
if (mDismissAnimationRefCount == 0) {
// No active animations, process all pending dismisses.
for(PendingDismissData dismiss : mPendingDismisses) {
if(mMode == UndoMode.SINGLE_UNDO) {
for(Undoable undoable : mUndoActions) {
undoable.discard();
}
mUndoActions.clear();
}
Undoable undoable = mCallback.onDismiss(mListView, dismiss.position);
if(undoable != null) {
mUndoActions.add(undoable);
}
mDelayedMsgId++;
}
if(!mUndoActions.isEmpty()) {
changePopupText();
changeButtonLabel();
// Show undo popup
mUndoPopup.showAtLocation(mListView,
Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM,
0, (int)(mDensity * 15));
// Queue the dismiss only if required
if(!mTouchBeforeAutoHide) {
// Send a delayed message to hide popup
mHandler.sendMessageDelayed(mHandler.obtainMessage(mDelayedMsgId),
mAutoHideDelay);
}
}
ViewGroup.LayoutParams lp;
for (PendingDismissData pendingDismiss : mPendingDismisses) {
// Reset view presentation
setAlpha(pendingDismiss.view, 1f);
setTranslationX(pendingDismiss.view, 0);
lp = pendingDismiss.view.getLayoutParams();
lp.height = originalHeight;
pendingDismiss.view.setLayoutParams(lp);
}
mPendingDismisses.clear();
}
}
});
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
lp.height = (Integer) valueAnimator.getAnimatedValue();
dismissView.setLayoutParams(lp);
}
});
mPendingDismisses.add(new PendingDismissData(dismissPosition, dismissView));
animator.start();
}
private void changePopupText() {
String msg = "";
if(mUndoActions.size() > 1 && mDeleteMultipleString != null) {
msg = String.format(mDeleteMultipleString, mUndoActions.size());
} else if(mUndoActions.size() >= 1) {
// Set title from single undoable or when no multiple deletion string
// is given
if(mUndoActions.get(mUndoActions.size() - 1).getTitle() != null) {
msg = mUndoActions.get(mUndoActions.size() - 1).getTitle();
} else {
msg = mDeleteString;
}
}
mUndoText.setText(msg);
}
private void changeButtonLabel() {
String msg;
if(mUndoActions.size() > 1 && mMode == UndoMode.COLLAPSED_UNDO) {
msg = mListView.getResources().getString(R.string.undoall);
} else {
msg = mListView.getResources().getString(R.string.undo);
}
mUndoButton.setText(msg);
}
/**
* Takes care of undoing a dismiss. This will be added as a
* {#link View.OnClickListener} to the undo button in the undo popup.
*/
private class UndoHandler implements View.OnClickListener {
public void onClick(View v) {
if(!mUndoActions.isEmpty()) {
switch(mMode) {
case SINGLE_UNDO:
mUndoActions.get(0).undo();
mUndoActions.clear();
break;
case COLLAPSED_UNDO:
Collections.reverse(mUndoActions);
for(Undoable undo : mUndoActions) {
undo.undo();
}
mUndoActions.clear();
break;
case MULTI_UNDO:
mUndoActions.get(mUndoActions.size() - 1).undo();
mUndoActions.remove(mUndoActions.size() - 1);
break;
}
}
// Dismiss dialog or change text
if(mUndoActions.isEmpty()) {
mUndoPopup.dismiss();
} else {
changePopupText();
changeButtonLabel();
}
mDelayedMsgId++;
}
}
/**
* Handler used to hide the undo popup after a special delay.
*/
private class HideUndoPopupHandler extends Handler {
#Override
public void handleMessage(Message msg) {
if(msg.what == mDelayedMsgId) {
// Call discard on any element
for(Undoable undo : mUndoActions) {
undo.discard();
}
mUndoActions.clear();
mUndoPopup.dismiss();
}
}
}
/**
* Enable/disable swipe.
*/
public void setSwipeDisabled(boolean disabled) {
this.mSwipeDisabled = disabled;
}
}
enjoy!
Use a ListView extension such as android-swipelistview.
You may also need to refer to the demo source. Instructions for setting up the demo can be found here.
To make a phone call when the user swipes left, add the below code to the instance of SwipeListView:
src > activities > SwipeListViewExampleActivity.java > onCreate()
swipeListView.setSwipeListViewListener(new BaseSwipeListViewListener() {
#Override
public void onOpened(int position, boolean toRight) {
if(!toRight) {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:0123456789"));
startActivity(intent);
}
}
});
For swipe right you can set it to dismiss which should be sufficient for what you are trying to achieve.
res > layout > swipe-list-view-activity.xml > SwipeListView
swipe:swipeActionRight="dismiss"
you can use this library https://github.com/timroes/EnhancedListView An Android ListView with enhanced functionality (e.g. Swipe To Dismiss or Undo).

Categories

Resources