Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

onCreateView() being called too often in custom preference

Tags:

android

I have created a Custom preference which has the following constructor

public CoordinatesPreference(Context context, AttributeSet attrs)
{
    super(context, attrs);
    setLayoutResource(R.layout.coordinates_preference);
}

And I have Overriden onCreateView() so it writes to the log like this:

@Override
protected View onCreateView(ViewGroup parent)
{
    Log.d("test", "Creating Preference view");
    return super.onCreateView(parent);
}

and my log is full of "Creating Preference view" messages, this creates a laggy feel to scrolling and I believe convert view is supposed to solve this, I had a look at the preference source code and if convert view is null then onCreateView() is called.

for testing purposes I added this method:

@Override
public View getView(View convertView, ViewGroup parent)
{
    if (convertView == null)
    {
        return super.getView(convertView, parent);
    }
    return super.getView(convertView, parent);
}

and set a break point. I have found that almost always my convert view is null. and therefore it must create a new view, why is this? and how can I improve this to avoid a laggy preference screen?

EDIT: Changed the way the onCreate is called, now its all android I just use setLayoutResource. but this does not solve the problem...

EDIT2: I have used Debug.StartMethodTracing() and have found as I suspected that 55% of the time spend (when I'm just scrolling up and down) is spend on the Inflation of the preference from the method onCreateView() which is called from getView() when convertView is null.

Thanks, Jason

like image 219
Jason Avatar asked Nov 19 '10 15:11

Jason


2 Answers

I don't know what have you implemented in this custom preference, but maybe the super class doesn't know how create a proper view to your preference?

From the documentation:

protected View onCreateView (ViewGroup parent)

Since: API Level 1 Creates the View to be shown for this Preference in the PreferenceActivity. The default behavior is to inflate the main layout of this Preference (see setLayoutResource(int). If changing this behavior, please specify a ViewGroup with ID widget_frame. Make sure to call through to the superclass's implementation.

http://developer.android.com/reference/android/preference/Preference.html

I guess you have set that id on the horizontal layout.

Now that I'm talking about it, why don't you include this horizontal layout in the layout that you are inflating?

like image 159
Pedro Loureiro Avatar answered Oct 01 '22 11:10

Pedro Loureiro


I am not sure if the code you are using is an accurate test. I have a custom preference and I only override 5 methods and three of them are constructors.

public ImageButtonPreference(Context context)
{
    this(context, null);
}

public ImageButtonPreference(Context context, AttributeSet attrs)
{
    this(context, attrs, 0);
}

public ImageButtonPreference(Context context, AttributeSet attrs, int defStyle)
{
    super(context, attrs, defStyle);
    mInflater = LayoutInflater.from(context);

    // This is where I pull all of the styleable info from the attrs
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ImageButtonPreference, defStyle, 0);
    for(int i = a.getIndexCount(); i >= 0; i--)
    {
        int attr = a.getIndex(i); 
        switch (attr)
        {
        case R.styleable.ImageButtonPreference_logo:
            mImageResource = a.getResourceId(attr, mImageResource);
            break;
        case R.styleable.ImageButtonPreference_button_text:
            mButtonText = a.getString(attr);
            break;
        }
    }
}

@Override
protected View onCreateView(ViewGroup parent)
{
    View view = mInflater.inflate(R.layout.image_button_preference, parent, false);
    return view;
}

@Override
protected void onBindView(View view)
{
    super.onBindView(view);

    ImageView image = (ImageView)view.findViewById(R.id.Logo);
    if(image != null && mImageResource != 0) image.setImageResource(mImageResource);

    Button button = (Button)view.findViewById(R.id.ConnectButton);
    if(button != null)
    {
        button.setText(mButtonText);
        button.setOnClickListener(mButtonListener);
    }
}

I have pulled this code, almost verbatim from the Android Source, so it should be just as fast as any other preference.

like image 30
CaseyB Avatar answered Oct 01 '22 11:10

CaseyB