Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scrolling in Horizontal ScrollView loses focus of focused element,

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.

like image 444
Marek Avatar asked Nov 12 '22 03:11

Marek


1 Answers

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

like image 86
g00dy Avatar answered Nov 15 '22 09:11

g00dy