I have a StreetViewPanoramaView inside a linearlayout inside a scrollview:
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scrollview">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="#+id/linear">
<com.google.android.gms.maps.StreetViewPanoramaView
android:id="#+id/street_view_panorama"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<other views - textviews etc>
</LinearLayout>
</ScrollView>
The StreetViewPanoramaView allows a user to move the streetview camera up and down and left and right. I want the scrollview to stop intercepting the user touch events when the user is moving the camera. Currently, if the user moves the camera in the StreetViewPanoramaView, the scrollview intercept the touch and moves the scrollview instead.
I tried this code from https://mobiarch.wordpress.com/2013/11/21/prevent-touch-event-theft-in-android/ but its doesn't seem to be working (scrollview is still taking the touch events):
mStreetViewPanoramaView = (StreetViewPanoramaView) findViewById(R.id.street_view_panorama);
LinearLayout.LayoutParams streetViewLp =
new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, displaymetrics.widthPixels/3);
mStreetViewPanoramaView.setLayoutParams(streetViewLp);
disableTouchTheft(mStreetViewPanoramaView);
public static void disableTouchTheft(View view) {
view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
view.getParent().getParent().requestDisallowInterceptTouchEvent(true);
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_UP:
view.getParent().getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return false;
}
});
}
EDIT:
Things I have tried:
I have made a custom view and imported the code dispatchTouchEvent into my activity code:
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
int[] loc = {0, 0};
mStreetViewPanoramaView.getLocationOnScreen(loc);
Rect rect = new Rect(loc[0], loc[1] , loc[0] + mStreetViewPanoramaView.getWidth(), loc[1] + mStreetViewPanoramaView.getHeight());
if (rect.contains((int) event.getRawX(), (int) event.getRawY())) {
Log.e("stretview", "dispatched");
return mStreetViewPanoramaView.dispatchTouchEvent(event);
/* return true;*/
}else{
//Continue with touch event
return super.dispatchTouchEvent(event);
}
}
public class CustomStreetViewPanoramaView extends StreetViewPanoramaView {
public CustomStreetViewPanoramaView(Context context) {
super(context);
}
public CustomStreetViewPanoramaView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomStreetViewPanoramaView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomStreetViewPanoramaView(Context context, StreetViewPanoramaOptions options) {
super(context, options);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return false; // Do not intercept touch event, let the child handle it
}
}
The code appears to half-work - it appears that the StreetViewParnoramaView will obtain the touch events for the top half of the view but the bottom half does not obtain any touch events.
My full layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:id="#+id/container"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scrollview">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="#+id/linearlayout">
<com.example.simon.customshapes.CustomStreetViewPanoramaView
android:id="#+id/street_view_panorama"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="500dp"
android:id="#+id/card"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello card"
android:layout_gravity="center" />
</android.support.v7.widget.CardView>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Save panorama"
android:padding="8dp"
android:id="#+id/savepano"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/my_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
</ScrollView>
</LinearLayout>
The answer of Johann is the right path but still not working - this one will work
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (streetView != null) {
streetView.getGlobalVisibleRect(rect);
if (rect.contains((int) event.getRawX(), (int) event.getRawY())) {
event.offsetLocation(-rect.left, -rect.top);
streetView.dispatchTouchEvent(event);
return true;
} else {
//Continue with touch event
return super.dispatchTouchEvent(event);
}
} else {
return super.dispatchTouchEvent(event);
}
}
dispatchTouchEvent of the activity and move the event after offsets it to the streetView
Can you try the following...
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
int[] loc = {0, 0};
mStreetViewPanoramaView.getLocationOnScreen(loc);
Rect rect = new Rect(loc[0], loc[1] , loc[0] + mStreetViewPanoramaView.getWidth(), loc[1] + mStreetViewPanoramaView.getHeight());
if (rect.contains((int) event.getRawX(), (int) event.getRawY())) {
mStreetViewPanoramaView.dispatchTouchEvent(event);
return true;
}else{
//Continue with touch event
return super.dispatchTouchEvent(event);
}
}
This hopefully will check if the touch was on the panoramaview and handle accordingly.
Related
I am able to implement TabLayout with ViewPager nicely but when the content inside the ViewPager is being scrolled (the deceleration animation), I cannot swipe left and right to change to other fragments inside ViewPager. I have to wait until the scrolling animation stops and then swipe.
Below is some of my code snippets.
I have a simple activity_main.xml layout like below.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbarHome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimary"/>
<LinearLayout
android:id="#+id/mainContainerLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical">
</LinearLayout>
<android.support.design.widget.BottomNavigationView
android:id="#+id/btmNavView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white"
app:menu="#menu/menu_main" />
</LinearLayout>
I then inflate a fragment containing TabLayout and ViewPager into the LinearLayout in the middle of the activity_main.xml. This layout can be seen below:
<LinearLayout 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"
android:orientation="vertical">
<com.pchatanan.sontana.custom_views.AppTabLayout
android:id="#+id/contactTabLayout"
app:tabMode="fixed"
android:layout_height="wrap_content"
android:layout_width="match_parent"
style="#style/AppTabLayout"/>
<android.support.v4.view.ViewPager
android:id="#+id/contactViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
The logic inside my fragment is below:
fragmentArray = new Fragment[]{new SimpleFragment(), new SimpleFragment(), new SimpleFragment()};
titleArray = new String[]{"fragment1", "fragment2", "fragment3"};
contactPagerAdapter = new AppPagerAdapter(getChildFragmentManager(), fragmentArray, titleArray);
contactViewPager.setAdapter(contactPagerAdapter);
contactViewPager.setOffscreenPageLimit(fragmentArray.length - 1);
contactTabLayout.setupWithViewPager(contactViewPager);
Use this instead defualt ViewPager.
Detect when ScrollView or RecyclerView is scrolling. Then call
viewPager.setPagingEnabled(pagingEnable);
If pagingEnable
public class CustomViewPager extends ViewPager {
private boolean isPagingEnabled = true;
public CustomViewPager(Context context) {
super(context);
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onTouchEvent(MotionEvent event) {
return this.isPagingEnabled && super.onTouchEvent(event);
}
public boolean onInterceptTouchEvent(MotionEvent event) {
return this.isPagingEnabled && super.onInterceptTouchEvent(event);
}
public void setPagingEnabled(boolean b) {
this.isPagingEnabled = b;
}
}
Update:
You can do reverse to like you can disable ScrollView scroll when ViewPager is swiping
mScrollView.requestDisallowInterceptTouchEvent(true);
Similar to what suggested by #Khemraj, the problem is the touch is not intercepted by ViewPager. It is only intercepted by the ScrollView. To fix this, we should allow ViewPage to intercept the touch when scrolling:
scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
#Override
public void onScrollChange(View view, int i, int i1, int i2, int i3) {
viewPager.requestDisallowInterceptTouchEvent(false);
}
});
A better implementation is to make use of CallBackListener onto the fragment containing the scrollView.
I made slidinguppanel using this repository https://github.com/dlukashev/AndroidSlidingUpPanel-foursquare-map-demo
However, it contains one bug which is not covered anywhere.
When I touch anywhere to expand panel (listview) works well, but while I'm trying to expand it by holding a top of a list view (blue line on screen2) panel hide under the map (framelayout) (screen3)
How it's even possible that this blue line hiding panel under mapfragment and rest of listview expand it well?
Any ideas why? Please give me just a hint how to fix it?
Please look at the screen:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<org.sothree.slidinguppanel.SlidingUpPanelLayout
android:id="#+id/slidingLayout"
android:gravity="bottom"
app:shadowHeight="0dp"
app:paralaxOffset="#dimen/paralax_offset"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<FrameLayout
android:gravity="top"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#+id/button">
<RelativeLayout
android:id="#+id/mapContainer"
android:layout_width="fill_parent"
android:layout_height="497dp"/>
</FrameLayout>
<RelativeLayout
android:id="#+id/slidingContainer"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#+id/button">
<View
android:id="#+id/transparentView"
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="#dimen/map_height"
android:layout_alignParentTop="true"/>
<ListView
android:id="#+id/listView1"
android:cacheColorHint="#android:color/white"
android:drawSelectorOnTop="true"
android:dividerHeight="#dimen/divider_height"
android:divider="#android:color/darker_gray"
android:background="#android:color/transparent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#id/transparentView"
android:smoothScrollbar="true"
android:layout_marginTop="0dp"/>
</RelativeLayout>
</org.sothree.slidinguppanel.SlidingUpPanelLayout>
</RelativeLayout>
You need to override ListView
public class LockableListView extends ListView {
private boolean mScrollable = true;
public LockableListView(Context context) {
super(context);
}
public LockableListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LockableListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setScrollingEnabled(boolean enabled) {
mScrollable = enabled;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// if we can scroll pass the event to the superclass
if (mScrollable) {
return super.onTouchEvent(ev);
}
// only continue to handle the touch event if scrolling enabled
return mScrollable; // mScrollable is always false at this point
default:
return super.onTouchEvent(ev);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Don't do anything with intercepted touch events if
// we are not scrollable
if (!mScrollable) {
return false;
} else {
return super.onInterceptTouchEvent(ev);
}
}
}
Then put disable scrolling when needed
private void collapseMap() {
mSpaceView.setVisibility(View.VISIBLE);
mTransparentView.setVisibility(View.GONE);
if (mMap != null && mLocation != null) {
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(mLocation, 11f), 1000, null);
}
mListView.setScrollingEnabled(true);
}
private void expandMap() {
mSpaceView.setVisibility(View.GONE);
mTransparentView.setVisibility(View.INVISIBLE);
if (mMap != null) {
mMap.animateCamera(CameraUpdateFactory.zoomTo(14f), 1000, null);
}
mListView.setScrollingEnabled(false);
}
I pushed this changes to GitHub https://github.com/dlukashev/AndroidSlidingUpPanel-foursquare-map-demo/commit/7b869394e9d197e6f56a833804426577dcb8458a
Enjoy
create a custom view for color picker that contains image and I'm going to take a color from this image on touch bot neither onTouch(View v, MotionEvent ev) nor onClick(View v) is called.
So here is my view class:
public class ChangeColor extends RelativeLayout {
public ChangeColor(Context context) { super(context); init(); }
public ChangeColor(Context context, AttributeSet attrs) { super(context, attrs); init(); }
private void init(){
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.change_color, this);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
ImageView iv = (ImageView)findViewById(R.id.colorScaleImageView);
final Bitmap bitmap = ((BitmapDrawable) iv.getDrawable()).getBitmap();
iv.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.d("ImageView", "onClick();");
}
});
iv.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent ev) {
Log.d("ChangeColor", "onTouch();");
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
int c = bitmap.getPixel((int)ev.getX(), (int)ev.getY());
SettingsManager.setColor(c);
Log.d("ChangeColor", "" + c);
return false;
default:
return true;
}
}
});
}
}
and layout xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/RelativeLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:id="#+id/colorScaleImageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#+id/seekBar1"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:clickable="true"
android:layout_gravity="center_horizontal"
android:src="#drawable/background_color" />
<SeekBar
android:id="#+id/seekBar1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#+id/textView1"
android:layout_alignParentLeft="true"
android:focusable="false"
android:maxHeight="8dp"
android:minHeight="8dp"
android:paddingLeft="7dp"
android:paddingRight="7dp"
android:progressDrawable="#drawable/seekbar_progress"
android:thumb="#drawable/seekbar_thumb_"
android:thumbOffset="7dp" />
</RelativeLayout>
So no one log message appear in LogCat.
P.S. I saw few similar questions on SO but they did not helped me.
P.P.S. This view is used in fragment and fragment is used in Activity. I added a fullscreen view in that activity with OnClickListener in order to see if clicks go to the parent view and I can see Log message from that background view only I tap outside the image and no message if I tap on image.
Thanks.
You have extended the relative layout but are not calling it in this example.
In the XML you should implement the class you developed instead of RelativeLayout
try
<?xml version="1.0" encoding="utf-8"?>
<**namespace**.ChangeColor xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/RelativeLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:id="#+id/colorScaleImageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#+id/seekBar1"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:clickable="true"
android:layout_gravity="center_horizontal"
android:src="#drawable/background_color" />
</**namespace**.ChangeColor>
**EDIT
It was correct the first time. To implement it you'll have to attach ChangeColor to another layout, it cannot be called in the layout that it is attempting to inflate when it's called. Ex. ChangeColor inflates change_color.xml, you cannot have the tag NAMESPACE.ChangeColor called in the layout that ChangeColor.init() is inflating.
I have a custom view class with a button and I let the user touch the screen so I can retrieve the coordinates. What I want to do is, whenever he clicks the button, to set its text to the coordinates of his touch. How do I do this?
public class TargetView extends RelativeLayout{
.
.
.
public float x=0;
public float y=0;
public TargetView(final Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
((Activity)getContext())
.getLayoutInflater()
.inflate(R.layout.target_view, this, true);
Target=findViewById(R.id.target);
Undo=(Button)findViewById(R.id.undo_bt);
Undo.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
Log.d("x,y",(String.valueOf(x)+","+String.valueOf(y)));
Undo.setText(String.valueOf(x)+","+String.valueOf(y));
}
});
#Override
public boolean onTouchEvent(MotionEvent event){
switch (event.getAction()){
case (MotionEvent.ACTION_DOWN): {
x = event.getX();
y = event.getY();
}
return false;
}
.
.
.
}
EDIT
I have added a Log.d inside the button listener and it seems that the button gets clicked only before the user touches the rest of the screen and changes x,y (text changes to "0.0").
My .xml files:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<android.archer.test.TargetView
android:id="#+id/myTargetView"
android:layout_width="300dip"
android:layout_height="300dip"
android:layout_gravity="center_horizontal"
android:background="#drawable/target" >
</android.archer.test.TargetView>
</LinearLayout>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<View
android:id="#+id/target"
android:layout_width="300dp"
android:layout_height="350dip" />
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="#+id/undo_bt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:text="-" />
</RelativeLayout>
- Return super.onTouchEvent(event) at the end of onTouchEvent method.
Eg:
#Override
public boolean onTouchEvent(MotionEvent event){
switch (event.getAction()){
case (MotionEvent.ACTION_DOWN): {
x = event.getX();
y = event.getY();
}
return super.onTouchEvent(event);
}
- Moreover, there is an hierarchy of views that Android maintains and the events are handled accordingly. Every View in the hierarchy gets the event, but only if the parent event has not consume it. Meaning - if you have a parent view that handles onTouchEvent() and returns true every time, then the child never receives the event.
Change return in your snippet:
#Override
public boolean onTouchEvent(MotionEvent event){
switch (event.getAction()){
case (MotionEvent.ACTION_DOWN): {
x = event.getX();
y = event.getY();
}
return false;
}
to return true;
And if possible, you can put log into your onTouchEvent to see if it can return the coordinates.
I can't get onTouch event triggered on a container when clicking on its ScrollView child.
I'm using API 12 on a Galaxy tab 10.1.
Anybody can help?
Thanks
Activity
public class TestActivity extends Activity{
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.dude);
LinearLayout mylayout = (LinearLayout) findViewById(R.id.mylayout);
mylayout.setOnTouchListener(new OnTouchListener () {
public boolean onTouch(View view, MotionEvent event) {
// Only called when touched outside the ScrollView
if (event.getAction() == android.view.MotionEvent.ACTION_DOWN) {
/* do stuff */
} else if (event.getAction() == android.view.MotionEvent.ACTION_UP) {
/* do stuff */
}
return true;
}
});
}
}
Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mylayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:gravity="center"
android:text="Touch here trigger parent's onTouch"
android:textColor="#000000"
android:textSize="40sp" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:layout_width="match_parent"
android:layout_height="1000dp"
android:background="#b00000"
android:gravity="center"
android:text="Touch here DOES NOT trigger parent's onTouch"
android:textColor="#000000"
android:textSize="40sp" />
</RelativeLayout>
</ScrollView>
</LinearLayout>
mylayout.setOnTouchListener(new OnTouchListener () {
public boolean onTouch(View view, MotionEvent event) {
// Only called when touched outside the ScrollView
if (event.getAction() == android.view.MotionEvent.ACTION_DOWN) {
/* do stuff */
return true;
} else if (event.getAction() == android.view.MotionEvent.ACTION_UP) {
/* do stuff */
return true;
}
return false;
}
});
this should work... but you no longer have auto-scroll when you fling it hard... it'll be a sticky scroll moving only as much as you drag.
Did u try to create ONE OnClickListener and add it to all childs?
Maybe this could solve your
As the ScrollView makes it childs scrollable it wouldn't have an own area to click in.
(Correct me if I'm wrong ^^)
You probably need this code
#Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
onTouchEvent(ev);
return false;
}