Cannot select right button on ListView with FastScroll enabled - android

I have a ListView with image on left, title & subtitle in the centre and an ImageButton on the right (this button doesn't have any margin on the right).
<ListView
android:id="#+id/contacts"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="#android:color/transparent"
android:fastScrollEnabled="true"
android:scrollbarStyle="outsideInset"/>
I have enabled fast scrolling for this ListView. When I try to click the right ImageButton the scrollbar comes in focus and ListView starts scrolling. I am not able to select the button on the right. Please help me out.

You need to override ListView class and its onInterceptTouchEvent method.
public class CustomListView extends ListView {
public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
setFastScrollEnabled(false);
boolean possibleResult = super.onInterceptTouchEvent(ev);
setFastScrollEnabled(true);
boolean actualResult = super.onInterceptTouchEvent(ev);
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
return possibleResult && actualResult;
}
return super.onInterceptTouchEvent(ev);
}
}
And it will look like:
However, the issue which you observe is an expected behaviour.
The proper fix would be to add padding to the end of your row.
Take a look on the Google's PhoneBook app, for example:
As you can see here, the cell-size is smaller, than 100% of screen's width.
I hope, it helps

Related

implementing Zoomable TextView in scrollView

I have a TextView inside a ScrollView I want to be able to change the font of the TextView whenever the user pinches it. I've been searching for a day now and I didn't find anything satisfactory. While I was hopping this to be pretty straightforward. Do you have any suggestions?
Ok, first implement pinch to zoom in TextView, you can get some idea from here and then as according to the scale value of user pinch try to set textSize of TextView. After that immediate disable touch event of ScrollView when user pinch or touch to the TextView. You can do that in this way(Just use variable instead of true/false return):
public class InterceptScrollView extends ScrollView {
public InterceptScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
}
and when user done with TextView then again enable the touch event of ScrollView.

Limited Swipe Area Android

I need to limit the swipe area inside a ViewPager. For example, if the user make the gesture to swipe on top half space of the screen it swipe to the next fragment, but if the user make the gesture on the bottom half of the screen it do nothing.
There is a way to do that?
This might be a what you need:
public class MyPager extends ViewPager {
public MyPager(Context context) {
super(context);
}
public MyPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(inNeutralArea(ev.getX(),ev.getY())){
//--events re-directed to this ViewPager's onTouch() and to its child views from there--
return false;
}else {
//--events intercepted by this ViewPager's default implementation, where it looks for swipe gestures--
return super.onInterceptTouchEvent(ev);
}
}
private boolean inNeutralArea(float x, float y) {
//--check if x,y inside non reactive area, return true/false accordingly--
return false;
}
}
Use this MyPager class in layout xml in place of ViewPager.
Don't forget to do to the same in overriding the onTouchEvent, otherwise the ViewPager will still scroll on Android 4.1 and later. Caught me ofguard and took me a while to figure it out.

Android - how to disable a ScrollView and all children

