Drag and drop scrolling in a linear layout - java

I'm currently working on an app that allows users to add a relative layout, linear layout, or nested linear layout within a linear layout, which is a child of a scroll view. The scrolling seems to work fine when just the relative layout or linear layout is added, however, once the nested linear layout is added, the scrolling becomes a problem. Basically, I want the scroll view to start scrolling once my dragged view comes within x amount of the top/bottom of the screen. Any advice? Below is some of my code to help
protected class myDragEventListener implements View.OnDragListener {
// This is the method that the system calls when it dispatches a drag event to the listener.
public boolean onDrag(View v, DragEvent event) {
// Defines a variable to store the action type for the incoming event
final int action = event.getAction();
Point touchPosition = getTouchPositionFromDragEvent(v, event);
Log.d("y", String.format("%s", String.format("%s", eventTouchYCoord)));
//View dragView = (View) event.getLocalState();
// Handles each of the expected events
switch(action) {
case DragEvent.ACTION_DRAG_STARTED:
return true;
case DragEvent.ACTION_DRAG_ENTERED:
return true;
case DragEvent.ACTION_DRAG_LOCATION:
Log.d("point", touchPosition.toString());
if (touchPosition.y > (absoluteBottom + mScrollDistance - 350)){
Log.d("bottom", "greater");
Log.d("actionbar", String.format("%s", actionBarHeight()));
homeScroll.scrollBy(0, 30);
}
if (touchPosition.y < (actionBarHeight + 350)){
Log.d("top", "greater");
homeScroll.scrollBy(0, -30);
}
break;
getTouchPositionFromDragEvent Method
public static Point getTouchPositionFromDragEvent(View item, DragEvent event) {
Rect rItem = new Rect();
item.getGlobalVisibleRect(rItem);
return new Point(rItem.left + Math.round(event.getX()), rItem.top + Math.round(event.getY()));
}
onScrollChangedListener Method
homeScroll.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
mScrollDistance = homeScroll.getScrollY();
int[] coords = new int[2];
homeScroll.getLocationOnScreen(coords);
absoluteTop = coords[1];
absoluteBottom = coords[1] + homeScroll.getHeight();
}
});

Its always best practice to implement scrolling attribute in xml file. This that you want scroll in side it. or in Palette you will find scrollview option of vertical and horizontal scrolling.. just drag and drop to where you want place it.
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ScrollView>

Related

How to drag a RecyclerView (or ALL of its items) horizontally?

I have a RecyclerView in Android Studio with items within it, and instead of swiping horizontally to a new activity, I'd like to swipe left/right, have the entire RecyclerView (or all of its items, either way, since the overall effect would appear similar) move with my finger. Once the items (or RecyclerView) leave the screen at a threshold (for example, 50%), then I'd like to initiate an animation of new items sliding in.
So far, I have the animation for the items sliding in but cannot:
Get all items or the entire RecyclerView to be dragged by by finger
Once that's accomplished, calculate the percentage of the items/view is offscreen to invoke a function.
Thanks in advance
EDIT:
In my main activity, I already have an onTouchListener to register swiping and each of my items are clickable, but I want to be able to "animate" the items moving left offscreen when I swipe left
An example of my code:
public class MainActivity extends AppCompatActivity {
// Set up layout and such...
...
Recyclerview recyclerview = findViewById(R.id.mRecyclerview);
// Set layout manager and adapter...
...
recyclerview.setOnTouchListener(new SwipeListener(MainActivity.this) {
#Override
public void onSwipeRight() {
super.OnSwipeRight();
swipeRightAnimation();
}
#Override
public void onSwipeLeft() {...}
...
});
/* How do I animate items moving left when swiping right and vice versa?
Inside of SwipeListener? By implementing a new listener? */
}
EDIT2:
I tried this (acting on the parent layout instead of the RecyclerView), but the overall view seems to grow/shrink symmetrically rather than move left or right, any suggestions?
public class HorizontalDragListener implements View.OnTouchListener {
private Context context;
private int x;
private int dXLeft;
private int dXRight;
private DrawerLayout.LayoutParams layoutParams;
public HorizontalDragListener(Context context) {
this.context = context;
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
// Get position information
x = (int) motionEvent.getRawX();
y = (int) motionEvent.getRawY();
// Detect movements
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case (MotionEvent.ACTION_DOWN): {
layoutParams = (DrawerLayout.LayoutParams) view.getLayoutParams();
dXLeft = x - layoutParams.leftMargin;
dXRight = layoutParams.rightMargin - x;
break;
}
case (MotionEvent.ACTION_MOVE): {
// move object
layoutParams = (DrawerLayout.LayoutParams) view.getLayoutParams();
layoutParams.leftMargin = x - dXLeft;
layoutParams.rightMargin = x + dXRight;
view.setLayoutParams(layoutParams);
break;
}
}
// Update view
view.invalidate();
return true;
}
}

