Scrolling vertically and horizontally in Android - android

I have a RelativeLayout (params = MatchParent,MatchParent) as my content view.
The RelativeLayout contains a ScrollView (params=Wrap_Content, Wrap_Content).
The ScrollView then contains a LinearLayout ( no params, set vertical orientation).
To the LinearLayout I'm adding "myrows".
Each myrow is made up of another LinearLayout (no params, horizontal orientation) which contains a SpannableString which contains equally spaced/padded numbers.
The result is a vertically scrollable table, and looks ok. The problem is that if I run it on a smaller device, the SpannableStrings extend out of the screen.
On the smaller screen I want the text to be large enough to read, and so I need to be able to scroll horizontally as well to see the part of the SpannableString which has extended beyond screen. I have tried a couple of solutions already. One was to put the vertical ScrollView inside a horizontal ScrollView and capture the xy etc...but people mentioned this is dangerous. I don't want the strings to wrap. I can't use a dropbox etc...to show the beyondcreen values. It has to be a flat table with text large enough to read, I can't eliminate any of the string length.
I can make it instead a horizontal ScrollView and put a page up/down button but it won't work as smooth as it does now. Could I put a ListView inside a horizontal ScrollView and be able to scroll in both directions?
Any other ideas for this? Is there a scroll bar I can put at the bottom that scrolls the screen horizontally where it only captures your finger touch on the bar itself?
Thanks!

I finally found a solution which works great. Create a custom gridView with the following overrides. I also created a scrolling header which follows the scroll of the gridView.
#Override
public boolean onInterceptTouchEvent (MotionEvent ev) {
getMaxLeftScroll();
myLastX = ev.getX();
myLastY = ev.getY();
return super.onInterceptTouchEvent(ev);
}
#Override
public boolean onTouchEvent(final MotionEvent ev){
final int action = ev.getAction();
final float x;
final float y;
switch (action) {
case MotionEvent.ACTION_MOVE:
// This handles Scrolling only.
x = ev.getX();
y = ev.getY();
final int deltaX = (int) (myLastX - x);
final int deltaY = (int) (myLastY - y);
if(Math.abs(deltaX) > Math.abs(deltaY)) {
if( (iCurrentLeftScroll+deltaX) > 0 &&
iCurrentLeftScroll+deltaX < iMaxLeftScroll) {
scrollBy(deltaX, 0);
//Listener and call the method here to scroll the header
mScrollChangeListener.onGridViewScrolled(deltaX);
iCurrentLeftScroll+=deltaX;
} //Scrolled by some value
// Close to right edge within 10? Smooth Scroll remaining small distance if not already there
else if(deltaX > 0 && (iCurrentLeftScroll<iMaxLeftScroll)) {
if ((iMaxLeftScroll - iCurrentLeftScroll) < 10) {
scrollBy(iMaxLeftScroll-iCurrentLeftScroll, 0);
mScrollChangeListener.onGridViewScrolled(iMaxLeftScroll-iCurrentLeftScroll);
iCurrentLeftScroll = iMaxLeftScroll;
}
}
// Close to left edge within 30? Smooth Scroll remaining small distance if not already there
else if(deltaX < 0 && (iCurrentLeftScroll>0)) {
if ((iCurrentLeftScroll) < 10) {
scrollBy(iCurrentLeftScroll*-1, 0);
mScrollChangeListener.onGridViewScrolled(iCurrentLeftScroll*-1);
iCurrentLeftScroll = 0;
}
}
//left and right are subjective
if(iCurrentLeftScroll == iMaxLeftScroll)
iScrollStatus = SCROLLED_FULL_LEFT;
if(iCurrentLeftScroll == 0)
iScrollStatus = SCROLLED_FULL_RIGHT;
if( (iCurrentLeftScroll > 0) && (iCurrentLeftScroll < iMaxLeftScroll) )
iScrollStatus = 0;
Log.d(TAG," Scroll Status " + String.valueOf(iScrollStatus) );
Log.d(TAG," MaxLeftScroll " + String.valueOf(iMaxLeftScroll) );
Log.d(TAG," Current Left Scroll " + String.valueOf(iCurrentLeftScroll) );
}
myLastX = x;
myLastY = y;
break;
}
return super.onTouchEvent(ev);
}

Related

How to swipe right (vertical) in particular box, and verify all results in Android with Appium