edited for clarity
I feel like this question already has an answer, but I can't find one.
I have a ScrollView in my layout, and it contains a variety of clickable views.
Under a specific condition I would like to disable clicks and events for the ScrollView and ALL of its children.
The following have not been helpful:
ScrollView.setEnabled(false)
ScrollView.setClickable(false)
ScrollView.setOnTouchListener(null)
As well as:
(parent view of the ScrollView).requestDisallowInterceptTouchEvent()
I have created a custom ScrollView with the following code:
public class StoppableScrollView extends ScrollView
{
private static boolean stopped = false;
public StoppableScrollView(Context context)
{
super(context);
}
public StoppableScrollView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
if(stopped)
{
return true;
}
else
{
super.onInterceptTouchEvent(ev);
return false;
}
}
#Override
public boolean onTouchEvent(MotionEvent ev)
{
if(stopped)
{
return true;
}
else
{
super.onTouchEvent(ev);
return false;
}
}
public static void setStopped(boolean inBool)
{
stopped = inBool;
}
public static boolean getStopped()
{
return stopped;
}
}
Using only onTouchEvent() will stop the scrolling, but not the clicking of child views.
Using only onInterceptTouchEvent() makes it such that when clicks work scrolling does not, and vice versa.
Using both onTouchEvent() and onInterceptTouchEvent() successfully stops unwanted clicks on child views when stopped is 'true' but it also disables scrolling regardless of the state of stopped.
Is there an easier way to get this behaviour, or is there a way to modify the StoppableScrollView class so that it will handle these touch events properly?
What probably should help is the following (because I had similar problems):
In the ScrollView you should do a RelativeLayout as Main Child (ScrollView does accept only 1 main child anyway). This RelativeLayout should of course of fill_parent in both directions.
At the really end of the RelativeLayout (after all other children), you could put now a LinearLayout with transparent background (#00FFFFFF) which has also fill_parent in both directions. This LinearLayout should have Visibility = View.GONE (by default)
Also you have to attach an empty OnClickListener to it. Now, because of zOrder if you make this LinearLayout Visibility = View.Visible it will catch all the events and avoid clicking the children above!
As scrollview allows immeditate one child say in my case i have linear layout.and in this linear layout i have other conreolls.
now our first task is to get this linear layout so what we can write is
LinearLayout l = (LinearLayout) scrollview.getChildAt(0);
now after getting this linear layour we can easily access other controlls placed inside it via this code and disable it.
for(int i =0; i<l.getChildCount(); i++)
{
Log.i(TAG,"child "+ l.getChildAt(i));
l.getChildAt(i).setEnabled(false);
}

Android WebView inside ListView onclick event issues

I have a ListView where each row has two webviews side by side, taking up the entire row. I've set up onListItemClick() in my ListActivity, but they are not fired when I tap on one of the rows (unless the place I happen to tap is outside the webview's border - but this isn't likely behavior, users would most likely want to tap on the image inside the webview).
I've tried setting setFocusable(false) and setFocusableInTouchMode(false), but those don't help.
Ideally this would operate exactly like Facebook's newsfeed, where you can tap on the profile picture of someone's wall post and it acts as if you've tapped the entire row (in FBs case the text for the wall post)
Figured it out, posting my solution in case someone else wants to do something similar:
I had to use an OnTouchListener, since OnClick and OnFocus weren't working. I extended a class that is reuseable:
private class WebViewClickListener implements View.OnTouchListener {
private int position;
private ViewGroup vg;
private WebView wv;
public WebViewClickListener(WebView wv, ViewGroup vg, int position) {
this.vg = vg;
this.position = position;
this.wv = wv;
}
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_CANCEL:
return true;
case MotionEvent.ACTION_UP:
sendClick();
return true;
}
return false;
}
public void sendClick() {
ListView lv = (ListView) vg;
lv.performItemClick(wv, position, 0);
}
}
The sendClick method could be overridden to do what's needed in your specific case. Use case:
WebView image = (WebView) findViewById(R.id.myImage);
image.setOnTouchListener(new WebViewClickListener(image, parent, position));
I managed to get this working with the following:
class NoClickWebView extends WebView {
public NoClickWebView(Context context) {
super(context);
setClickable(false);
setLongClickable(false);
setFocusable(false);
setFocusableInTouchMode(false);
}
}
You can use this class or just set these properties on a standard WebView.
Got it working in Android 4.4 using the same approach as bjdodson above but also overriding dispatchTouchEvent
public class NonClickableWebview extends WebView {
public NonClickableWebview(Context context, AttributeSet attrs) {
super(context, attrs);
setClickable(false);
setLongClickable(false);
setFocusable(false);
setFocusableInTouchMode(false);
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return false;
}
}
Setup to parent layout of each WebView:
android:descendantFocusability="blocksDescendants"
android:layout_width="match_parent"
android:layout_height="wrap_content"
I just tried what is suggested in this post WebView inside the Custom ListView: Click Listener is not Working, I dunno why but if you set these features in the XML they don't seem to work. Doing this dynamically does allow you to click on the listview item =)
I was using the following layout in ListView and it was working perfectly, ie its clicking, scrolling and so on.
TextView1
ImageView1 TextView2 ImageView2
TextView3
Then I have changed the layout with following, ie I have added with WebView control in left most corner in place of ImageView1
TextView1
WebView TextView2 ImageView2
TextView3
After doing this, clicking was not working for me.
I solve this problem by adding this to the Webview:
webView.setFocusable(false);
webView.setClickable(false);

