Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: Enable Scrollbars in Custom View

I have implemented a custom Layout that extends RelativeLayout. It displays lots of different elements that are created at run-time and is scrollable in both dimensions using scrollTo() and scrollBy(). Scrolling works and now I'd like to add the standard Android scrollbars.

Using Scrollviews ist not possible, since I need the Layout to be scrollable in 2 dimensions, so I tried to do it as described here: Android: Enable Scrollbars on Canvas-Based View

I have implemented all the compute* methods with some (bogus) values and enabled the scrollbars. But I still can't get them to show up. Any ideas what might be the problem?

There are tons of questions like this in various mailing lists and on SO, but everywhere the answer seems to be "1. call setHorizontalScrollbarEnabled(true), 2. implement all the compute* methods, 3. call awakenScrollbars()". As far as I can tell, I have done all of that and even tried to use initializeScrollbars() but nothing happens and the docs don't offer any help.

public NodeLayout(Context context) {
    super(context);

    setVerticalScrollBarEnabled(true);
    setHorizontalScrollBarEnabled(true);

    TypedArray a = context.obtainStyledAttributes(R.styleable.View);
    initializeScrollbars(a);
    a.recycle();
}

@Override
protected int computeHorizontalScrollExtent() {
    return 5;
}

@Override
protected int computeHorizontalScrollOffset() {
    return 10;
}

@Override
protected int computeHorizontalScrollRange() {
    return 50;
}

@Override
protected int computeVerticalScrollExtent() {
    return getHeight() / 2;
}

@Override
protected int computeVerticalScrollOffset() {
    return getHeight() / 2;
}

@Override
protected int computeVerticalScrollRange() {
    return getHeight();
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    awakenScrollBars();
    invalidate();
    return true;
}

And this is how my attrs.xml file looks like:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="View">
<attr name="android:background"/>
<attr name="android:clickable"/>
<attr name="android:contentDescription"/>
<attr name="android:drawingCacheQuality"/>
<attr name="android:duplicateParentState"/>
<attr name="android:fadeScrollbars"/>
<attr name="android:fadingEdge"/>
<attr name="android:fadingEdgeLength"/>
<attr name="android:fitsSystemWindows"/>
<attr name="android:focusable"/>
<attr name="android:focusableInTouchMode"/>
<attr name="android:hapticFeedbackEnabled"/>
<attr name="android:id"/>
<attr name="android:isScrollContainer"/>
<attr name="android:keepScreenOn"/>
<attr name="android:longClickable"/>
<attr name="android:minHeight"/>
<attr name="android:minWidth"/>
<attr name="android:nextFocusDown"/>
<attr name="android:nextFocusLeft"/>
<attr name="android:nextFocusRight"/>
<attr name="android:nextFocusUp"/>
<attr name="android:onClick"/>
<attr name="android:padding"/>
<attr name="android:paddingBottom"/>
<attr name="android:paddingLeft"/>
<attr name="android:paddingRight"/>
<attr name="android:paddingTop"/>
<attr name="android:saveEnabled"/>
<attr name="android:scrollX"/>
<attr name="android:scrollY"/>
<attr name="android:scrollbarAlwaysDrawHorizontalTrack"/>
<attr name="android:scrollbarAlwaysDrawVerticalTrack"/>
<attr name="android:scrollbarDefaultDelayBeforeFade"/>
<attr name="android:scrollbarFadeDuration"/>
<attr name="android:scrollbarSize"/>
<attr name="android:scrollbarStyle"/>
<attr name="android:scrollbarThumbHorizontal"/>
<attr name="android:scrollbarThumbVertical"/>
<attr name="android:scrollbarTrackHorizontal"/>
<attr name="android:scrollbarTrackVertical"/>
<attr name="android:scrollbars"/>
<attr name="android:soundEffectsEnabled"/>
<attr name="android:tag"/>
<attr name="android:visibility"/>
</declare-styleable>
</resources>

I am developing for Android 3.0.

like image 560
Frederic Avatar asked Sep 05 '11 20:09

Frederic


People also ask

Which layout is used to add scrollbars to a view?

In order to place multiple views in the scroll view, one needs to make a view group(like LinearLayout) as a direct child and then we can define many views inside it. A ScrollView supports Vertical scrolling only, so in order to create a horizontally scrollable view, HorizontalScrollView is used.


2 Answers

Just had this same problem and finally figured it out.

The "problem" is that you are extending RelativeLayout, which is a subclass of ViewGroup. I found that I had no problem doing what you described in your question to get a custom View to display scrollbars, but when I extended ViewGroup or one of its subclasses, no scrollbars appeared.

I finally remembered that ViewGroups, by default, don't draw themselves. I added one line to my custom ViewGroup constructor: setWillNotDraw(false);

What do you know, the scrollbars appeared!

Hope that helps.

like image 199
ashughes Avatar answered Oct 20 '22 11:10

ashughes


Fyi, on Android 3.2/Google TV, and Android 3.2.1/Tablet, the <declare-styleable/> part of the solution is not optional when extending ViewGroup. The problem seems to be that none of the scrollbar drawables and sizes used by View to draw the scrollbars are loaded unless all of the corresponding scrollbar properties in the <declare-styleable/> are set. Failing to set some significant number of those properties results in a null pointer exception during layout on 3.2. I also suspect that the GridView/ListView/AbsListView/AdapterView series of classes don't set the horizontal scrollbar attributes, if that's what you happen to be chasing.

The general steps seem to be:

  • Use a <declare-styleable/> (as described above).
  • Call initScrollbars early in your constructor (as described above).
  • Call setHorizontalScrollbarEnabled/setrVerticalScrollBarEnbled after calling initScrollbars.
  • Implement the set of three computeHorizontalScrollXXX, and computeVerticalScrollXXX as appropriate.
  • make frequent calls to awakenScrollBars() as appropriate to get the scrollbars to display.
  • Most probably, a call to setWillNotDraw(false) has to be made in the constructor. Scrollbar drawing is implemented in View; but I do in fact call this method myself.
  • consider a call to setScrollBarStyle to control placement of the scrollbars. Most useful custom views that scroll will end up clipping to the padding margins, which will screw up View's drawing of the scrollbars if they are placed outside the padding rectangle, since We don't have access to the same hidden methods in View that stock android views do.

I think that's it. Chances of all that working on Android 6.0... slim to none. :-/

like image 27
Robin Davies Avatar answered Oct 20 '22 10:10

Robin Davies