Scenario is swipe right to left at a box. and you will get some dynamic result. and verify them.
U can Identify locators for 2 elements -
1 - Element till where u want to reach
2 - A Common Xpath for All Horizontal Elements in that box and run following code to Scroll Horizontally.
public static void scrollTillElementHorizontally(By by, By allElements) {
if (driver.findElements(By) > 0 || driver.findElement(By).isDisplayed())
return;
int y = driver.findElement(allElements).getCenter().y;
int width = driver.manage().window().getSize().getWidth();
double startX = width * 0.80;
double endX = width * 0.20;
TouchAction action = new TouchAction(driver);
int i = 0;
while (i < 50) {
if (driver.findElements(By) > 0 || driver.findElement(By).isDisplayed())
break;
action.press(PointOption.point((int) startX, y)).waitAction(WaitOptions.waitOptions(Duration.ofSeconds(2)))
.moveTo(PointOption.point((int) endX, y)).release().perform();
i++;
}
}
And if u want to remove duplicate elements while scrolling add the elements to HashSet which would remove duplicates while scrolling.

How to clear child layout in android?

I am developing an android application where I am creating dynamic Images arrow on relative layout. The images are created on a x,y coordinated of the click area of relative layout. Below is the code the I am using for it.
presciptionScreenArrowImg.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (canSelectMedianStatus == 2) {
if (event == simulationEvent)
return false;
int action = event.getAction();
int x = (int) event.getX();
int y = (int) event.getY();
Log.e("onTouchListener", "User touch at X:" + x + " Y:" + y);
pointerArrow = new ImageView(getApplication());
pointerArrow.setImageResource(R.drawable.pointer);
pointerArrow.setId(imageArrayTag);
imageArrayTag++;
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(40, 40);
params.topMargin = y;
params.leftMargin = x;
pointerArrow.setLayoutParams(params);
presciptionScreenArrowImg.addView(pointerArrow);
long length = 0;
if (action == MotionEvent.ACTION_DOWN) {
// click(v, x, y);
}
}
return false;
}
});
Now, I need is there on button click the last Image drawn should remove first. Basically I need an undo functionality to remove Images as LIFO structure.
let consider presciptionScreenArrowImg is your main layout which contains your all views so for removing
int index=presciptionScreenArrowImg.getChildCount();
if(index>0)
presciptionScreenArrowImg.removeViewAt(index-1);
from above get count of child and remove last view if you have any problem let me know
Store the views in a Queue, and when you add a new view check if the queue is full. If it is, pop a view from the queue, and call presciptionScreenArrowImg.remove(poppedView);

Layout flicker when dragging view in its MotionEvent.ACTION_MOVE android

Here is a picture of what I'm trying to do:
So, I want to resize a single cell while dragging resize anchors (black quads) which are ImageViews. To do this I attached custom onTouchListener to them that does next in MotionEvent.ACTION_MOVE:
calculate drag offset
set cell height/width based on this offset
reposition anchor point by changing it's layout params
The outcome of this is that the cell resizes but there is some king of flicker, more like shaking left/right or up/down, by some small offset.
My guess is that the problem comes when it catches move event, then I manualy change position of anchor and then, when it catches move event again it doesn't handle that change well...or something
I have an idea to put invisible ImageViews under each anchor and do resize based on their movement but do not move that anchor while draging. Then when I relese it, it lines up with coresponding visible anchor. But this is more hacking than solution :)
And finally, does anubody know why is this happening?
EDIT:
Here is the code where I'm handlign move event:
float dragY = event.getRawY() - resizePreviousPositionY;
LinearLayout.LayoutParams componentParams = (LinearLayout.LayoutParams)selectedComponent.layout.getLayoutParams();
LinearLayout parrent = (LinearLayout)selectedComponent.layout.getParent();
View topSibling = parrent.getChildAt(parrent.indexOfChild(selectedComponent.layout) - 1);
LinearLayout.LayoutParams topSiblingParams = (LinearLayout.LayoutParams)topSibling.getLayoutParams();
if(dragY > 0 && selectedComponent.layout.getHeight() > 100 ||
dragY < 0 && topSibling.getHeight() > 100)
{
componentParams.height = selectedComponent.layout.getHeight() - (int)dragY;
topSiblingParams.height = topSibling.getHeight() + (int)dragY;
//bottomSiblingParams.height = bottomSibling.getHeight();
selectedComponent.layout.setLayoutParams(componentParams);
repositionResizeAnchors(selectedComponent);
}
resizePreviousPositionY = event.getRawY();
and here is where I reposition it:
if(((LinearLayout)viewHolder.layout.getParent()).getOrientation() == LinearLayout.VERTICAL)
{
leftMargin = ((LinearLayout)viewHolder.layout.getParent()).getLeft() +
viewHolder.layout.getLeft() + viewHolder.layout.getWidth()/2;
}
else
{
leftMargin = viewHolder.layout.getLeft() + viewHolder.layout.getWidth()/2;
}
topMargin = viewHolder.layout.getTop();
params = (RelativeLayout.LayoutParams)resizeTop.getLayoutParams();
params.leftMargin = leftMargin - dpToPx(resizeAnchorRadius/2);
params.topMargin = topMargin - dpToPx(resizeAnchorRadius/2);
resizeTop.setLayoutParams(params);

