5

Before Scrolling: enter image description here

During Scrolling: enter image description here

What I expect during scrolling: enter image description here

I have a problem with HorizontalScrollView. When I choose an element from this View, I set focus to that element by calling:

else if (v.getParent() == candidatesScrollView.getChildAt(0))
{
    Button candidateButton = (Button) v;
    v.requestFocusFromTouch();
    v.setSelected(true);
            (...)
}

After that, when I scroll the list without choosing other element, I lose focus of previously selected element. I made some research about this topic, but there was no solution that could work for me... How can I scroll my HorizontalScrollList without loosing focus from selected element? Any Help is Appreciated. It has been about 14 days since I asked that question and still didn't find solution. Please help.

Here is part of my XML:

<HorizontalScrollView
        android:id="@+id/CandidatesHorizontalScrollView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/linearLayout2"
        android:clickable="false"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:visibility="gone" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clickable="false"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/horizontalscrollview1_button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button"
            android:textSize="25sp" />

        (...)
        // 11 more buttons
    </LinearLayout>

</HorizontalScrollView>

UPDATE

MY CURRENT SOLUTION #1 (not working correctly): After scrolling, and then scrolling again (for example scrolling back), scrolling starts from selected element.

I created custom HorizontalScrollView class inside which I overridden onTouchEvent() method. I don't think this is optimal way of doing that, because in that case I have to do calculations every time I move even one pixel. for example, if I add toast.show() to the below method, it will try to show as many toast as many I moved pixels (If I move by 10 pixels, it will try to show 10 Toast). Anyway, it works for me and the selection and focus are being kept.

Please help me modify this code to make finally a good answer for that known issue:

@Override
public boolean onTouchEvent(MotionEvent ev)
{
    int i = 0;
    Button button = null;
    for (; i < 11; i++)
    {
        button = (Button)((LinearLayout)getChildAt(0)).getChildAt(i);
        if(button.isSelected())
            break;
    }
    super.onTouchEvent(ev);
    button.setSelected(true);
    button.requestFocusFromTouch();

    return true;
}

To be sure that the above code will work, you need to have only one selected item in your HorizontalScrollView at a time, i.e when you press diferent button, you need to make the previous one setSelected(false)

MY CURRENT SOLUTION #2 (not working correctly):

Solution #2 that I tried to implement, thinking that first one is not elegant enough, involves usage of gesture detector. In my custom HorizontalListView class I have added the following code:

Constructor:

public MyHorizontalScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.context = context;
    gestureDetector = new GestureDetector(context, new MyHorizontalScrollViewGestureDetector());
    this.setOnTouchListener(new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            gestureDetector.onTouchEvent(event);

            return true;
        }
    });
    // TODO Auto-generated constructor stub
}

MyHorizontalScrollViewGestureDetector internal class:

public class MyHorizontalScrollViewGestureDetector extends SimpleOnGestureListener
    {

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
        {
            //Here code similar like that one in solution #1
            //But the View is not scrolling, even without that code 
            super.onScroll(e1, e2, distanceX, distanceY);

            return true; 
        }
    }   

However, the list is not scrolling with that solution. I can add to onScroll method:

ScrollBy((int)positionX, (int)positionY);

which makes the list will scroll, but not in a good way ad it will freeze sometimes. I am wondering why scrolling is not called by the super. method.

MY CURRENT SOLUTION #3 (working, but it is walk-around):

Because both solution 1 and 2 were not working, I decided to not play with focus anymore. What I do now, is to change the Button Drawable whenever I click it and every time when I change to different Button. I use same Drawable as is used for focused button (Holo). In that case, I don't have to be worried about scrolling in HorizontalScrollView. This solution is some kind of walk-around, so I am looking forward to any comments, suggestions and edits.

Marek
  • 3,935
  • 10
  • 46
  • 70
  • 1
    +1 There are duplicate of this question, but none of them have answers. – Alex Gittemeier Jul 01 '13 at 13:22
  • Even when I set my ScrollView as not focusable, its elements will lose focus onScroll... – Marek Jul 01 '13 at 23:49
  • 1
    I had the same problem, but I managed with ToggleButton – Jibin Scaria Jul 02 '13 at 05:08
  • You mean you just changed all your Buttons to ToggleButtons? And it works? Anyway lets see if someone here will know how to do it with a button (and probably all other Views) – Marek Jul 02 '13 at 05:14
  • @Jibin Scaria can you post your solution? It is not working for me neither with ToggleButtons - they lose selection same way as Buttons do. – Marek Jul 02 '13 at 05:25
  • using ToggleButton is not a proper solution but it serves the purpose. What I did was, make the background drawable to same as that of Buttons, So when you click on Toggle button it become toggled (the background changes to selected) and it will remain selected(toggled) even if you scroll the view until another click or as your solution1, toggle the current button to changes the background to normal (when user clicks another button) – Jibin Scaria Jul 02 '13 at 05:52
  • Ah this is nice workaround but it is not what I am looking for. I think my Solution 1 is the proper way to do that but still not sure. Also, I would like someone to fix Solution 2. – Marek Jul 02 '13 at 06:08
  • Also I just found out that is not about selection, but about focus. – Marek Jul 02 '13 at 06:18

4 Answers4

1

Can the following solution be applicable (I took the idea from here):

You may want to try creating your own custom class that extends HorizontalScrollView and overriding the onScrollChanged() function as such:

public class TestHorizontalScrollView extends HorizontalScrollView {

