Just came across this app RetailMeNot Coupons. The list view used is custom and looks pretty cool. Initially, the list show smaller image of a child-item. As the user scrolls up, the child item expands fully to reveal a bigger image.
Out of curiosity, I plan to build a similar list-view in a test app.
I am sure this is a custom list-view, has anyone seen a library or implementation of a similar list-view?
Look at this library
https://github.com/ksoichiro/Android-ObservableScrollView
May be some customization on this library can help you.
I have tried directly working with listviewscroll.
Is not perfect but is a start
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getListView().setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
for (int j=0; j<visibleItemCount; j++) {
if(absListView.getChildAt(j).getClass() == FrameLayout.class) {
FrameLayout item = (FrameLayout) absListView.getChildAt(j);
String result = "";
if (item != null) {
RelativeLayout relative1 = (RelativeLayout) item.getChildAt(0);
ImageView promoImage = (ImageView) relative1.getChildAt(0);
View promoOverlay = (View) relative1.getChildAt(1);
RelativeLayout relative2 = (RelativeLayout) relative1.getChildAt(2);
ImageView brandImage = (ImageView) relative2.getChildAt(0);
TextView promoText = (TextView) relative2.getChildAt(1);
item.setLayoutParams(new AbsListView.LayoutParams(item.getWidth(), (int) minHeight));
if (item.getTop() < maxHeight) {
if (item.getTop() >= 0) {
float topPercent = item.getTop() / maxHeight;
float newHeight = minHeight + ((maxHeight - minHeight) * (1 - topPercent));
if (newHeight <= maxHeight) {
item.setLayoutParams(new AbsListView.LayoutParams(item.getWidth(), (int) newHeight));
promoOverlay.setAlpha(topPercent / 2);
} else {
item.setLayoutParams(new AbsListView.LayoutParams(item.getWidth(), (int) maxHeight));
if (item.getTop() > 0) {
promoOverlay.setAlpha(0.5f);
} else {
promoOverlay.setAlpha(0f);
}
}
} else {
float topPercent = (-item.getTop()) / maxHeight;
float newHeight = maxHeight - ((maxHeight - minHeight) * (topPercent));
if (newHeight >= minHeight) {
item.setLayoutParams(new AbsListView.LayoutParams(item.getWidth(), (int) newHeight));
promoOverlay.setAlpha(topPercent / 2);
} else {
item.setLayoutParams(new AbsListView.LayoutParams(item.getWidth(), (int) minHeight));
}
promoOverlay.setAlpha(0);
}
} else {
if (item.getTop() > 0) {
item.setLayoutParams(new AbsListView.LayoutParams(item.getWidth(), (int) minHeight));
promoOverlay.setAlpha(0.5f);
}
}
}
}
}
}
});
}
Related
My requirement is scrolling a ImageView left and right on top of a list of data in the Recyclerview. When Recyclerview is scrolled up the ImageView will move towards right until list reaches to the end and vice versa.
I have tried but facing few complexity any suggestions and reference will be very helpful.Or any logic will also be appreciated.
The problem what I facing now is that not able to handle the values in the addOnScrollListener
Sharing my code in the github: Github Link
MainActivity Code:
Sharing few lines of code:
public class MainActivity extends AppCompatActivity implements View.OnClickListener, ClickEventLisener {
HorizontalScrollView v_scroll;
Button btn_left, btn_right;
int pos = 0;
int temppos = 0;
int initialpos = 0;
RecyclerView rv_recycler;
UserApapter adapter;
List<Users> user = new ArrayList<>();
Context mContext;
boolean isFirsttime = true;
ImageView iv_image;
String imageurl = "https://www.isometrix.com/wp-content/uploads/2017/03/featured-images-about.jpg";
int screenwidth = 0, imagewidth = 0;
int scroolby_val = 0;
#SuppressLint("ClickableViewAccessibility")
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
iv_image = findViewById(R.id.iv_image);
v_scroll = findViewById(R.id.v_scroll);
btn_left = findViewById(R.id.btn_left);
btn_right = findViewById(R.id.btn_right);
rv_recycler = findViewById(R.id.rv_recycler);
btn_left.setOnClickListener(this);
btn_right.setOnClickListener(this);
v_scroll.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
filllist();
getScreenSize();
setupimage();
getleftscroll();
// when scrolled to the leftmost position the variables are initialized
v_scroll.setOnScrollChangeListener(new View.OnScrollChangeListener() {
#Override
public void onScrollChange(View view, int i, int i1, int i2, int i3) {
initialpos = i;
if (i == 0) {
temppos = 0;
} else {
}
}
});
///not able to understand how to handle the values
//handling the scroll
rv_recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
getleftscroll();
if (dy > 0) {
temppos = temppos + scroolby_val;
v_scroll.scrollTo(temppos, 0);
Log.d("Scroll", "ScrollUp");
Log.d("temppos", "" + temppos);
} else {
if (initialpos != 0) {
temppos = temppos - scroolby_val;
v_scroll.scrollTo(temppos, 0);
Log.d("temppos1", "" + temppos);
Log.d("Scroll", "ScrollDown");
}
}
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
// Do something
} else if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
// Do something
} else {
// Do something
}
}
});
}
private void getScreenSize() {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels;
Log.d("ScreenWidth", "" + width);
screenwidth = width;
}
private void setupimage() {
Glide.with(mContext)
.asBitmap()
.load(imageurl)
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap bitmap, Transition<? super Bitmap> transition) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
Log.d("ImageWitdh", "" + w);
iv_image.setImageBitmap(bitmap);
imagewidth = w;
}
});
}
/*
Logic I have implemented:calculated Imagewidth and screenwidth. Then minus it (imagewidth > screenwidth) and then divide it by the number of values in the array.
if the value is 0 then scrolling it by 1 per scroll or if the returned value is 3 then scrolling it by 3.
IOS team have done this way and working good for them but I am facing a minor issue here.
*/
private void getleftscroll() {
imagewidth = 2000;
if (imagewidth > screenwidth) {
int leftoutscreen = imagewidth - screenwidth;
scroolby_val = leftoutscreen / user.size();
if (scroolby_val == 0) {
scroolby_val = 1;
}
// temppos = 0;
Log.d("scroolby_val", "" + scroolby_val);
}
}
///button is just for demo purpose///
#Override
public void onClick(View v) {
if (v == btn_left) {
if (initialpos != 0) {
temppos = temppos - pos--;
v_scroll.scrollTo(temppos, 0);
Log.d("temppos", "" + temppos);
}
} else if (v == btn_right) {
temppos = temppos + pos++;
v_scroll.scrollTo(temppos, -temppos);
Log.d("temppos", "" + temppos);
}
}
private void filllist() {
user.clear();
for (int i = 0; i <= 300; i++) {
user.add(new Users(String.valueOf(i + 1), "name " + i));
}
inflateadapter();
}
private void inflateadapter() {
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
adapter = new UserApapter(this, R.layout.row_users, user, this);
rv_recycler.setLayoutManager(mLayoutManager);
rv_recycler.setAdapter(adapter);
}
#Override
public void Currentposition(int position) {
Log.d("Currentposition", "" + position);
if (position == user.size() - 1) {
if (scroolby_val == 1) {
Log.d("scroolby_val", "" + scroolby_val);
Log.d("Inside", "Inside");
// v_scroll.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
// temppos = 1;
}
} else if (position == 0) {
temppos = 0;
}
pos = position;
}
#Override
public void ClickEventLisener(View view, int position) {
Toast.makeText(mContext, "" + position, Toast.LENGTH_SHORT).show();
}
}
Few things you may want to consider-
Don't use dy values from onScrolled callback directly to set scroll of v_scroll. There should be a logic to map scroll state of recycler view to the required scroll in v_scroll.
Don't use dy at all. Logic to set scroll of v_scroll should be based on the number of items in the list and the first/last visible child in it. (RecyclerView provides methods to get that.) You can run that logic from onScrolled callback.
I have been trying to make a gridview with drag and drop functionality along with one cell of different size. I have already made the the grid drag and drop and its working fine. you can check the code from here
but I want it to be like this and purely dynamic as I will be draging and dropping the other which will be replaced and resized automatically
Updated with new code that accommodates resizing of cells.
Your question refers to GridView but the code you supplied doesn't mention GridView but uses GridLayout instead, so I am assuming that GridLayout is the right layout.
I have put together a demo using a mocked-up layout with one 2x2 tile. I have modified the code that you have supplied to accommodate the 2x2 tile. Other than the code that I added to implement the 2x2 tile, the only other change to MainAcitivity was to the calculateNextIndex method that uses a different way of calculating the index at an (x, y) position. Layouts and the LongPressListener class were also mocked up since they were not supplied.
Here is a video of the demo:
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final int ITEMS = 10;
private GridLayout mGrid;
private ScrollView mScrollView;
private ValueAnimator mAnimator;
private Boolean isScroll = false;
private GridLayout.Spec m1xSpec = GridLayout.spec(GridLayout.UNDEFINED, 1);
private GridLayout.Spec m2xSpec = GridLayout.spec(GridLayout.UNDEFINED, 2);
private int mBaseWidth;
private int mBaseHeight;
private int mBaseMargin;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mScrollView = (ScrollView) findViewById(R.id.scrollView);
mScrollView.setSmoothScrollingEnabled(true);
mGrid = (GridLayout) findViewById(R.id.grid);
mGrid.setOnDragListener(new DragListener());
final LayoutInflater inflater = LayoutInflater.from(this);
GridLayout.LayoutParams lp;
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
float dpiToPx = displayMetrics.density;
View view = inflater.inflate(R.layout.item, mGrid, false);
lp = (GridLayout.LayoutParams) view.getLayoutParams();
mBaseWidth = lp.width;
mBaseHeight = lp.height;
mBaseMargin = lp.rightMargin;
for (int i = 0; i < ITEMS; i++) {
final View itemView = inflater.inflate(R.layout.item, mGrid, false);
final TextView text = (TextView) itemView.findViewById(R.id.text);
text.setText(String.valueOf(i + 1));
itemView.setOnLongClickListener(new LongPressListener());
lp = (i == 0) ? make2x2LayoutParams(itemView) : make1x1LayoutParams(itemView);
mGrid.addView(itemView, lp);
}
}
private GridLayout.LayoutParams make2x2LayoutParams(View view) {
GridLayout.LayoutParams lp = (GridLayout.LayoutParams) view.getLayoutParams();
lp.width = mBaseWidth * 2 + 2 * mBaseMargin;
lp.height = mBaseHeight * 2 + 2 * mBaseMargin;
lp.rowSpec = m2xSpec;
lp.columnSpec = m2xSpec;
lp.setMargins(mBaseMargin, mBaseMargin, mBaseMargin, mBaseMargin);
return lp;
}
private GridLayout.LayoutParams make1x1LayoutParams(View view) {
GridLayout.LayoutParams lp = (GridLayout.LayoutParams) view.getLayoutParams();
lp.width = mBaseWidth;
lp.height = mBaseHeight;
lp.setMargins(mBaseMargin, mBaseMargin, mBaseMargin, mBaseMargin);
lp.rowSpec = m1xSpec;
lp.columnSpec = m1xSpec;
return lp;
}
private int mDraggedIndex;
class DragListener implements View.OnDragListener {
#Override
public boolean onDrag(View v, DragEvent event) {
final View view = (View) event.getLocalState();
int index = calculateNextIndex(event.getX(), event.getY());
View child;
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
mDraggedIndex = index;
break;
case DragEvent.ACTION_DRAG_LOCATION:
if (view == v) return true;
// get the new list index
final Rect rect = new Rect();
mScrollView.getHitRect(rect);
final int scrollY = mScrollView.getScrollY();
if (event.getY() - scrollY > mScrollView.getBottom() - 250) {
startScrolling(scrollY, mGrid.getHeight());
} else if (event.getY() - scrollY < mScrollView.getTop() + 250) {
startScrolling(scrollY, 0);
} else {
stopScrolling();
}
child = mGrid.getChildAt(0);
if (index == 0) {
child.setLayoutParams(make1x1LayoutParams(child));
view.setLayoutParams(make2x2LayoutParams(view));
} else if (mDraggedIndex == 0) {
view.setLayoutParams(make1x1LayoutParams(view));
child.setLayoutParams(make2x2LayoutParams(child));
} else {
child.setLayoutParams(make2x2LayoutParams(child));
view.setLayoutParams(make1x1LayoutParams(view));
}
mGrid.removeView(view);
mGrid.addView(view, index);
break;
case DragEvent.ACTION_DROP:
for (int i = 0; i < mGrid.getChildCount(); i++) {
child = mGrid.getChildAt(i);
child.setLayoutParams(make1x1LayoutParams(child));
}
mGrid.removeView(view);
if (index == 0) {
view.setLayoutParams(make2x2LayoutParams(view));
}
mGrid.addView(view, index);
view.setVisibility(View.VISIBLE);
mGrid.getChildAt(0).setLayoutParams(make2x2LayoutParams(mGrid.getChildAt(0)));
break;
case DragEvent.ACTION_DRAG_ENDED:
if (!event.getResult()) {
view.setVisibility(View.VISIBLE);
}
break;
}
return true;
}
}
private void startScrolling(int from, int to) {
if (from != to && mAnimator == null) {
isScroll = true;
mAnimator = new ValueAnimator();
mAnimator.setInterpolator(new OvershootInterpolator());
mAnimator.setDuration(Math.abs(to - from));
mAnimator.setIntValues(from, to);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mScrollView.smoothScrollTo(0, (int) valueAnimator.getAnimatedValue());
}
});
mAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
isScroll = false;
mAnimator = null;
}
});
mAnimator.start();
}
}
private void stopScrolling() {
if (mAnimator != null) {
mAnimator.cancel();
}
}
private int calculateNextIndexOld(float x, float y) {
// calculate which column to move to
final float cellWidth = mGrid.getWidth() / mGrid.getColumnCount();
final int column = (int) (x / cellWidth);
final float cellHeight = mGrid.getHeight() / mGrid.getRowCount();
final int row = (int) Math.floor(y / cellHeight);
int index = row * mGrid.getColumnCount() + column;
if (index >= mGrid.getChildCount()) {
index = mGrid.getChildCount() - 1;
}
Log.d("MainActivity", "<<<<index=" + index);
return index;
}
private int calculateNextIndex(float x, float y) {
// calculate which column to move to
int index;
for (index = 0; index < mGrid.getChildCount(); index++) {
View child = mGrid.getChildAt(index);
Rect rect = new Rect();
child.getHitRect(rect);
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
break;
}
}
if (index >= mGrid.getChildCount()) {
// Move into empty cell? Calculate based upon uniform cell sizes.
index = calculateNextIndexOld(x, y);
}
if (index >= mGrid.getChildCount()) {
// Can't determine where to put it? Add it to the end.
index = mGrid.getChildCount() - 1;
}
return index;
}
}
If you work with the demo a little, you will see that it is possible to move tiles such that a 1x1 tile gap is opened up. This may be OK, but the code may need to be reworked a little if not.
You can try :
https://github.com/askerov/DynamicGrid
I hope it can help your problem!
I have a custom view inside a NestedScrollView. Every time invalidate is called on the view, the layout scrolls to top of the custom view. I want the scroll position to be unchanged after the view has been redrawn. Any idea how this can be done?
I have tried implementing an onScrollChangeListener and onGlobalLayoutListener in combination to rescroll to the previously scrolled position, but it doesn't work. I have the following code in the onCreateView method of a fragment.
ViewTreeObserver observer = layoutView.getViewTreeObserver();
if (scrollListener != null) {
observer.removeOnScrollChangedListener(scrollListener);
} else {
scrollListener = new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
scrollX = layoutView.getScrollX();
scrollY = layoutView.getScrollY();
}
};
}
observer.addOnScrollChangedListener(scrollListener);
if (layoutListener != null) {
observer.removeOnGlobalLayoutListener(layoutListener);
} else {
layoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (customView != null && scrollY != 0 && getUserVisibleHint()) {
layoutView.setVerticalScrollbarPosition(scrollY);
}
};
}
observer.addOnGlobalLayoutListener(layoutListener);
The following is the code for the onDraw and onMeasure method of the custom view.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (pieces == null) {
return;
}
int position = 0;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols && position < pieces.length; c++) {
Paint paint = (pieces[position] ? complete : empty);
int left = c * stepSize + borderSize + margin;
int right = left + stepSize - borderSize * 2;
int top = r * stepSize + borderSize;
int bottom = top + stepSize - borderSize * 2;
canvas.drawRect(left + borderSize, top + borderSize,
right + borderSize, bottom + borderSize, paint);
++position;
}
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
cols = width / stepSize;
rows = (int) Math.ceil((float) cells / (float) cols);
margin = (width - cols * stepSize) / 2;
int height = rows * stepSize;
setMeasuredDimension(width, Math.max(width, height));
}
I want to remove image which was recently added if anyone click minus button.
I have added image one by one on plus button click.
can see in snapshot
On plus button click images are going to add one by one.
want to remove on click of minus button recently added image.
image.setOnClickListener(new View.OnClickListener() {
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public void onClick(View v) {
ImageView image = new ImageView(Water.this);
image.setBackgroundResource(R.drawable.glass);
predicate.addView(image);
}
});
ImageView minus=(ImageView)findViewById(R.id.minus);
minus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ImageView image = new ImageView(Water.this);
image.setImageBitmap(null);
predicate.removeView(image);
//image.setBackgroundResource(R.drawable.glass);
//((ViewGroup) image.getParent()).removeView(image);
//predicate.removeView(image);
}
});
xml
<TextView
android:id="#+id/waterdescription"
android:text="Water Intake"
android:textSize="16dp"
android:layout_weight="1"
android:layout_marginLeft="20dp"
android:layout_width="wrap_content"
android:textColor="#283D65"
android:textStyle="bold"
android:layout_height="wrap_content"
/>
<ImageView
android:id="#+id/minus"
android:layout_weight="1"
android:layout_toRightOf="#+id/waterdescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/minus"
/>
<ImageView
android:id="#+id/image"
android:layout_weight="1"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/plus"
/>
predicate Layout
public class PredicateLayout extends ViewGroup {
private int line_height;
public PredicateLayout(Context context) {
super(context);
}
public PredicateLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
assert (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED);
final int width = MeasureSpec.getSize(widthMeasureSpec);
// The next line is WRONG!!! Doesn't take into account requested MeasureSpec mode!
int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
final int count = getChildCount();
int line_height = 0;
int xpos = getPaddingLeft();
int ypos = getPaddingTop();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = child.getLayoutParams();
child.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED));
final int childw = child.getMeasuredWidth();
line_height = Math.max(line_height, child.getMeasuredHeight() + lp.height);
if (xpos + childw > width) {
xpos = getPaddingLeft();
ypos += line_height;
}
xpos += childw + lp.width + 8;
}
}
this.line_height = line_height;
if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) {
height = ypos + line_height;
} else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
if (ypos + line_height < height) {
height = ypos + line_height;
}
}
setMeasuredDimension(width, height + 20);
}
#Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(2, 2); // default of 1px spacing
}
#Override
protected boolean checkLayoutParams(LayoutParams p) {
return (p instanceof LayoutParams);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
final int width = r - l;
int xpos = getPaddingLeft();
int ypos = getPaddingTop();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final int childw = child.getMeasuredWidth();
final int childh = child.getMeasuredHeight();
final LayoutParams lp = child.getLayoutParams();
if (xpos + childw > width) {
xpos = getPaddingLeft();
ypos += line_height;
}
child.layout(xpos, ypos, xpos + childw, ypos + childh);
xpos += childw + lp.width + 8;
}
}
}
}
You're not referencing the imageView you added in plus button's onClick() method.
minus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ImageView image = new ImageView(Water.this);
image.setImageBitmap(null);
predicate.removeView(image);
//image.setBackgroundResource(R.drawable.glass);
//((ViewGroup) image.getParent()).removeView(image);
//predicate.removeView(image);
}
});
ImageView image = new ImageView(Water.this); in this line, you're creating a new ImageView with water and trying to remove it from parent layout. But you didn't even add this.
What you need to do is to keep a reference to the the views you are adding in plus button's onClick() method.
You can do something like:
public class PredicateLayout extends ViewGroup {
private LinkedList<ImageView> imageViews;
//other parts are omitted...
public PredicateLayout(Context context, AttributeSet attrs) {
super(context, attrs);
imageViews = new LinkedList();
}
//...some other code...
public LinkedList<ImageView> getImageViews(){
return imageViews;
}
}
and when adding:
Plus Button:
...onClick() {
//..
predicate.addView(image);
predicate.getImageViews().add(image);
}
Minus Button:
...onClick(){
//pollLast returns last element in the list
ImageView lastAddedImageView = predicate.getImageViews().pollLast()
predicate.removeView(lastAddedImageView);
}
Add view like:
ImageView image = new ImageView(Water.this);
image.setId(Integer.parseInt("1234"));
image.setBackgroundResource(R.drawable.glass);
predicate.addView(image);
And remove it:
View rView=(ImageView)view.findViewById(Integer.parseInt("1234"));
predicate.removeView(rView);
To understand this question, first read how this method works.
I am trying to implements a drag and drop ListView, it's going alright but have run into
a road block. So I don't have to handled everything, I am intercepting(but returning false) MotionEvents sent to the ListView, letting it handle scrolling and stuff. When I want to start dragging a item, I then return true and handled all the dragging stuff. Everything is working fine except for one thing. The drag(drag and drop) is started when it is determined that a long press as a occurred(in onInterceptTouchEvent). I get the Bitmap for the image that I drag around like so. itemPositition being the index of the item that was selected.
(omitting irrelevant parts)
...
View dragItem = mListView.getChildAt(itemPosition);
dragItem.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(dragItem.getDrawingCache());
mDragImage = new ImageView(mContext);
mDragImage.setImageBitmap(bitmap);
...
The problem is, mDragImage is a solid black like this.
But, if I don't let ListView handle anything. As in, I start the drag on ACTION_DOWN and stop on ACTION_UP, mDragImage looks has expected(but I obviously lose scrolling abilities).
Since the drag is started with a long press, the ListView is given the opportunity to do things before the long press occurs. This is my guess as to why this is happening. When a item is pressed, it is highlighted by the ListView. Somewhere in doing so, it is messing with the bitmap. So when I go to get it, it's in a weird state(all black).
I see two options for fixing this, neither of which I know how to do.
Create a image from scratch.
Handle the highlighting myself(if that is the problem).
Option two seems a better one to me, except that I looked at the documentation and the source code and could not find out how to do so. Here are some things I have done/tried.
I set setOnItemClickListener(...) and
setOnItemSelectedListener(...) with a empty method(highlighting
still happens). (Before anyone suggests it, calling
setOnClickListener results in a runtime error.)
I also looked into trying to get the ListView to make a new item
(for option 2), but could not find a way.
Spent 45ish minutes looking through the source code and
documentation trying to pinpoint where the highlighting was
happening(I never found it).
Any help fixing this would be appreciated.
(EDIT1 START)
So I don't actually know if onLongClickListener is working, I made an error before thinking it was. I am trying to set it up right now, will update when I find out if it does.
(EDIT1 END)
Last minute edit before post. I tried using onLongClickListener just now, and the image is good. I would still like to know if there is another way. How I have to use onLongClickListener to get things working is ugly, but it works. I also spent so much time trying to figure this out, it would be nice to find out the answer. I still want to be able to change/handle the highlight color, the default orangeish color is not pretty. Oh and sorry about the length of the post. I could not think of way of making it shorter, while supplying all the information I thought was needed.
use this code, it's allows operation drug and drop in ListView:
public class DraggableListView extends ListView {
private static final String LOG_TAG = "tasks365";
private static final int END_OF_LIST_POSITION = -2;
private DropListener mDropListener;
private int draggingItemHoverPosition;
private int dragStartPosition; // where was the dragged item originally
private int mUpperBound; // scroll the view when dragging point is moving out of this bound
private int mLowerBound; // scroll the view when dragging point is moving out of this bound
private int touchSlop;
private Dragging dragging;
private GestureDetector longPressDetector;
public DraggableListView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.listViewStyle);
}
public DraggableListView(final Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
longPressDetector = new GestureDetector(getContext(), new SimpleOnGestureListener() {
#Override
public void onLongPress(final MotionEvent e) {
int x = (int) e.getX();
final int y = (int) e.getY();
int itemnum = pointToPosition(x, y);
if (itemnum == AdapterView.INVALID_POSITION) {
return;
}
if (dragging != null) {
dragging.stop();
dragging = null;
}
final View item = getChildAt(itemnum - getFirstVisiblePosition());
item.setPressed(false);
dragging = new Dragging(getContext());
dragging.start(y, ((int) e.getRawY()) - y, item);
draggingItemHoverPosition = itemnum;
dragStartPosition = draggingItemHoverPosition;
int height = getHeight();
mUpperBound = Math.min(y - touchSlop, height / 3);
mLowerBound = Math.max(y + touchSlop, height * 2 / 3);
}
});
setOnItemLongClickListener(new OnItemLongClickListener() {
#SuppressWarnings("unused")
public boolean onItemLongClick(AdapterView<?> paramAdapterView, View paramView, int paramInt, long paramLong) {
// Return true to let AbsListView reset touch mode
// Without this handler, the pressed item will keep highlight.
return true;
}
});
}
/* pointToPosition() doesn't consider invisible views, but we need to, so implement a slightly different version. */
private int myPointToPosition(int x, int y) {
if (y < 0) {
return getFirstVisiblePosition();
}
Rect frame = new Rect();
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
child.getHitRect(frame);
if (frame.contains(x, y)) {
return getFirstVisiblePosition() + i;
}
}
if ((x >= frame.left) && (x < frame.right) && (y >= frame.bottom)) {
return END_OF_LIST_POSITION;
}
return INVALID_POSITION;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
if (longPressDetector.onTouchEvent(ev)) {
return true;
}
if ((dragging == null) || (mDropListener == null)) {
// it is not dragging, or there is no drop listener
return super.onTouchEvent(ev);
}
int action = ev.getAction();
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
dragging.stop();
dragging = null;
if (mDropListener != null) {
if (draggingItemHoverPosition == END_OF_LIST_POSITION) {
mDropListener.drop(dragStartPosition, getCount() - 1);
} else if (draggingItemHoverPosition != INVALID_POSITION) {
mDropListener.drop(dragStartPosition, draggingItemHoverPosition);
}
}
resetViews();
break;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
int x = (int) ev.getX();
int y = (int) ev.getY();
dragging.drag(x, y);
int position = dragging.calculateHoverPosition();
if (position != INVALID_POSITION) {
if ((action == MotionEvent.ACTION_DOWN) || (position != draggingItemHoverPosition)) {
draggingItemHoverPosition = position;
doExpansion();
}
scrollList(y);
}
break;
}
return true;
}
private void doExpansion() {
int expanItemViewIndex = draggingItemHoverPosition - getFirstVisiblePosition();
if (draggingItemHoverPosition >= dragStartPosition) {
expanItemViewIndex++;
}
// Log.v(LOG_TAG, "Dragging item hovers over position " + draggingItemHoverPosition + ", expand item at index "
// + expanItemViewIndex);
View draggingItemOriginalView = getChildAt(dragStartPosition - getFirstVisiblePosition());
for (int i = 0;; i++) {
View itemView = getChildAt(i);
if (itemView == null) {
break;
}
ViewGroup.LayoutParams params = itemView.getLayoutParams();
int height = LayoutParams.WRAP_CONTENT;
if (itemView.equals(draggingItemOriginalView)) {
height = 1;
} else if (i == expanItemViewIndex) {
height = itemView.getHeight() + dragging.getDraggingItemHeight();
}
params.height = height;
itemView.setLayoutParams(params);
}
}
/**
* Reset view to original height.
*/
private void resetViews() {
for (int i = 0;; i++) {
View v = getChildAt(i);
if (v == null) {
layoutChildren(); // force children to be recreated where needed
v = getChildAt(i);
if (v == null) {
break;
}
}
ViewGroup.LayoutParams params = v.getLayoutParams();
params.height = LayoutParams.WRAP_CONTENT;
v.setLayoutParams(params);
}
}
private void resetScrollBounds(int y) {
int height = getHeight();
if (y >= height / 3) {
mUpperBound = height / 3;
}
if (y <= height * 2 / 3) {
mLowerBound = height * 2 / 3;
}
}
private void scrollList(int y) {
resetScrollBounds(y);
int height = getHeight();
int speed = 0;
if (y > mLowerBound) {
// scroll the list up a bit
speed = y > (height + mLowerBound) / 2 ? 16 : 4;
} else if (y < mUpperBound) {
// scroll the list down a bit
speed = y < mUpperBound / 2 ? -16 : -4;
}
if (speed != 0) {
int ref = pointToPosition(0, height / 2);
if (ref == AdapterView.INVALID_POSITION) {
//we hit a divider or an invisible view, check somewhere else
ref = pointToPosition(0, height / 2 + getDividerHeight() + 64);
}
View v = getChildAt(ref - getFirstVisiblePosition());
if (v != null) {
int pos = v.getTop();
setSelectionFromTop(ref, pos - speed);
}
}
}
public void setDropListener(DropListener l) {
mDropListener = l;
}
public interface DropListener {
void drop(int from, int to);
}
class Dragging {
private Context context;
private WindowManager windowManager;
private WindowManager.LayoutParams mWindowParams;
private ImageView mDragView;
private Bitmap mDragBitmap;
private int coordOffset;
private int mDragPoint; // at what offset inside the item did the user grab it
private int draggingItemHeight;
private int x;
private int y;
private int lastY;
public Dragging(Context context) {
this.context = context;
windowManager = (WindowManager) context.getSystemService("window");
}
/**
* #param y
* #param offset - the difference in y axis between screen coordinates and coordinates in this view
* #param view - which view is dragged
*/
public void start(int y, int offset, View view) {
this.y = y;
lastY = y;
this.coordOffset = offset;
mDragPoint = y - view.getTop();
draggingItemHeight = view.getHeight();
mDragView = new ImageView(context);
mDragView.setBackgroundResource(android.R.drawable.alert_light_frame);
// Create a copy of the drawing cache so that it does not get recycled
// by the framework when the list tries to clean up memory
view.setDrawingCacheEnabled(true);
mDragBitmap = Bitmap.createBitmap(view.getDrawingCache());
mDragView.setImageBitmap(mDragBitmap);
mWindowParams = new WindowManager.LayoutParams();
mWindowParams.gravity = Gravity.TOP;
mWindowParams.x = 0;
mWindowParams.y = y - mDragPoint + coordOffset;
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.windowAnimations = 0;
windowManager.addView(mDragView, mWindowParams);
}
public void drag(int x, int y) {
lastY = this.y;
this.x = x;
this.y = y;
mWindowParams.y = y - mDragPoint + coordOffset;
windowManager.updateViewLayout(mDragView, mWindowParams);
}
public void stop() {
if (mDragView != null) {
windowManager.removeView(mDragView);
mDragView.setImageDrawable(null);
mDragView = null;
}
if (mDragBitmap != null) {
mDragBitmap.recycle();
mDragBitmap = null;
}
}
public int getDraggingItemHeight() {
return draggingItemHeight;
}
public int calculateHoverPosition() {
int adjustedY = (int) (y - mDragPoint + (Math.signum(y - lastY) + 2) * draggingItemHeight / 2);
// Log.v(LOG_TAG, "calculateHoverPosition(): lastY=" + lastY + ", y=" + y + ", adjustedY=" + adjustedY);
int pos = myPointToPosition(0, adjustedY);
if (pos >= 0) {
if (pos >= dragStartPosition) {
pos -= 1;
}
}
return pos;
}
}
}