OnTouch imageview creation being placed too low

I have a custom floorplanView class within my main Activity. This custom view uses up roughly half the screen space. I am trying to simply create a star image and place it on the floor plan onTouch. However, for some reason this appears to place the image below where I am touching (I have tried a few variations and cannot work it out).
public class FloorplanView extends RelativeLayout {
public FloorplanView(Context context, AttributeSet attrs) {
super(context, attrs);
SCROLL_THRESHOLD = getResources().getDimensionPixelSize(R.dimen.margin5);
setClipChildren(false);
setOnTouchListener(new FloorplanTouchListener());
}
public class FloorplanTouchListener implements View.OnTouchListener {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
posX = event.getRawX() - v.getX();
posY = event.getRawY() - v.getY();
break;
case MotionEvent.ACTION_UP:
ImageView img = new ImageView(getContext());
img.setOnTouchListener(new FlagTouchListener());
img.setBackground(getResources().getDrawable(android.R.drawable.btn_star));
img.setX(event.getRawX()); //Used posX/Y without success
img.setY(event.getRawY());
addView(img);
break;
}
return true;
}
}
}
Further to this, is there a way to set bounds to my custom class with extends a relativeLayout so that when I drag and move my image (which is setup and working) it does not go outside of the Floorplanview custom class.
EDIT:
The parent Activity class XML
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.fcs_systems.inspector.FloorplanActivity">
<TextView
android:id="#+id/txtFloorplanName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Floorplan Name"
android:visibility="gone"/>
<com.fcs_systems.inspector.FloorplanView
android:id="#+id/floorplanView"
android:background="#color/fcs_red"
android:layout_width="match_parent"
android:layout_height="200dp"/>
</RelativeLayout>
According to the documentation,
float getRawX () : Returns the original raw X coordinate of this event. For touch events on the screen, this is the original location of the event on the screen, before it had been adjusted for the containing window and views.
(Source : https://developer.android.com/reference/android/view/MotionEvent.html#getRawX() )
This is why getRawX() and getRawY() seem to place the image lower than where you tapped.
You should simply use the getX() and getY() methods on the MotionEvent instead.
As for your images going out of the RelativeLayout, this will depends on two things :
Add your images to your RelativeLayout (not sure what your actual addView(img); does)
Make sure your RelativeLayout doesn't have https://developer.android.com/reference/android/view/ViewGroup.html#setClipChildren(boolean) enabled
This will make sure that your images belong to your RelativeLayout container, and will not be drawn when moved outside of it.

Direction of scroll RecyclerView

Can I detect the direction of scroll when I add the on scroll listener to my recycler view?
The code is as follows:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
switch (recyclerView.getScrollState()) {
case RecyclerView.SCROLL_STATE_DRAGGING :
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) relativeBaseLayout.getLayoutParams();
if(layoutParams.weight < 1.0f) {
SlideAnimation slideAnimation = new SlideAnimation(layoutParams.weight, 1.0f, relativeBaseLayout);
slideAnimation.setDuration(250);
relativeBaseLayout.startAnimation(slideAnimation);
}
break;
default:
break;
}
}
});
I want this function to work only if I scroll down.
Currently it'll work on both scroll up and down. What changes do I make?
I do not want to implement the onScrolled() method since that will be called after the scroll has finished. I want to detect it during the scroll.
What am I missing here?

onFling and LinearLayouts

