I'm trying to add swipe gesture on a basic application which just show a google map. It works fine without map. Here is my solution without the map :
private float x1,x2;
static final int MIN_DISTANCE = 150;
private Point pt;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pt = new Point();
getWindowManager().getDefaultDisplay().getSize(pt);
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
if(event.getX()>pt.x-5){
x1 = event.getX();
Log.d("DEBUUUUG", ""+event.getX());
}else{
x1=0;
}
break;
case MotionEvent.ACTION_UP:
if(x1>=pt.x-5){
x2 = event.getX();
Log.d("DEBUUUUG", "X1="+x1+" X2="+x2);
float deltaX = x2 - x1;
if (Math.abs(deltaX) > MIN_DISTANCE)
{
Toast.makeText(this, "swipe", Toast.LENGTH_SHORT).show ();
}
}
break;
}
return super.onTouchEvent(event);
}
and here is my xml layout with the map (the layout without just has a simple LinearLayout who's empty) :
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mapSee"
android:name="com.google.android.gms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
My goal is to add a right-to-left slider, like on the GoogleMaps app.
Is it possible ?
Thanks.
EDIT
Done, I've found a solution. I don't think it's the best way to do this, but it works.
I've added an invisible button :
<Button
android:id="#+id/btn_slideMenuCM"
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:background="#android:color/transparent"/>
</RelativeLayout>
I've implemented a TouchListener on this button :
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//The action to do
break;
}
return false;
}
It's not a real "swipe gesture", but it looks like.
Related
I am new to Android Studio, I need help.
Here is my ImageView layout
<ImageView
android:id="#+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="#string/app_name"
android:src="#drawable/launcher"
android:layout_centerHorizontal="true"
android:layout_centerVertical ="true"/>
I want to jump image when I touch on the image..
I have the following code:
#Override
public boolean onTouch(View v, MotionEvent event){
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN :
{
x = event.getX();
y = event.getY();
dx = x-myView.getX();
dy = y-myView.getY();
}
break;
case MotionEvent.ACTION_MOVE :
{
myView.setX(event.getX()-dx);
myView.setY(event.getY()-dy);
}
break;
case MotionEvent.ACTION_UP :
{
//your stuff
}
return true;
}
Please help me, what will be the proper code?
thats my xml layout which I am using
<com.example.pinto.myapplication.myworkspace.MyScrollView
android:id="#+id/scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.pinto.myapplication.myworkspace.MyLinearLayout
android:id="#+id/linearlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<com.example.pinto.myapplication.myworkspace.DragLayer
android:id="#+id/relative_view_drag1"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#android:color/holo_blue_dark">
</com.example.pinto.myapplication.myworkspace.DragLayer>
<com.example.pinto.myapplication.myworkspace.DragLayer
android:layout_margin="20dp"
android:id="#+id/relative_view_drag2"
android:layout_width="match_parent"
android:layout_height="600dp"
android:background="#android:color/holo_orange_dark">
</com.example.pinto.myapplication.myworkspace.DragLayer>
</com.example.pinto.myapplication.myworkspace.MyLinearLayout>
</com.example.pinto.myapplication.myworkspace.MyScrollView>
I hope this helps you with the hierarchy and let me know any changes that I need to do the layout that I have used are custom
You can use the hidden function by giving view.hidden = true else refer to the previously asked questions.
How to get the Touch position in android?
#Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int)event.getX();
int y = (int)event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
}
return false;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
float x = event.getX(); // x axis
float y = event.getY(); //y axis
return true;
}
If you want to recognize specific child layout your figner touching on with the same touch move, you should pass the touch event of your parent layout to your childviews by implementing onTouchEvent for your childviews, you could identify on which child your finger moving on.
Let's say you are having 2 child layout inside one parent
1) Implement the onTouchEvent for your parent layout
parentLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("Touch Parent : ",""+event.getAction());
if(flag){
//dispatchTouchEvent method will help us to pass parent's touch event to specific child
FirstchildLayout.dispatchTouchEvent(event);
SecondchildLayout.dispatchTouchEvent(event);
return true;
}
return false;
}
});
2) Implement onTouchListener for FirstChildLayout and identify whether your finger is moving on it or outside of it?
FirstChildLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Rect viewRect = new Rect();
FirstChildLayout.getGlobalVisibleRect(viewRect);
if (!viewRect.contains((int) event.getRawX(), (int) event.getRawY())) {
Log.d("Touch : ","Finger is moving outside of this FirstChildLayout");
}else{
Log.d("Touch : ","Finger is moving on this FirstChildLayout");
}
return false;
}
});
3) Implement onTouchListener for SecondChildLayout and identify whether your finger is moving on it or outside of it?
SecondChildLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Rect viewRect = new Rect();
SecondChildLayout.getGlobalVisibleRect(viewRect);
if (!viewRect.contains((int) event.getRawX(), (int) event.getRawY())) {
Log.d("Touch : ","Finger is moving outside of this SecondChildLayout");
}else{
Log.d("Touch : ","Finger is moving on this SecondChildLayout");
}
return false;
}
});
I have a layout with 2 LinearLayout. The first one is used as a container to contain a graph, and the second one contains few buttons.
When the app starts, at first instance the LinearLayout1 which contains the graph is hidden View.GONE.
Then when I touch a button from LinearLayout2, this layout goes back to its original place using a translate animation.
Finally, I should have the capability to hide again the LinearLayout1. I would like to achieve this by draggin up the LinearLayout2, so when the user has moved a bit upwards the LinearLayout2, the LinearLayouy1 would become again hidden by View.GONE.
This final part is the one with I need some help. I have tried something using an OnTochListener() but I haven't worked too much with this and i'm not sure about how to do it. This is code snnipet where I do this:
/*Layout views*/
private View graphContainer; //This is the LinearLayout1
private View valuesContainer; //This is the LinearLayout2
private float oldY;
private float newY;
...
valuesContainer.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float y = event.getY();
oldY = y;
break;
case MotionEvent.ACTION_MOVE:
float y2 = event.getRawY();
newY = y2;
if (oldY < newY) {
graphContainer.setVisibility(View.GONE);
}
break;
}
return true;
}
});
Depending on where I touch to do the movement I get to set the visibility to GONE, but the movement is not as I would like, I don't get to move the LinearLayout2.
What you did above is hiding layout2 when the user moves it's finger up.
You say that "you don't get to move the LinearLayout2" -> in order to move a view you need to update it's LayoutParams. You can see an example to this here : How to move a view in Android?
This way you can "push" layout2 up and at some point hide layout1 (using animation, or pushing layout1 as well). Hope this helps.
Edit (a requested code sample) :
BTW - animations transitions is the better way to go when view's params needs changing. It's not the answer to your question since you want a real "drag" feel (when going up).
Also - android L has some beautiful animations (I haven't used yet) that we should keep an eye on.
So... using a layout as follows :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:context=".MyActivity">
<LinearLayout
android:id="#+id/first_layout"
android:background="#android:color/darker_gray"
android:layout_width="match_parent"
android:layout_height="100dp" >
</LinearLayout>
<LinearLayout
android:id="#+id/second_layout"
android:background="#android:color/holo_green_dark"
android:layout_width="match_parent"
android:layout_height="100dp" >
</LinearLayout>
</LinearLayout>
and a corresponding activity as follows :
public class MyActivity extends Activity {
int yDown = 0;
int initialTopMargin = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
LinearLayout layout2 = (LinearLayout)findViewById(R.id.second_layout);
layout2.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event)
{
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
switch (event.getAction())
{
case MotionEvent.ACTION_MOVE:
params.topMargin = initialTopMargin - (yDown - (int)event.getRawY());
view.setLayoutParams(params);
break;
case MotionEvent.ACTION_DOWN:
yDown = (int)event.getRawY();
initialTopMargin = params.topMargin;
break;
}
return true;
}
});
}
}
how about using onDragListener ?
valuesContainer.setOnDragListener(new OnDragListener() {
#Override
public boolean onDrag(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float y = event.getY();
oldY = y;
break;
case MotionEvent.ACTION_MOVE:
float y2 = event.getRawY();
newY = y2;
if (oldY < newY) {
graphContainer.setVisibility(View.GONE);
}
break;
}
return true;
}
});
I have xml in which I have 2 relative layouts, the first one is map(using map fragments) and the second one is ViewPager layout. I added button to map to hide map when clicked, now I want a method to get back the map layout by sweep down the screen.
I tried setting onTouchListener to relative layout but it is not working, also tried implementing OnTouchListener
public class MainActivity extends FragmentActivity implements OnTouchListener
it is not working! how to achieve this?
#Override
public boolean onTouch(View v, MotionEvent event) {
x= event.getX();
y=event.getY();
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
sX = event.getX();
sY = event.getY();
break;
case MotionEvent.ACTION_UP:
fX = event.getX();
fY = event.getY();
if(fX-sX == 0 || fX-sX > 0 || fX-sX <0)
if(fY-sY < 0)
{
if(mapview.getVisibility()==View.GONE)
{
mapview.setVisibility(View.VISIBLE);
}
}
break;
}
return true;
}
Post your code, btw in simple way, ACTION_DOWN is when you touch the screen, ACTION_UP is when you finger release the screen. Look here
I would like to set up following layout with working scroll and click events:
My CustomSlidingDrawer.xml:
<com.project.util.CustomSliderDrawer
android:id="#+id/slidingDrawerHotelList"
android:layout_width="0dp"
android:layout_height="match_parent"
android:allowSingleTap="false"
android:content="#+id/content"
android:handle="#+id/handle"
android:orientation="horizontal" >
<RelativeLayout
android:id="#+id/handle"
android:layout_width="0dp"
android:layout_height="match_parent" >
<RelativeLayout
android:id="#+id/content"
android:layout_width="0dp"
android:layout_height="0dp" >
</RelativeLayout>
</RelativeLayout>
</com.project.util.CustomSliderDrawer>
The width of the elements are set during runtime.
Here I have to mention, that my real content is in the handle of the Drawer, and the content is only a dummy. I'm doing that in this way, because I was not able to hide the handle. Or make it invisible. So, if I want to swipe in I touch the handle (my content) and pull. In the SlidingDrawer, the handle is a placeholder for a fragment which contains a CustomListView.
What now works: I can scroll the SlidingDrawer.
What behavior I would like to gain:
On the SlidingDrawer the MotionEvents are detected in X-Axis direction (Vertical swipe). If a MotionEvent in Y-Axis direction is detected, then the Events shoud go to my CustomListView. Even the onClicks should pass through to the ListView.
What I've tried: I read a lot: here, this page and this one
Then I tried to put all the navigation control in the main activity, what makes really sense in my eyes:
MainActivity:
slidingDrawer.setOnTouchListener(new OnTouchListener() {
int downX, downY;
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
downY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int deltaX = downX - (int) event.getX();
int deltaY = downY - (int) event.getY();
if (Math.abs(deltaX) > Math.abs(deltaY)) {
Log.i(TAG, "scrollX direction");
isVerticalSwipe = true;
} else {
Log.i(TAG, "scrollY direction");
isVerticalSwipe = false;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return false;
}
});
But when I try here to return "isVerticalSwipe" as local, the SlidingDrawer works not properly anymore. :-(
The 2nd try was to intercept the touch events in the custom sliding drawer:
CustomSlidingDrawer:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, "onInterceptTouchEvent ACTION_DOWN");
xDistance = yDistance = 0f;
downX = (int) ev.getX();
downY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, "onInterceptTouchEvent ACTION_MOVE");
final int curX = (int) ev.getX();
final int curY = (int) ev.getY();
xDistance += Math.abs(curX - downX);
yDistance += Math.abs(curY - downY);
downX = curX;
downY = curY;
if (xDistance > yDistance)
return false;
}
return super.onInterceptTouchEvent(ev);
}
Here happens only the DOWN event and the MOVE event is never reached.
I also made some trys with "dispatchTouchEvent" but I got no success.
I know, that the events will pass through the "layer" of my views - from top to bottom.
So it would be perfect to know, how I can implement the construct from the first link above in my CustomSlidingDrawer (= top view) and when a vertical swipe is detected keep and handle it and if it's a horizontal swipe, then please send it to the CustomListView and perform there the events.
Any help, thoughts, ideas or code snippets are appreciated. :)
Thank you!
[EDIT]
I've made some progress. I get now my events from the SlidingDrawer down to my ListView. I can scroll and click in the list - that works fine. But unfortunately, my SlidingDrawer shows no reaction anymore. These lines are in my custom SlidingDrawer:
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
gestureDetector = new GestureDetector(new MyGestureDetector());
if (gestureDetector.onTouchEvent(event)) {
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP
|| event.getAction() == MotionEvent.ACTION_CANCEL) {
return false;
} else {
Log.i(TAG, "gestureDetector returned false");
return false;
}
}
class MyGestureDetector extends SimpleOnGestureListener {
#Override
public boolean onDown(MotionEvent e) {
Log.i(TAG, "onDown");
downX = (int) e.getX();
downY = (int) e.getY();
return false;
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
float deltaX = downX - e2.getX();
float deltaY = downY - e2.getY();
if (Math.abs(deltaX) > Math.abs(deltaY)) {
Log.i(TAG, "scrollX direction");
return true;
} else {
Log.i(TAG, "scrollY direction");
return false;
}
}
}
I don't really understand why it's not working, if there is a swipe in x-direction, then my Drawer should handle the gesture, bc I return true. Instead every event is forwarded to the ListView.
I tried to run "dispatchTouchEvent" before the "onInterceptTouchEvent" but I had no success. Maybe there is a better solution instead of using a SlidingDrawer for moving views by finger?
Okay, after a lot of tears, sweat and blood I finally found a solution which works for me: I don't take a SlidingDrawer anymore and move my layout via changing the LayoutParams.
Over the "dispatchTouchEvent" I get access to my list.
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mGestureDetector.onTouchEvent(ev)) {
return true;
}
if (ev.getAction() == MotionEvent.ACTION_UP) {
Log.i(TAG, "ACTION_UP");
Log.i(TAG, "getLeft: " + getLeft());
if (getLeft() < 280) {
// run ani to left
} else {
// run ani to right
}
}
return super.dispatchTouchEvent(ev);
}
And in my GestureDetector I handle in the overridden onScroll-function the layout translation. If someone has need for the detector don't hesitate to ask, I'll share.