I'm programming a custom ChipGroup class and in the layout I have a HorizontalScrollView and a Material ChipGroup inside. In my constructor I pass a boolean called "isScrollable" because I want it to be configurable the scrolling.
Is there a way to inactivate the scroll of the HorizontalScrollView if scrolling is configured as false?
You cannot disable the scrolling of a ScrollView. You would need to
extend to ScrollView and override the onTouchEvent method to return
false when some condition is matched.
The Gallery component scrolls
horizontally regardless of whether it is in a ScrollView or not - a
ScrollView provides only vertical scrolling (you need a
HorizontalScrollView for horizontal scrolling).
You seem to say you
have a problem with the image stretching itself -- this has nothing
to do with the ScrollView, you can change how an ImageView scales
with the android:scaleType property (XML) or the setScaleType method
for instance ScaleType.CENTER will not stretch your image and will center it at it's original size.
You could modify ScrollView as follows to disable scrolling:
class LockableScrollView extends ScrollView {
// true if we can scroll (not locked)
// false if we cannot scroll (locked)
private boolean mScrollable = true;
public void setScrollingEnabled(boolean enabled) {
mScrollable = enabled;
}
public boolean isScrollable() {
return mScrollable;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// if we can scroll pass the event to the superclass
return mScrollable && super.onTouchEvent(ev);
default:
return super.onTouchEvent(ev);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Don't do anything with intercepted touch events if
// we are not scrollable
return mScrollable && super.onInterceptTouchEvent(ev);
}
}
then,
<com.mypackagename.LockableScrollView
android:id="#+id/QuranGalleryScrollView"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<Gallery android:id="#+id/Gallery"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="horizontal">
</Gallery>
</com.mypackagename.LockableScrollView>
in your XML file (just changed the ScrollView to your special LockableScrollView).
Then call
((LockableScrollView)findViewById(R.id.QuranGalleryScrollView)).setScrollingEnabled(false);
to disable scrolling of the view.
Related
I have a ViewPager below a NestedScrollView width some top padding, and clipToPadding(false) and transparent background (like as image).
My ViewPager can't get touch event and doesn't work.
How can I solve this problem?
(I can't change my structure and can't move ViewPager to above of NestedScrollView or set TopMargin to NestedScrollView)
NestedScrollView
nestedScrollView = new NestedScrollView(getContext());
nestedScrollView.setFillViewport(true);
nestedScrollView.setLayoutParams(scrollParams);
nestedScrollView.setClipToPadding(false);
Solution:
This Problem solved With overwriting NestedScrollView and Override onTouchEvent.
(Thanks to #petrumo)
public class MyNestedScrollView extends NestedScrollView {
private boolean topZone = false;
public MyNestedScrollView(Context context) {
super(context);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_DOWN ){
topZone = (getPaddingTop() - getScrollY() > ev.getY());
}
if(topZone){
if(ev.getAction() == MotionEvent.ACTION_UP){
topZone = false;
}
return false;
}
return super.onTouchEvent(ev);
}
}
There is a workaround for this case, you can override onInterceptTouchEvent and onTouchEvent in the nestedscrollview. There are posts explaining how to do it, https://developer.android.com/training/gestures/viewgroup.html and http://neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html. When you intercept the event, based on the position and your custom logic you would decide to not use the touch to leave it for the viewpager or let the default scrollview logic handle it.
I am not in favor of this solution, but as you explained you need to have the NestedScrollview cover the viewPager, unless you can reconsider the restrictions
I have a ListView inside ScrollView. I can enable scroll of ListView by
listView.getParent().requestDisallowInterCeptTouchEvent(true);
But the problem is when i scroll up in listView and it reaches top it should scroll to parent view i.e. parent scroll has to work . How can i do this ? any suggestion please.
listView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
return true; // Indicates that this has been handled by you and will not be forwarded further.
}
return false;
}
});
OR
To make the View unselectable just get the view and .setClickable(false)
OR
listView.setScrollContainer(false);
You can override ScrollView class and insert these methods inside:
private boolean isScrollEnabled = true;
public void enableScroll(boolean isScrollEnabled ) {
this.isScrollEnabled = isScrollEnabled ;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (isScrollEnabled) {
return super.onInterceptTouchEvent(ev);
} else {
return false;
}
}
This is the cleanest solution to achieve this. You only call scrollView.enableScroll(true) to enable scrolling or scrollView.enableScroll(false) to disable it.
I would suggest to embed your upper view i.e any viewgroup above list view into listview header. ListView has a method, listview.addHeaderView(). That way you would be able to scroll your list (Whole View) even on small size display and you don't need scrollview.
I have a custom view that extends Android ScrollView. The direct child is a relative layout which has children that are clickable.
I want to be able to:
detect onScale Gesture on the scroll view (than I will manually manage the scale of the items).
scroll the ScrollView vertically.
keep those child items clickable.
What I have tried so far is (pseudo code):
public class CustomView extends ScrollView {
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(ev) || mScaleDetector.onTouchEvent(ev);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
return mScaleDetector.onTouchEvent(ev);
}
private class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
// Handle the scale..
return true;
}
}
}
I also tried different configurations for the onInterceptMethod such as first call the super and the return the mScaleDetector.onTouchEvent and so on.
I succeeded to intercept the scale or the click and scroll but not both.
Thanks,
Daniel
The solution is to use:
#Override
public boolean dispatchTouchEvent(MotionEvent ev){
super.dispatchTouchEvent(ev);
return mScaleDetector.onTouchEvent(ev);
}
and not override onInterceptTouchEvent and onTouchEvent methods.
I have a ScrollView on top of another view(with Buttons). The ScrollView is taking the whole screen and is obscuring the view that is beneath it.
At some point in my app I need the ScrollView to be disabled (but still visible) and transfer all the touch events to the Buttons that are beneath the ScrollView. How can I do that? Some views like Buttons are automatically doing that when disabled but a ScrollView is not doing that.
Try to implement your own ScrollView which has a flag to indicate the status(disabled/enabled) and also overrides the onTouchEvent and dispatchTouchEvent to let the touch events get pass the ScrollView. Here is an example:
public class DisabledScrollView extends ScrollView {
private boolean mIsDisable = false;
// if status is true, disable the ScrollView
public void setDisableStatus(boolean status) {
mIsDisable = status;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// no more tocuh events for this ScrollView
if (mIsDisable) {
return false;
}
return super.onTouchEvent(ev);
}
public boolean dispatchTouchEvent(MotionEvent ev) {
// although the ScrollView doesn't get touch events , its children will get them so intercept them.
if (mIsDisable) {
return false;
}
return super.dispatchTouchEvent(ev);
}
}
Then all you have to do is change the value of that flag. See if it works.
In my case, I just needed to handle the touch event in View A, which was overlaping View B and then send the event to View B. Both views were child of the same RelativeLayout, but there was no parent-child relation between views A and B. This worked for me:
viewA.setOnTouchListener( new OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
// do my stuff here
viewB.dispatchTouchEvent( event );
}
}
In this case I have a recyclerview under a scrollview. The top of scrollview is in vertical scroll, and the recyclerview is in horizontal scroll. The scrollview have top padding, making the recyclerview is visible through the transparency in the scrollview padding. I have to make it this way because when the scrollview is scrolled the recyclerview will scroll vertically to like parallax effect (this effect is in another code). This code below is working for my case, might help
scrollView.setOnTouchListener(new View.OnTouchListener() {
float mDownX,mDownY;
boolean mIsSwiping,isDown;
#Override
public boolean onTouch(View v, MotionEvent event) {
if(mIsSwiping){
recyclerView.dispatchTouchEvent(event);
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mIsSwiping = false;
isDown = true;
mDownX = event.getX();
mDownY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
if(isDown){
float deltaX = Math.abs(event.getX() - mDownX);
float deltaY = Math.abs(event.getY() - mDownY);
mDownX = event.getX();
mDownY = event.getY();
if(deltaX!=deltaY){
isDown = false;
if(deltaX>deltaY){
mIsSwiping = true;
}
}
}
}
return mIsSwiping;
}
});
This is the layout
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/gray0"
android:clipToPadding="false"
android:clipChildren="false"
android:paddingBottom="70dp">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="320dp"
android:orientation="horizontal"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"/>
<ScrollView
android:id="#+id/scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="301.75dp"
android:paddingBottom="23.5dp"
android:clipChildren="false"
android:clipToPadding="false">
.
.
.
In my viewFlipper some TextViews are loaded dynamically. The size may differ which means that under the viewFlipper there could be some space left (see the green part in the screenshot)
screenshot
I want the onFling method to be called not only when swiping on the grey part (which is the viewflipper) but also when swiping on the green part
my layout looks like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:id="#+id/root">
<ViewFlipper android:id="#+id/viewFlipper"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</ViewFlipper>
</RelativeLayout>
In my onCreate i do this:
this.viewFlipper = (ViewFlipper) this.findViewById(R.id.viewFlipper);
this.gestureDetector = new GestureDetector(new MyGestureDetector());
RelativeLayout root = (RelativeLayout) this.findViewById(R.id.root);
root.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
Log.d("root", "true");
return false;
} else {
Log.d("root", "false");
return false;
}
}
});
So far I tried to return false even if I get true, so that the event does not get consumed and gets passed down to the viewFlipper even if the swipe has been made outside the viewflipper.
Note that the viewFlipper does not need any explicit onTouchListener. It works with or without one ( I dont really understand why..)
Does anyone know what to do?
Try adding android:clickable="true" to your RelativeLayout and you should be able to do it.
If that doesn't work you might need to implement these two methods also (if you haven't allready):
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//TouchEvent dispatcher.
if (gestureDetector != null) {
if (gestureDetector.onTouchEvent(ev))
//If the gestureDetector handles the event, a swipe has been executed and no more needs to be done.
return true;
}
return super.dispatchTouchEvent(ev);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}