two directional scroll view

I would like to have a linearlayout with a header section on top and a webview below. The header will be short and the webview may be longer and wider than the screen.
What is the best way to get horizontal and vertical scrolling? Is a ScrollView nested inside a HorizontalScrollView a good idea?
Is a ScrollView nested inside a HorizontalScrollView a good idea?
Yes, and no.
Yes, my understanding is that ScrollView and HorizontalScrollView can be nested.
No, AFAIK, neither ScrollView nor HorizontalScrollView work with WebView.
I suggest that you have your WebView fit on the screen.
there is another way. moddified HorizontalScrollView as a wrapper for ScrollView. normal HorizontalScrollView when catch touch events don't forward them to ScrollView and you only can scroll one way at time. here is solution:
package your.package;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
import android.view.MotionEvent;
import android.content.Context;
import android.util.AttributeSet;
public class WScrollView extends HorizontalScrollView
{
public ScrollView sv;
public WScrollView(Context context)
{
super(context);
}
public WScrollView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public WScrollView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
#Override public boolean onTouchEvent(MotionEvent event)
{
boolean ret = super.onTouchEvent(event);
ret = ret | sv.onTouchEvent(event);
return ret;
}
#Override public boolean onInterceptTouchEvent(MotionEvent event)
{
boolean ret = super.onInterceptTouchEvent(event);
ret = ret | sv.onInterceptTouchEvent(event);
return ret;
}
}
using:
#Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
/*BIDIRECTIONAL SCROLLVIEW*/
ScrollView sv = new ScrollView(this);
WScrollView hsv = new WScrollView(this);
hsv.sv = sv;
/*END OF BIDIRECTIONAL SCROLLVIEW*/
RelativeLayout rl = new RelativeLayout(this);
rl.setBackgroundColor(0xFF0000FF);
sv.addView(rl, new LayoutParams(500, 500));
hsv.addView(sv, new LayoutParams(WRAP_CONTENT, MATCH_PARENT /*or FILL_PARENT if API < 8*/));
setContentView(hsv);
}
Two years further down the line I think the open source community might have to your rescue:
2D Scroll View.
Edit: The Link doesn't work anymore but here is a link to an old version of the blogpost;
I searched really long to make this work and finally found this thread here. wasikuss' answer came quite close to the solution, but still it did not work properly. Here is how it works very well (at least for me (Android 2.3.7)). I hope, it works on any other Android version as well.
Create a class called VScrollView:
package your.package.name;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
public class VScrollView extends ScrollView {
public HorizontalScrollView sv;
public VScrollView(Context context) {
super(context);
}
public VScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
sv.dispatchTouchEvent(event);
return true;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
super.onInterceptTouchEvent(event);
sv.onInterceptTouchEvent(event);
return true;
}
}
Your layout should look like:
<your.package.name.VScrollView
android:id="#+id/scrollVertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<HorizontalScrollView
android:id="#+id/scrollHorizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<TableLayout
android:id="#+id/table"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:stretchColumns="*" >
</TableLayout>
</HorizontalScrollView>
</your.package.name.VScrollView>
In your activity, you should do something like:
hScroll = (HorizontalScrollView) findViewById(R.id.scrollHorizontal);
vScroll = (VScrollView) findViewById(R.id.scrollVertical);
vScroll.sv = hScroll;
... and that's how it works. At least for me.
There is an easy workaround:
In you activity get a reference to the outer scrollView (I'm going to assume a vertical scrollview) and a reference to the first child of that scroll view.
Scrollview scrollY = (ScrollView)findViewById(R.id.scrollY);
LinearLayout scrollYChild = (LinearLayout)findViewById(R.id.scrollYChild);
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
scrollYChild.dispatchTouchEvent(event);
scrollY.onTouchEvent(event);
return true;
}
One could argue that this solution is a bit hacky. But it has worked great for me in several applications!
Late to answer, but hopefully might be helpful to someone.
You can check out droid-uiscrollview. This is heavily based on #MrCeeJ's answer, but I seemed to have a lot of trouble getting the actual content to be rendered. Hence I pulled in the latest source from HorizontalScrollView & ScrollView to create droid-uiscrollview. There are a few todo's left which I haven't gotten around to finish, but it does suffice to get content to scroll both horizontally & vertically at the same time
I've try both wasikuss and user1684030 solutions and I had to adapt them because of one warning log: HorizontalScrollView: Invalid pointerId=-1 in onTouchEvent, and because I wasn't fan of this need of creating 2 scroll views.
So here is my class:
public class ScrollView2D extends ScrollView {
private HorizontalScrollView innerScrollView;
public ScrollView2D(Context context) {
super(context);
addInnerScrollView(context);
}
public ScrollView2D(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() == 1) {
View subView = getChildAt(0);
removeViewAt(0);
addInnerScrollView(getContext());
this.innerScrollView.addView(subView);
} else {
addInnerScrollView(getContext());
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
boolean handled = super.onTouchEvent(event);
handled |= this.innerScrollView.dispatchTouchEvent(event);
return handled;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
super.onInterceptTouchEvent(event);
return true;
}
public void setContent(View content) {
if (content != null) {
this.innerScrollView.addView(content);
}
}
private void addInnerScrollView(Context context) {
this.innerScrollView = new HorizontalScrollView(context);
this.innerScrollView.setHorizontalScrollBarEnabled(false);
addView(this.innerScrollView);
}
}
And when using it in XML, you have nothing to do if the content of this scroll view is set in here. Otherwise, you just need to call the method setContent(View content) in order to let this ScrollView2D knows what is its content.
For instance:
// Get or create a ScrollView2D.
ScrollView2D scrollView2D = new ScrollView2D(getContext());
scrollView2D.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
addView(scrollView2D);
// Set the content of scrollView2D.
RelativeLayout testView = new RelativeLayout(getContext());
testView.setBackgroundColor(0xff0000ff);
testView.setLayoutParams(new ViewGroup.LayoutParams(2000, 2000));
scrollView2D.setContent(testView);
For a while I've been trying solutions from here, but the one that worked best still had one problem: It ate all events, none were making it through to elements within the scroller.
So I've got ... yet another answer, in Github and well-commented at least hopefully: https://github.com/Wilm0r/giggity/blob/master/app/src/main/java/net/gaast/giggity/NestedScroller.java
Like all solutions, it's a nested HorizontalScrollview (outer) + ScrollView (inner), with the outer receiving touch events from Android, and the inner receiving them only internally from the outer view.
Yet I'm relying on the ScrollViews to decide whether a touch event is interesting and until they accept it, do nothing so touches (i.e. taps to open links/etc) can still make it to child elements.
(Also the view supports pinch to zoom which I needed.)
In the outer scroller:
#Override
public boolean onInterceptTouchEvent(MotionEvent event)
{
if (super.onInterceptTouchEvent(event) || vscroll.onInterceptTouchEventInt(event)) {
onTouchEvent(event);
return true;
}
return false;
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
super.onTouchEvent(event);
/* Beware: One ugliness of passing on events like this is that normally a ScrollView will
do transformation of the event coordinates which we're not doing here, mostly because
things work well enough without doing that.
For events that we pass through to the child view, transformation *will* happen (because
we're completely ignoring those and let the (H)ScrollView do the transformation for us).
*/
vscroll.onTouchEventInt(event);
return true;
}
vscroll here is the "InnerScroller", subclassed from ScrollView, with a few changes to event handling: I've done some terrible things to ensure incoming touch events directly from Android are discarded, and instead it will only take them from the outer class - and only then pass those on to the superclass:
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
/* All touch events should come in via the outer horizontal scroller (using the Int
functions below). If Android tries to send them here directly, reject. */
return false;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
/* It will still try to send them anyway if it can't find any interested child elements.
Reject it harder (but pretend that we took it). */
return true;
}
public boolean onInterceptTouchEventInt(MotionEvent event) {
return super.onInterceptTouchEvent(event);
}
public boolean onTouchEventInt(MotionEvent event) {
super.onTouchEvent(event);
}
I know you have accepted your answer but may be this could give you some idea.
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<HorizontalScrollView
android:layout_alignParentBottom="true"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView
android:src="#drawable/device_wall"
android:scaleType="center"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</HorizontalScrollView>
</RelativeLayout>
</LinearLayout>
</ScrollView>

Categories

Resources