    public TestHorizontalScrollView(Context context) {
        super(context);
    }


    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        // TODO Auto-generated method stub
        Log.i("Scrolling", "X from ["+oldl+"] to ["+l+"]");
        Button button = null;
        for (; i < 11; i++)
        {
          button = (Button)((LinearLayout)getChildAt(0)).getChildAt(i);
          if(button.isSelected())
            break;
        }
        button.setSelected(true);
        button.requestFocusFromTouch();

        super.onScrollChanged(l, t, oldl, oldt);
    }

}

Now that you have detected the scroll (we're speaking about one of the HorizontalScrollView) you can set again the selected status of the corresponding button. Can you please try this solution out and see if it works. I'm very interested in the resolution of this one, as the question is quite interesting also.

Anyway, I managed to find the following article. They state there that:

Imagine a simple application, ApiDemos for example, that shows a list of text items. The user can freely navigate through the list using the trackball and they can also scroll and fling the list using their finger. The issue in this scenario is the selection. If I select an item at the top of the list and then fling the list towards the bottom, what should happen to the selection? Should it remain on the item and scroll off the screen? In this case, what would happen if I then decide to move the selection with the trackball? Or worse, if I press the trackball to act upon the currently selected item, which is not shown on screen anymore. After careful considerations, we decided to remove the selection altogether.

In touch mode, there is no focus and no selection. Any selected item in a list of in a grid becomes unselected as soon as the user enters touch mode. Similarly, any focused widgets become unfocused when the user enters touch mode. The image below illustrates what happens when the user touches a list after selecting an item with the trackball.

Anyway, the statements there didn't make me happy and I went further to find this:

android:focusable="true"
android:focusableInTouchMode="true"

Also set android:clickable="true" (you might as well set android:focusable="true" of the LinearLayout).

Try adding those to the buttons and to the Layout that contains them. Try everything.

I'l try backing you up as much as I can.

Cheers

Community
  • 1
  • 1
g00dy
  • 6,752
  • 2
  • 30
  • 43
  • I think I shouldn't change clickable and focusable parameteres of Layout containing buttons. Also solution that you posted is same as my solution #1 (you should also be aware that you should call super.onTouchEvent(ev) BEFORE setting focus to the Button. As far as now, solution #3 is the most appropriate one I think. There is no need to play with the focus. – Marek Jul 04 '13 at 00:00
  • "I think I shouldn't change clickable and focusable parameteres of Layout containing buttons." - why? Any did you consider putting those parameters on the buttons themselves? – g00dy Jul 04 '13 at 06:53
  • Because it is not Layout that I want to be clickable or focusable, it's Buttons. Anyway the get focus in the right way, they just lose it when I scroll, which maybe is normal behavior. Thats why I decided to use background Drawable instead. – Marek Jul 04 '13 at 07:30
  • Yes, that's a solution, but one more thing, did you try `android:focusableInTouchMode="true"` on the buttons to see if it fixes it? If not, then the background Drawable will be the only solution. – g00dy Jul 04 '13 at 07:33
  • Indeed I tried it. I think it just wasn't the proper way to do that using focus idea. – Marek Jul 04 '13 at 07:54
0

I remember having a problem with scroll views that I think was similar to the problem you're having.

The solution I came up with was to override the onRequestFocusInDescendants method in the HorizontalScrollView class. This requires you creating your own class extended from HorizontalScrollView if you aren't already doing so.

In my case, I always returned true from the method. This tells the caller that you have taken care of the focus change so it shouldn't try to do anything further.

@Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
  return true;
}

Depending on your requirements, you may find it necessary to return true only under certain conditions (say when a scroll is in progress), otherwise forward the call to the superclass.

James Holderness
  • 22,721
  • 2
  • 40
  • 52
0

This question has no answer except for custom way of doing it, as you might have already done like changing the drawables. Reason is when you slide, the focus goes to the HorizontalScroll, and you can only focus on one item, it makes no sense to focus more than one view. So either implement a drawable (which you seem to have done) or extend a checkbox and override the functionality so when it is focused it is checked and changes the looks.

LuckyMe
  • 3,820
  • 2
  • 27
  • 35
  • But why when I set HorizontalScrollView focusable:false, selectable:false, it still gets focus? – Marek Jul 09 '13 at 00:51
  • You can receive focus by multiple methods, like tabbing to it, arrow keys, just because you set `focusable(false)` doesn't mean it will never get focus. When you are scrolling, it implies that view is getting focus, otherwise, you can't scroll.. – LuckyMe Jul 09 '13 at 01:07
  • You are miss-understanding the concept of focus, there is also `android:focusableInTouchMode` but that will not do what you either, read this: http://developer.android.com/reference/android/view/View.html#FocusHandling – LuckyMe Jul 09 '13 at 01:26
  • @Marek Hello?! are you still there? – LuckyMe Jul 10 '13 at 08:46
  • Yes. I know about both things, but as you said it wouldn't do that either... Best thing - drawable :) – Marek Jul 11 '13 at 00:01
  • @Marek Then you should approve one of the answers, don't leave the question hanging.. – LuckyMe Jul 11 '13 at 00:15
0

I have found a solution to my problem. You can see the updated question for details. Basically, instead of using focus I decided to use Drawable selector, which is much more easier.

Marek
  • 3,935
  • 10
  • 46
  • 70
  • 1
    Usually SO, you would put the solution inside your answer instead of updating your question with the solutions. Please edit your question and move the solution to this answer. :) – ForceMagic Mar 06 '14 at 19:49