Android - Moving a single image

Can anyone help please.
Im writing a small Android game where the player is able to select a "barrier" and drag it accross the screen with their finger. I have the barriers drawn on the screen and I am able to drag it accross the screen.
My problem however is when I add more than 1 barrier, eg 3 barriers, and drag a barrier accross the screen, they all drag and they all drag to the same position. That is to say they all lie on top of each other, making it look as though there is only 1 barrier.
Here is my code, can anyone please tell me where I am going wrong/explain where I am going wrong.
public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback, SensorEventListener {
// Initialising the Barrier
private Barrier barrier[] = new Barrier[3];
// The Main Game Panel
public MainGamePanel(Context context) {
super(context);
// Adding a call-back (this) to the surfaceHolder to intercept events
getHolder().addCallback(this);
// Creating the Game items
// The starting coordinates of the Barrier
int x = 30;
int y = 270;
barrier[0] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.blue_barrier), x, y);
barrier[1] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.green_barrier), x + 15, y);
barrier[2] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.pink_barrier), x + 30, y);
// Create the Game Loop Thread
thread = new MainThread(getHolder(), this);
// Make the GamePanel focusable so it can handle events
setFocusable(true);
}
// Handles the touch events
public boolean onTouchEvent(MotionEvent event)
{
int eventAction = event.getAction();
int x = (int)event.getX();
int y = (int)event.getY();
switch (eventAction)
{
// Touch down so check if finger is on Barrier
case MotionEvent.ACTION_DOWN:
if (x > barrier[0].getX() && x < barrier[0].getX() + 8
&& y > barrier[0].getX() && y < barrier[0].getY() + 8)
{
barrier[0].isTouched();
}
else if (x > barrier[1].getX() && x < barrier[1].getX() + 8
&& y > barrier[1].getX() && y < barrier[1].getY() + 8)
{
barrier[1].isTouched();
}
else if (x > barrier[2].getX() && x < barrier[2].getX() + 8
&& y > barrier[2].getX() && y < barrier[2].getY() + 8)
{
barrier[2].isTouched();
}
break;
// Touch-drag with the Barrier
case MotionEvent.ACTION_MOVE:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
if (barrier[i] == barrier[0])
{
barrier[0].setX(x);
barrier[0].setY(y);
} // end if
else if (barrier[i] == barrier[1])
{
barrier[1].setX(x);
barrier[1].setY(y);
}
else if (barrier[i] == barrier[2])
{
barrier[2].setX(x);
barrier[2].setY(y);
} // end else if
} // end for
break;
case MotionEvent.ACTION_UP:
// Finger no longer on Barrier - Do Nothing
break;
}
return true;
}
// Render - Draws the Game Item Bitmaps to the screen
public void render(Canvas canvas)
{
// Set the background to white
canvas.drawColor(Color.WHITE);
barrier[0].draw(canvas);
barrier[1].draw(canvas);
barrier[2].draw(canvas);
}
// Update
// This is the Game's update method
// It iterates through all the Objects and calls their update() methods (if they have one)
public void update()
{
} // end update
In your barrier move code, you aren't checking a particular barrier is touched, so you're moving all of them to the same coordinates. The following loop does exactly the same thing as your current code:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
barrier[i].setX(x);
barrier[i].setY(y);
} //end for
To fix this, you need to check if the current barrier in the loop is the one that was touched, and can change that whole loop to something like:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
if (barrier[i].isTouched())
{
barrier[i].setX(x);
barrier[i].setY(y);
} // end if
} // end for
You would then need to make sure that you un-set the touched property in the ACTION_UP section. If you post your Barrier class definition I can probably help more.
I'm pretty sure your problem could be solved by making the barriers three separate objects instead of in an array.
As already pointed out, your code in handling the move is wrong because it moves all the Barriers to the same X,Y when it should only move the one that is touched.
Also, you are never resetting the isTouched on the objects in the Action up. When the user lifts their finger you should set them all to isTouched == false. If you don't do that then once you touch one it will always move to the X,Y.