My Linear Layout: main_linear_layout contains four Linear Layouts: ll1 ll2 ll3 ll4. Each LinearLayout contains Buttons. I am trying to implement the onFling method on main_linear_layout. I want to be able to swipe anywhere on the Layout of Buttons and call an action.
Currently I am able to swipe anywhere on the screen except for the Layout of Buttons. I tried using the following code to fix the problem:
swipeLayout = (LinearLayout) findViewById(R.id.main_linear_layout);
//swipeLayout is the main Layout that contains 4 other linear layouts
swipeLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
gestureScanner.onTouchEvent(event);
return true;
}
});
But I am still not able to swipe on the Layout of Buttons. Does anybody know why this is? Is there something else I need to implement? Am I supposed to implement an OnTouchListener to every Linear Layout inside the main Linear Layout? Or to every Button in each Layout?
And in the xml I added a bunch of code to the main Linear Layout:
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:longClickable="true"
But this did not work either. Can someone please help?
If you have a LinearLayout, and then more layouts inside of that, and then buttons on those as you say you do, then this is functioning as intended.
You're only adding the listener to the outside layout...so of course, it'll only trigger when you slide on it. By sliding on the other layouts, or even the buttons, the slide event doesn't even get to that listener, because it is consumed.
You'd need to add the listener to each element that you want to be checking for.
One way to do this is to create an array of your buttons and do it all at once:
private Button button1;
private Button button2;
....
private Button button10;
...
protected void onCreate(Bundle b) {
super.onCreate(b);
button1 = findViewById(R.id.button1);
...
button10 = findViewById(R.id.button10);
OnClickListener onCL = new OnClickListener . . . //do all of your creation here
Button[] buttons = {button1, button2, . . . , button10};
for(Button b : buttons) {
b.setOnClickListener(onCL);
}
}
Well the reason the ll1,ll2,ll3,ll4 don't pick up on touch events is because the parent linear layout receives the motion event and it doesn't propagate further.
Why do you need a touch event listener on the main linear layout? Do you want the sub layouts to all fling together?
If you want the other layouts to pick up on the touch event return false on the touch listener
swipeLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
gestureScanner.onTouchEvent(event);
return false;
}
});
Or you could create 4 GestureDetectors and pass the event to the right one depending on which view was pressed
swipeLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(v.getId()){
case R.id.ll1:
gesture1.ontouchEvent(e);
break;
case R.id.ll2:
gesture1.ontouchEvent(e);
break;
}
//etc
return false;
}
});

Android OnGestureListener issues when view contains a listview

Im having some issues with the Android onGestureListener, I have 3 Linear Layouts sitting next to each other horizontally in a Linear Layout, the Linear Layout position is set to the middle Layout onCreate, the middle layout also contains a listview inside of it, what I would like to happen is when I swipe left or right the layout moves, this seems to not pick up the gestures when I attempt to swipe left or right on the Linear Layout with the listview inside of it, but if I swipe right or left on the other views that dont have anything in them it picks up the gesture and the view animates accordingly, has anyone had this issue before or have an idea how to fix it? any help will go a long way thanks
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
return gestureDetector.onTouchEvent(event);
}
SimpleOnGestureListener simpleOnGestureListener = new SimpleOnGestureListener() {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
float sensitvity = 50;
if ((e1.getX() - e2.getX()) > sensitvity) {
slideLeft();
} else if ((e2.getX() - e1.getX()) > sensitvity) {
slideRight();
}
return true;
}
};
private void slideRight() {
if (swipeDirection > -1) {
if (swipeDirection == 0) {
layoutContainer.animate().translationX(theDistance - 0)
.setDuration(500);
} else {
//go to home
layoutContainer.animate().translationX(0).setDuration(500);
}
swipeDirection--;
}
}
private void slideLeft() {
if (swipeDirection < 1) {
if (swipeDirection == 0) {
layoutContainer.animate().translationX(0 - theDistance)
.setDuration(500);
} else {
layoutContainer.animate().translationX(0).setDuration(500);
}
swipeDirection++;
}
}
The ListView itself has a gesture listener built in already (for scrolling the list), and possibly it also has an onItemClickListener on the individual items in the list. It can be that this interferes with your swipe behavior of the whole layout.
The solution explained here by Pinhassi has worked best for me so far:
Android Swipe on List
probably you will need to expand on the onItemClickListener of your ListView and include the swipe detector mentioned above. Also, putting #Override in front of the line where you declare the onItemClick might be needed to override the listview listeners. That way, you will maintain clickable list items and you can perform swipes on them.

Categories