I want to make a view like
Sample image
in which a want to show google maps inside a bottom sheet fragment.
What I've tried
I've tried to show maps inside a bottom sheet dialog fragment but the output isn't what I desire.
What I require is a fixed size view which should be able to display maps. Currently my view is also responding to user gestures to change bottom sheet state but I require gestures to work on map only (e.g for map panning).
When we use the map on BottomSheet, it conflicts touch events. So, need to disallow touch of BottomSheet.
Please find a below custom class which allows the map to move.
public class BottomSheetMapView extends MapView {
public BottomSheetMapView(Context context) {
super(context);
}
public BottomSheetMapView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BottomSheetMapView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public BottomSheetMapView(Context context, MapboxMapOptions options) {
super(context, options);
}
#Override
public boolean onInterceptTouchEvent(final MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
getParent().requestDisallowInterceptTouchEvent(false);
break;
default:
break;
}
return super.onInterceptTouchEvent(event);
}
}
I am using Mapbox. So, I use com.example.BottomSheetMapView instead of com.mapbox.mapboxsdk.maps.MapView in xml. Similarly, you can use Google map.
This satisfies your requirement.
I need to implement the same feature. In my case, I used a BottomSheetDialogFragment that contains SupportMapFragment. The problem was, I could only make horizontal gestures on the map like panning it, but not vertical gestures. What needs to be done is to disable the BottomSheet's touch listener while the user is doing some gestures on the map. You can refer to my similar post here to see how it should be done https://stackoverflow.com/a/53740355/1767167
Related
I'm a novice and this is my first Android app. I'm building a card game where the player should be able to tap to move a card (onClick) and also drag a card to a new location (onTouch).
I'm using CardViews for the cards.
I've built the setOnClickListener with no issues. I'm not having the same luck with the onTouchListener. I also really don't know if I should be using onTouch or onTouchListener...or maybe even onDragListener?
Based on YouTube and other StackOverflow posts, I've constructed a onTouchListener like so:
aceSpades.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
switch(event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// do work
break;
case MotionEvent.ACTION_MOVE:
// do more work
break;
case MotionEvent.ACTION_UP:
// do final work
break;
}
}
}
This works fine, I'm able to move the card around OK within the game. However, it appears that a CardView cannot have both assigned:
setOnClickListener and setOnTouchListener
Also, in the onTouchListener the whole things is highlighted in yellow with the warning that it does not override the performClick.
I've tried to understand in more detail other explanations/solutions, but what I've read so far doesn't make much sense to me.
The most popular solution I have founded instructs the creation of a new package and custom view and let the custom view manage Overrides for onTouch and performClick(). I went as far as creating the file and added it to my xml, but the cardView disappears, so obviously something is wrong. I'll provide the code for that below.
I don't really follow how the custom view solution would work - it seems to need the performClick to be added to the ACTION_UP. For my app, the onClick is a set of different methods, separate from the methods I would include in ACTION_UP. Any assistance would be helpful, I'm really stuck here. Thanks.
Class File:
package views;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
public class CustomView extends View {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomView(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
Log.d("Test", "onTouchEvent: Touched!");
switch(event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
performClick();
break;
}
return false;
}
#Override
public boolean performClick() {
super.performClick();
Log.d("Test", "performClick: clicked!");
return true;
}
}
XML:
<views.CustomView
android:id="#+id/test_drag_card"
android:layout_width="100px"
android:layout_height="250px"
android:backgroundTint="#color/standard_red"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
As I shared above, when this code the card is not even visible when I launch the app. I'm testing on a real device. I'm using ConstraintLayout for my XML.
Please let me know if I need to provide any more details for a possible solution. Thanks.
My overall goal is to be able of dragging a row from the RecyclerView to another view inside my app.
With a standard RecyclerView with LinearLayoutmanager and no extra mumbo-jumbo my drag-and-drop operations work flawlessly. However, as soon as I introduce a custom library called AndroidSwipeLayout to make each row swipeable to reveal extra actions everything fails and I get the common error:
08-28 09:59:03.465: I/ViewRootImpl(15310): Reporting drop result: false
Also I can see that the only DragEvents that are fired on my receiving view are ACTION_DRAG_STARTED and ACTION_DRAG_ENDED, all other events are skipped. As you can see I am returning true from ACTION_DRAG_STARTED but that doesn't help, my thought is that the custom library somehow eats my event. But I can't pinpoint where.
Here is my OnDragListener:
private class MyDropListener implements View.OnDragListener {
#Override
public boolean onDrag(View v, DragEvent event) {
// Doing some calculations based on event x and y. Not related to the problem.
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
// Some unrelated code, updating how views are displayed
return true;
case DragEvent.ACTION_DRAG_LOCATION:
// Some unrelated code, updating how views are displayed
return true;
case DragEvent.ACTION_DROP:
// Some unrelated code, updating some data and updating how views are displayed
return true;
case DragEvent.ACTION_DRAG_ENDED:
// Some unrelated code, updating how views are displayed
return true;
case DragEvent.ACTION_DRAG_ENTERED:
// Some unrelated code, updating how views are displayed
return true;
case DragEvent.ACTION_DRAG_EXITED:
// Some unrelated code, updating how views are displayed
return true;
default:
return false;
}
}
}
I've experimented now for a few days inside the library but can't find a solid solution exactly where my event dies. Sometimes I've managed to get the drop working, but it's very irregular behaviour.
I've also made a Github issue for this problem:
AndroidSwipeLayout - issue #211
I'm sure this is not specific to this library, but a problem when there's too much gesture detection going on for each view in a list. The library is really excellent I think and I don't wish to write that interaction myself.
Any thoughts, or comments, are welcome and appreciated. Even if you don't know the exact solution.
Thanks.
This was actually not caused by the library, or the listeners, it was caused by an EditText that was located in the same layout.
It was solved by creating a new class that subclasses EditText and ignores the dragEvent.
public class EditTextNoDrag extends EditText {
public EditTextNoDrag(Context context) {
super(context);
}
public EditTextNoDrag(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EditTextNoDrag(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public EditTextNoDrag(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public boolean onDragEvent(DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
return false;
default:
return super.onDragEvent(event);
}
}
}
You can also toggle the focusable of the EditText on and off.
See this related isseu: Prevent drag drop of custom mimetype to EditText
I have a Webview that is embedded inside a scrollview. The Webview itself has areas that are vertical scrollable.
Now if I try to scroll inside the webview, the scrollview intercepts the touchevent and scrolls the whole webview instead that only the small scrollable div is moved.
How can I make the scrollview work only if the webview does not want to scroll?
#Janusz, I have had the same problem. My solution is based on the extended scroll view behaviour in couple with the correct layout. I have wrote the answer to the same question here.
Let me know in case you have implementation problems or questions and, please inform whether it helps :)
In my case, I have a webview within a scrollview container, and the scrollview and webview are full screen. I was able to fix this by overriding the onTouch event in my webView touchListener.
scrollView = (ScrollView)findViewById(R.id.scrollview);
webView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
scrollView.requestDisallowInterceptTouchEvent(true);
return false;
}
}
Use TouchyWebView.java
public class TouchyWebView extends WebView {
public TouchyWebView(Context context) {
super(context);
}
public TouchyWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TouchyWebView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onTouchEvent(MotionEvent event){
requestDisallowInterceptTouchEvent(true);
return super.onTouchEvent(event);
}
}
In layout file:
<yourclasapath.TouchyWebView
android:id="#+id/description_web"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
Our solution uses a Javascript callback through the Javascript Interface. Every time a part of the UI that is scrollable inside the WebView is touched a listener is called through java script and this listener calls requestDisallowInterceptTouchEvent on the WebViews parent.
This is not optimal but the nicest solution found at the moment. If the user scrolls very fast the layer in the WebView won't scroll but at a normal scroll rate it works fine.
I've got a class that extends EditText and overwrites the onTouchEvent()-method in order to see when the corresponding MotionEvents occur:
public class CustomEditText extends EditText {
public CustomEditText(Context context) {
super(context);
}
public CustomEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomEditText(Context context, AttributeSet attrs, int i) {
super(context, attrs, i);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN: Log.v("ME", "down");
break;
case MotionEvent.ACTION_UP: Log.v("ME", "up");
break;
case MotionEvent.ACTION_MOVE: Log.v("ME", "move");
break;
case MotionEvent.ACTION_CANCEL: Log.v("ME", "cancel");
break;
}
return true;
}
}
When the View isn't inside a ScrollView, everything works as expected: LogCat prints "move" as long as my finger is moving around on the screen.
But when the View is inside a ScrollView and I'm moving my finger vertically, LogCat prints some "move", after a couple of millimeters a "cancel" and then nothing anymore until I replace my finger on the screen. This doesn't happen when I move horizontally.
I think the reason is that at this point the ScrollView recognizes that it should start scrolling now and consequently "steals" the MotionEvents from the View in order to evaluate them itself.
My question is now: How can I prohibit this behaviour without creating a new class extending ScrollView?
Thanking you in anticipation
Daniel R.
Why don't you try to setOnTouchListener
ex:
ScrollView yourScrollView;
yourScrollView.setOnTouchListener(new View.OnTouchListener(){
public boolean onTouch (View v, MotionEvent event){
yourTextView.onTouch(yourTextView,event);
}
});
something in these lines.. please report back so I could edit the answer to the best
(It's me, DanielR. I've now got my own account, sorry for that.)
Thanks a lot for the rapid answer, Sherif. That solves my problem.
What I am actually doing in my app is viewing a scrollable EditText that has 3 areas: a small margin on the left and the right in which you can scroll the View and a main area in the centre in which the common editing actions are performed.
What I am doing to achieve this, is:
1. when the user starts a gesture in the margins, I set the onTouchListener to null, so the ScrollView's scrolling action is performed (read that in a post somewhere around here).
2. touching the main area, the onTouchListener is set to the one you suggested above, so scrolling is disabled AND all touch events reach the EditText. Previously, my onTouchListener was empty so merely scrolling was prohibited. I don't know why I didn't see that myself. I think it's just too late ;)
Once again, thank you a lot.
Is there a way of disabling panning/zooming and keeping map overlays clickable?
I am specifically thinking about an ItemizedOverlay that I want to be tappable while denying users from moving around the map's viewport (It's for a game).
I've seen the same question answered but the answer sadly doesn't allow for overlay items to be clicked.
Thanks alot,
best regards,
Alex
I know this question is a little old but I had the same problem and found a workaround, it's not a graceful solution but appears to work.
First you have to create your own map subclass overriding the onTouchEvent handler
public class NonPannableMapView extends MapView {
public NonPannableMapView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public NonPannableMapView(Context context, AttributeSet attrs){
super(context, attrs);
}
public NonPannableMapView(Context context, String apiKey) {
super(context, apiKey);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
//Handle a move so there is no panning or zoom
if(ev.getAction() == MotionEvent.ACTION_MOVE)
return true;
return super.onTouchEvent(ev);
}
}
Then it's just a case of changing the MapView reference in your layout to NonPannableMapView.
Try this:
mapView.setBuiltInZoomControls(false);
Hope that works! Have fun!