Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to customize preference-header layout?

I have found similar questions, but can't find a specific answer that is up-to-date. I'm using <preference-header>, as per 3.0+ settings design guidelines ( I target 4.1.2+) to build my headers; I want to set a custom layout to these headers. Note that I don't want to fall back to the old PreferenceScreen method as described here, because I don't support older Android version.

As far as I could research, this layout is held by a private member of the PreferenceActivity class, and it's retrieved with a styleable attribute that doesn't seem publicly accessible:

private int mPreferenceHeaderItemResId = 0;
...
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
TypedArray sa = obtainStyledAttributes(null,
            com.android.internal.R.styleable.PreferenceActivity,
            com.android.internal.R.attr.preferenceActivityStyle,
            0);
...
mPreferenceHeaderItemResId = sa.getResourceId(
    com.android.internal.R.styleable.PreferenceActivity_headerLayout,
            com.android.internal.R.layout.preference_header_item);
...
}

This resource is then passed to a private Adapter to populate the header ListView.

Is there a way to pass a different layout resource?

like image 863
devrocca Avatar asked Jan 13 '16 15:01

devrocca


1 Answers

UPDATE 14.4.2016: there was problem with recreate by savedInstanceState, but I found another similar solution from which I used setListAdapter method code (I modified the code below).


I solve this problem just too. I don't know if the following solution is correct, but was the fastest. Because PreferenceActivity is child of ListActivity, you can override setListAdapter method for using own adapter for header items. This is ugly hack, because setListAdapter method is called in PreferenceActivity.onCreate() with sets adapter parameter to new instance of HeaderAdapter, so following adjustment ignore this instance.

@Override
public void setListAdapter(ListAdapter adapter) {
    int i, count;

    if (mHeaders == null) {
        mHeaders = new ArrayList<>();
        // When the saved state provides the list of headers, onBuildHeaders is not called
        // so we build it from the adapter given, then use our own adapter

        count = adapter.getCount();
        for (i = 0; i < count; ++i) {
            mHeaders.add((Header) adapter.getItem(i));
        }
    }

    super.setListAdapter(new CustomHeaderAdapter(this, mHeaders, R.layout.preference_header_item, true));
}

mHeaders property is defined as class member

private List<Header> mHeaders;

and is assigned in onBuildHeaders:

@Override
public void onBuildHeaders(List<Header> target) {
    mHeaders = target;
    loadHeadersFromResource(R.xml.preference_headers, target);
    ...
}

I copied and modified adapter inner class and layout from SDK source:

private static class CustomHeaderAdapter extends ArrayAdapter<Header> {
    private static class HeaderViewHolder {
        ImageView icon;
        TextView title;
        TextView summary;
    }

    private LayoutInflater mInflater;
    private int mLayoutResId;
    private boolean mRemoveIconIfEmpty;

    public CustomHeaderAdapter(Context context, List<Header> objects, int layoutResId,
                         boolean removeIconBehavior) {
        super(context, 0, objects);
        mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mLayoutResId = layoutResId;
        mRemoveIconIfEmpty = removeIconBehavior;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        HeaderViewHolder holder;
        View view;

        if (convertView == null) {
            view = mInflater.inflate(mLayoutResId, parent, false);
            holder = new HeaderViewHolder();
            holder.icon = (ImageView) view.findViewById(R.id.icon);
            holder.title = (TextView) view.findViewById(R.id.title);
            holder.summary = (TextView) view.findViewById(R.id.summary);
            view.setTag(holder);
        } else {
            view = convertView;
            holder = (HeaderViewHolder) view.getTag();
        }

        // All view fields must be updated every time, because the view may be recycled
        Header header = getItem(position);
        if (mRemoveIconIfEmpty) {
            if (header.iconRes == 0) {
                holder.icon.setVisibility(View.GONE);
            } else {
                holder.icon.setVisibility(View.VISIBLE);
                holder.icon.setImageResource(header.iconRes);
            }
        } else {
            holder.icon.setImageResource(header.iconRes);
        }
        holder.title.setText(header.getTitle(getContext().getResources()));
        CharSequence summary = header.getSummary(getContext().getResources());
        if (!TextUtils.isEmpty(summary)) {
            holder.summary.setVisibility(View.VISIBLE);
            holder.summary.setText(summary);
        } else {
            holder.summary.setVisibility(View.GONE);
        }

        return view;
    }
}

preference_header_item.xml with modified minHeight:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="64dp"
    android:background="?android:attr/activatedBackgroundIndicator"
    android:gravity="center_vertical"
    android:paddingRight="?android:attr/scrollbarSize"
    android:paddingEnd="?android:attr/scrollbarSize">

    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="6dip"
        android:layout_marginEnd="6dip"
        android:layout_gravity="center" />

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="2dip"
        android:layout_marginStart="2dip"
        android:layout_marginRight="6dip"
        android:layout_marginEnd="6dip"
        android:layout_marginTop="6dip"
        android:layout_marginBottom="6dip"
        android:layout_weight="1">

        <TextView android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:ellipsize="marquee"
            android:fadingEdge="horizontal" />

        <TextView android:id="@+id/summary"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@android:id/title"
            android:layout_alignLeft="@android:id/title"
            android:layout_alignStart="@android:id/title"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:ellipsize="end"
            android:maxLines="2" />

    </RelativeLayout>

</LinearLayout>
like image 121
Petr Daňa Avatar answered Sep 28 '22 12:09

Petr Daňa