How can you detect which view you are passing over when performing a touch event?

I want to know how I can detect child views if I move a view from one ViewGroup to another ViewGroup, particularly when doing a touch event. Is there a method I can call that will let me know which views i'm "hovering" over?
What I'm doing right now is when I detect an ACTION_MOVE event on my view i'm raising it to the top level parent so that it can move and be drawn within the entire window ( and not just inside it's original parent bounds ), then I want to move the view across to a different ViewGroup and on ACTION_UP attach the view to that ViewGroup.
Inspired by Ami's response, but discovering that MotionEvent#getX()/getY() along with View#getTop()/etc return coordinates wrt the parent View, I ended up doing the following below to operate in screen coordinates, allowing me to work across ViewGroups:
private boolean inRegion(float x, float y, View v) {
v.getLocationOnScreen(mCoordBuffer);
return mCoordBuffer[0] + v.getWidth() > x && // right edge
mCoordBuffer[1] + v.getHeight() > y && // bottom edge
mCoordBuffer[0] < x && // left edge
mCoordBuffer[1] < y; // top edge
}
whose usage inside an OnTouchListener is e.g.:
boolean inside = inRegion(event.getRawX(), event.getRawY(), targetView);
I think I found a simpler way to do this.
Create an ArrayList of possible targets
Call this method from your touch event, supplying your targets list and the coords
private View findView(float x, float y, ArrayList<View> targets)
{
final int count = targets.size();
for (int i = 0; i < count; i++) {
final View target = targets.get(i);
if (target.getRight() > x && target.getTop() < y
&& target.getBottom() > y && target.getLeft() < x) {
return target;
}
}
return null;
}
I found Sebastian Roth's answer very helpful with resources, but since it wasn't really an answer to my question, I thought I'd share what I came up with.
Here is the code I use to detect views ( only views that will accept a drop that is ) given a coordinate on the screen.
private DropView findDropTarget( int x, int y, int[] dropCoordinates ){
final Rect r = mRectTemp;
final ArrayList<DropView> dropTargets = ((main) context).getBoardDropTargets();
final int count = dropTargets.size();
for (int i=count-1; i>=0; i--) {
final DropView target = dropTargets.get(i);
target.getHitRect(r);
target.getLocationOnScreen(dropCoordinates);
r.offset(dropCoordinates[0] - target.getLeft(), dropCoordinates[1] - target.getTop());
if (r.contains(x, y)) {
dropCoordinates[0] = x - dropCoordinates[0];
dropCoordinates[1] = y - dropCoordinates[1];
return target;
}
}
}
Ok, first off mRectTemp is just an allocated Rectangle so you don't have to keep creating new ones ( I.E. final Rect r = new Rect() )
The next line dropTargets is a list of views that will accept a drop in my app.
Next I loop through each view.
I then use getHitRect(r) to return the screen coordiantes of the view.
I then offset the coordiantes to account for the notification bar or any other view that could displace the coordiantes.
finally I see if x and y are inside the coordinates of the given rectangle r ( x and y are the event.rawX() and event.rawY() ).
It actually turned out to be simpler then expected and works very well.
Read this:
http://developer.android.com/reference/android/view/ViewGroup.html#onInterceptTouchEvent(android.view.MotionEvent)
http://developer.android.com/reference/android/view/View.html#onTouchEvent(android.view.MotionEvent)
I had implemented a Drag and Drop using that method.
I also highly recommend a read of the HomeScreen sourcecode, which contains this thing (kind of):
https://android.googlesource.com/platform/packages/apps/Launcher2

Categories

Resources