9

Background

I'm using this library which for one of its classes (that extends from ViewGroup), in "PLA_AbsListView.java", inside the CTOR, there are those lines:

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

Recently, I've updated the SDK & ADT of Android to support the new Android version (Lollipop - API21) .

The problem

Ever since I've updated everything, I keep getting this error:

The method initializeScrollbars(TypedArray) is undefined for the type PLA_AbsListView

What I've tried

I've tried to set the API to be used as lower than 21, but it didn't help.

I've also tried to find out where this function is declared. It's supposed to be a protected function within "View.java", but for some reason, I can't see it in the documentations

The question

How could it be?

How can I fix it?

Is it possible it's a bug in the documentation?

It worked before, when targeting Kitkat...

Mogsdad
  • 44,709
  • 21
  • 151
  • 275
android developer
  • 114,585
  • 152
  • 739
  • 1,270

2 Answers2

9

As @biegleux mentioned in his answer, initializeScrollbars() is now annotated with @removed in the API 21 source code. Here is the method source from API 21:

protected void initializeScrollbars(TypedArray a) {
    // It's not safe to use this method from apps. The parameter 'a' must have been obtained 
    // using the View filter array which is not available to the SDK. As such, internal 
    // framework usage now uses initializeScrollbarsInternal and we grab a default 
    // TypedArray with the right filter instead here. 
    TypedArray arr = mContext.obtainStyledAttributes(com.android.internal.R.styleable.View);

    initializeScrollbarsInternal(arr);

    // We ignored the method parameter. Recycle the one we actually did use. 
    arr.recycle();
}

Based on the comment in the method, it sounds like the issue before API 21 was that it was not safe to pass in a TypedArray, but now it no longer uses the passed in TypedArray. So it seems like this should be annotated with @Deprecated instead of @removed and there should be a new version of this method that takes no parameter that can be called when we need to initialize the scrollbars from a custom view that's created programmatically.

Until this gets fixed, there's two ways you can work around the issue:

1) Inflate your custom view from xml with the android:scrollbars attribute set. This is the safest method and should work with all past and future platform versions. For example:

Create an xml layout file (my_custom_view.xml):

<com.example.MyCustomView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="horizontal|vertical"/>

Inflate your custom view:

MyCustomView view = (MyCustomView) LayoutInflater.from(context).inflate(R.layout.my_custom_view, container, false);

2) Use reflection to invoke initializeScrollbars() in your custom view's constructor. This could fail in future API versions if the method initializeScrollbars() is actually removed or renamed. For example:

In your custom view (e.g. MyCustomView.java):

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

    // Need to manually call initializedScrollbars() if instantiating view programmatically
    final TypedArray a = context.getTheme().obtainStyledAttributes(new int[0]);
    try {
        // initializeScrollbars(TypedArray)
        Method initializeScrollbars = android.view.View.class.getDeclaredMethod("initializeScrollbars", TypedArray.class);
        initializeScrollbars.invoke(this, a);
    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
        e.printStackTrace();
    }
    a.recycle();
}
ashughes
  • 7,155
  • 9
  • 48
  • 54
7

from View.java of android-21 sources:

/**
 * ...
 *
 * @removed
 */
protected void initializeScrollbars(TypedArray a) {
    // It's not safe to use this method from apps. The parameter 'a' must have been obtained
    // using the View filter array which is not available to the SDK. As such, internal
    // framework usage now uses initializeScrollbarsInternal and we grab a default
    // TypedArray with the right filter instead here.
    TypedArray arr = mContext.obtainStyledAttributes(com.android.internal.R.styleable.View);

    initializeScrollbarsInternal(arr);

    // We ignored the method parameter. Recycle the one we actually did use.
    arr.recycle();
}

/**
 * ...
 *
 * @hide
 */
protected void initializeScrollbarsInternal(TypedArray a) {

You don't see it because the method is annotated with @removed. initializeScrollbarsInternal() also cannot be used as it is annotated with @hide. As from the comment it's not safe to use this method, you should definitely report it to the author of the lib.

biegleux
  • 13,179
  • 11
  • 45
  • 52
  • How could such a thing be removed? how would it work well in previous versions if the call would be removed? would you use reflection for previous versions because of it? How would previous versions of the library be able to work on the same API, if it's not a safe thing to call to? And if it's not safe, why not just remove the code inside? I just don't get it... – android developer Oct 19 '14 at 09:29
  • Problem is that the lib uses `R.styleable.View` when it is obtaining `TypedArray` holding the attribute values. Instead it should use `com.android.internal.R.styleable.View`, but it is internal and can't be accessed from the SDK. What is the problem I can just guess, maybe it causes some leak or inproper styling. – biegleux Oct 19 '14 at 09:45
  • I suggest you to remove `initializeScrollbars()` from the lib and let it on the `View` class by defining `R.styleable.View_scrollbars` attr which is not internal. See source code of `View.java` – biegleux Oct 19 '14 at 09:53
  • I've removed it, but I hope it doesn't cause any problem on both old and new versions of Android. You mean it's already being initialized anyway via the CTOR? If so, is it also true for previous versions of Android? I wonder why it was added then... – android developer Oct 19 '14 at 10:28
  • 1
    No, it is not initialized via ctor, but if you are able to use one of the other two ctors than it should be fine, if you set `scrollbars` to `vertical`. Or you can check https://github.com/etsy/AndroidStaggeredGrid if it can help you, it directly inherits from `AbsListView` and doesn't create it's own implementation of it. – biegleux Oct 19 '14 at 11:13
  • Regarding compatibility, it's @removed which means the method is still there for apps that have already been compiled against the older API, but you're no longer allowed to compile against it for new apps. – alanv Oct 19 '14 at 23:33
  • @alanv So what should be done if I wish to use such a library, and also compile it to latest API ? – android developer Oct 20 '14 at 06:42
  • @biegleux I see. I do it anyway, and according to the code, I don't see any problem in doing so, so I think it's a good solution for now. Wonder though what's the best solution. – android developer Oct 20 '14 at 06:43
  • The only options are to not use the library, fix the library, or ask the developer to fix it. – alanv Oct 20 '14 at 06:56
  • Or, alternatively, use a pre-compiled version of the library. But the method call that's being made isn't safe and could lead to strange errors, so i wouldn't recommend that. – alanv Oct 20 '14 at 06:57
  • @alanv Yes, I see. I don't use the CTOR that has this issue, so it should be fine. About the problem, Google has answered me and it seems you are correct: "The API has been removed because R.styleable is not public or stable. There is no safe way to pass a TypedArray into that method. Don't call the method." . – android developer Oct 20 '14 at 20:23
  • There's a bug placed here: https://code.google.com/p/android/issues/detail?id=77745 They say to not use the method but haven't posted any alternative yet. The workaround I've found meanwhile is to use android:scrollbars="horizontal|vertical" if you reference your view in an xml layout (of course the problem persists if creating the view from Java code) – Daniel López Lacalle Nov 07 '14 at 13:19