使用ListView的自定义适配器

时间:2015-02-08 17:21:46

标签: android android-layout android-listview android-adapter

我正在尝试使用我在Github上找到的自定义ListView库来固定节的标题。该库工作正常,但我现在正在尝试实现自己的自定义Adapter,以便固定标题与其他列表项具有不同的布局。我收到一个错误:

java.lang.UnsupportedOperationException: addView(View, LayoutParams) is not supported in AdapterView

在自定义ListView中,有对适配器的调用:ListAdapter adapter = getAdapter();但我使用的是BaseAdapter。我无法弄清楚如何在BaseAdapter中使用ListView或如何自定义ListAdapter,以便我可以使用不同的Layouts

的ListView:

public class PinnedHeaderListView extends ListView {

/** Wrapper class for pinned section view and its position in the list. */
static class PinnedSection {
    public View view;
    public int position;
    public long id;
}

// fields used for drawing shadow under a pinned section
private GradientDrawable mShadowDrawable;
private int mSectionsDistanceY;
private int mShadowHeight;

/** Delegating listener, can be null. */
OnScrollListener mDelegateOnScrollListener;

/** Shadow for being recycled, can be null. */
PinnedSection mRecycleSection;

/** shadow instance with a pinned view, can be null. */
PinnedSection mPinnedSection;

/** Pinned view Y-translation. We use it to stick pinned view to the next section. */
int mTranslateY;

/** Scroll listener which does the magic */
private final OnScrollListener mOnScrollListener = new OnScrollListener() {

    @Override public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (mDelegateOnScrollListener != null) {
            mDelegateOnScrollListener.onScrollStateChanged(view, scrollState);
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

        if (mDelegateOnScrollListener != null) {
            mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
        }

        // get expected adapter or fail fast
        ListAdapter adapter = getAdapter();
        if (adapter == null || visibleItemCount == 0) return;

        final boolean isFirstVisibleItemSection =
                isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));

        if (isFirstVisibleItemSection) {
            View sectionView = getChildAt(0);
            if (sectionView.getTop() == getPaddingTop()) { // view sticks to the top, no need for pinned shadow
                destroyPinnedShadow();
            } else { // section doesn't stick to the top, make sure we have a pinned shadow
                ensureShadowForFirstItem(firstVisibleItem, firstVisibleItem, visibleItemCount);
            }

        } else { // section is not at the first visible position
            int sectionPosition = findCurrentSectionPosition(firstVisibleItem);
            if (sectionPosition > -1) { // we have section position
                ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);
            } else { // there is no section for the first visible item, destroy shadow
                destroyPinnedShadow();
            }
        }
    }
};

private Runnable recreatePinnedShadow = new Runnable() {
    @Override
    public void run() {
        recreatePinnedShadow();
    }
};

/** Default change observer. */
private final DataSetObserver mDataSetObserver = new DataSetObserver() {
    @Override public void onChanged() {
        post(recreatePinnedShadow);
    };
    @Override public void onInvalidated() {
        post(recreatePinnedShadow);
    }
};

public PinnedHeaderListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView();
}

public PinnedHeaderListView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    initView();
}

private void initView() {
    setOnScrollListener(mOnScrollListener);
    initShadow(true);
}

public void setShadowVisible(boolean visible) {
    initShadow(visible);
    if (mPinnedSection != null) {
        View v = mPinnedSection.view;
        invalidate(v.getLeft(), v.getTop(), v.getRight(), v.getBottom() + mShadowHeight);
    }
}

public void initShadow(boolean visible) {
    if (visible) {
        if (mShadowDrawable == null) {
            mShadowDrawable = new GradientDrawable(Orientation.TOP_BOTTOM,
                    new int[] { Color.parseColor("#ffa0a0a0"), Color.parseColor("#50a0a0a0"), Color.parseColor("#00a0a0a0")});
            mShadowHeight = (int) (8 * getResources().getDisplayMetrics().density);
        }
    } else {
        if (mShadowDrawable != null) {
            mShadowDrawable = null;
            mShadowHeight = 0;
        }
    }
}

/** Create shadow wrapper with a pinned view for a view at given position */
void createPinnedShadow(int position) {

    // try to recycle shadow
    PinnedSection pinnedShadow = mRecycleSection;
    mRecycleSection = null;

    // create new shadow, if needed
    if (pinnedShadow == null) pinnedShadow = new PinnedSection();
    // request new view using recycled view, if such
    View pinnedView = getAdapter().getView(position, pinnedShadow.view, PinnedHeaderListView.this);

    // read layout parameters
    LayoutParams layoutParams = (LayoutParams) pinnedView.getLayoutParams();
    if (layoutParams == null) { // create default layout params
        layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        pinnedView.setLayoutParams(layoutParams);
    }

    int heightMode = MeasureSpec.getMode(layoutParams.height);
    int heightSize = MeasureSpec.getSize(layoutParams.height);

    if (heightMode == MeasureSpec.UNSPECIFIED) heightMode = MeasureSpec.EXACTLY;

    int maxHeight = getHeight() - getListPaddingTop() - getListPaddingBottom();
    if (heightSize > maxHeight) heightSize = maxHeight;

    // measure & layout
    int ws = MeasureSpec.makeMeasureSpec(getWidth() - getListPaddingLeft() - getListPaddingRight(), MeasureSpec.EXACTLY);
    int hs = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
    pinnedView.measure(ws, hs);
    pinnedView.layout(0, 0, pinnedView.getMeasuredWidth(), pinnedView.getMeasuredHeight());
    mTranslateY = 0;

    // initialize pinned shadow
    pinnedShadow.view = pinnedView;
    pinnedShadow.position = position;
    pinnedShadow.id = getAdapter().getItemId(position);

    // store pinned shadow
    mPinnedSection = pinnedShadow;
}

/** Destroy shadow wrapper for currently pinned view */
void destroyPinnedShadow() {
    if (mPinnedSection != null) {
        // keep shadow for being recycled later
        mRecycleSection = mPinnedSection;
        mPinnedSection = null;
    }
}

/**
 * Makes sure we have a pinned header for the first position.
 */
void ensureShadowForFirstItem(int sectionPosition, int firstVisibleItem, int visibleItemCount) {
    // if the first item is a section, only recreate if getTop() < 0

    View sectionView = getChildAt(0);

    // when scrolling downwards, invalidate header iff sectionView's top exceeds view boundaries
    if (mPinnedSection != null && mPinnedSection.position != sectionPosition
            && sectionView.getTop() <= getPaddingTop()) {
        destroyPinnedShadow();
    }
    // when scrolling upwards, invalidate header as soon as sectionView leaves the building
    else if (mPinnedSection != null && mPinnedSection.position == sectionPosition
            && sectionView.getTop() > getPaddingTop()) {
        destroyPinnedShadow();
    }

    // create header based on the view of the current section position
    if (mPinnedSection == null && sectionView.getTop() <= getPaddingTop()) {
        createPinnedShadow(sectionPosition);
    }
    // create header based on the view of the previous section position
    else if (mPinnedSection == null && sectionView.getTop() > getPaddingTop()) {
        int prevSection = findPreviousVisibleSectionPosition(sectionPosition);
        if (prevSection > -1) {
            createPinnedShadow(prevSection);
        }
    }

    if (mPinnedSection != null && sectionView.getTop() > getPaddingTop()) {
        final int bottom = mPinnedSection.view.getBottom() + getPaddingTop();
        mSectionsDistanceY = sectionView.getTop() - bottom;
        if (mSectionsDistanceY < 0) {
            // next section overlaps pinned shadow, move it up
            mTranslateY = mSectionsDistanceY;
        } else {
            // next section does not overlap with pinned, stick to top
            mTranslateY = 0;
        }
    } else {
        mTranslateY = 0;
        mSectionsDistanceY = Integer.MAX_VALUE;
    }

}

/** Makes sure we have an actual pinned shadow for given position. */
void ensureShadowForPosition(int sectionPosition, int firstVisibleItem, int visibleItemCount) {

    if (mPinnedSection != null && mPinnedSection.position != sectionPosition) {
        // invalidate shadow, if required
        destroyPinnedShadow();
    }

    if (mPinnedSection == null) { // create shadow, if empty
        createPinnedShadow(sectionPosition);
    }

    // align shadow according to next section position, if needed
    int nextPosition = sectionPosition + 1;
    if (nextPosition < getCount()) {
        int nextSectionPosition = findFirstVisibleSectionPosition(nextPosition,
                visibleItemCount - (nextPosition - firstVisibleItem));
        if (nextSectionPosition > -1) {
            View nextSectionView = getChildAt(nextSectionPosition - firstVisibleItem);
            final int bottom = mPinnedSection.view.getBottom() + getPaddingTop();
            mSectionsDistanceY = nextSectionView.getTop() - bottom;
            if (mSectionsDistanceY < 0) {
                // next section overlaps pinned shadow, move it up
                mTranslateY = mSectionsDistanceY;
            } else {
                // next section does not overlap with pinned, stick to top
                mTranslateY = 0;
            }
        } else {
            // no other sections are visible, stick to top
            mTranslateY = 0;
            mSectionsDistanceY = Integer.MAX_VALUE;
        }
    }
}

int findPreviousVisibleSectionPosition(int fromPosition) {
    ListAdapter adapter = getAdapter();
    for (int childIndex = fromPosition - 1; childIndex >= 0; childIndex--) {
        int viewType = adapter.getItemViewType(childIndex);
        if (isItemViewTypePinned(adapter, viewType))
            return childIndex;
    }
    return -1;
}

int findFirstVisibleSectionPosition(int firstVisibleItem, int visibleItemCount) {
    ListAdapter adapter = getAdapter();
    for (int childIndex = 0; childIndex < visibleItemCount; childIndex++) {
        int position = firstVisibleItem + childIndex;
        int viewType = adapter.getItemViewType(position);
        if (isItemViewTypePinned(adapter, viewType)) return position;
    }
    return -1;
}

int findCurrentSectionPosition(int fromPosition) {
    ListAdapter adapter = getAdapter();

    if (adapter instanceof SectionIndexer) {
        // try fast way by asking section indexer
        SectionIndexer indexer = (SectionIndexer) adapter;
        int sectionPosition = indexer.getSectionForPosition(fromPosition);
        int itemPosition = indexer.getPositionForSection(sectionPosition);
        int typeView = adapter.getItemViewType(itemPosition);
        if (isItemViewTypePinned(adapter, typeView)) {
            return itemPosition;
        } // else, no luck
    }

    // try slow way by looking through to the next section item above
    for (int position=fromPosition; position>=0; position--) {
        int viewType = adapter.getItemViewType(position);
        if (isItemViewTypePinned(adapter, viewType)) return position;
    }
    return -1; // no candidate found
}

void recreatePinnedShadow() {
    destroyPinnedShadow();
    ListAdapter adapter = getAdapter();
    if (adapter != null && adapter.getCount() > 0) {
        int firstVisiblePosition = getFirstVisiblePosition();
        int sectionPosition = findCurrentSectionPosition(firstVisiblePosition);
        if (sectionPosition == -1) return; // no views to pin, exit
        ensureShadowForPosition(sectionPosition,
                firstVisiblePosition, getLastVisiblePosition() - firstVisiblePosition);
    }
}

@Override
public void setOnScrollListener(OnScrollListener listener) {
    if (listener == mOnScrollListener) {
        super.setOnScrollListener(listener);
    } else {
        mDelegateOnScrollListener = listener;
    }
}

@Override
public void onRestoreInstanceState(Parcelable state) {
    super.onRestoreInstanceState(state);
    // restore pinned view after configuration change
    post(recreatePinnedShadow);
}

@Override
public void setAdapter(ListAdapter adapter) {
    // unregister observer at old adapter and register on new one
    ListAdapter oldAdapter = getAdapter();
    if (oldAdapter != null) oldAdapter.unregisterDataSetObserver(mDataSetObserver);
    if (adapter != null) adapter.registerDataSetObserver(mDataSetObserver);

    // destroy pinned shadow, if new adapter is not same as old one
    if (oldAdapter != adapter) destroyPinnedShadow();

    super.setAdapter(adapter);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
    if (mPinnedSection != null) {
        int parentWidth = r - l - getPaddingLeft() - getPaddingRight();
        int shadowWidth = mPinnedSection.view.getWidth();
        if (parentWidth != shadowWidth) {
            recreatePinnedShadow();
        }
    }
}

@Override
protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);

    if (mPinnedSection != null) {

        // prepare variables
        int pLeft = getListPaddingLeft();
        int pTop = getListPaddingTop();
        View view = mPinnedSection.view;

        // draw child
        canvas.save();

        int clipHeight = view.getHeight() +
                (mShadowDrawable == null ? 0 : Math.min(mShadowHeight, mSectionsDistanceY));
        canvas.clipRect(pLeft, pTop, pLeft + view.getWidth(), pTop + clipHeight);

        canvas.translate(pLeft, pTop + mTranslateY);
        drawChild(canvas, mPinnedSection.view, getDrawingTime());

        if (mShadowDrawable != null && mSectionsDistanceY > 0) {
            mShadowDrawable.setBounds(mPinnedSection.view.getLeft(),
                    mPinnedSection.view.getBottom(),
                    mPinnedSection.view.getRight(),
                    mPinnedSection.view.getBottom() + mShadowHeight);
            mShadowDrawable.draw(canvas);
        }

        canvas.restore();
    }
}

public static boolean isItemViewTypePinned(ListAdapter adapter, int viewType) {
    if (adapter instanceof HeaderViewListAdapter) {
        adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
    }
    return ((MainActivity.SimpleAdapter) adapter).isItemViewTypePinned(viewType);
}

/**
 * Sets the selected item and positions the selection y pixels from the top edge of the
 * ListView, or bottom edge of the pinned view iff it exists. (If in touch mode, the item will
 * not be selected but it will still be positioned appropriately.)
 *
 * @param position Index (starting at 0) of the data item to be selected.
 * @param y The distance from the top edge of the ListView (plus padding) that the item will be
 *            positioned.
 * @param adjustForHeader If true, will additionally scroll down so first item will be below header
 */
public void setSelectionFromTop(final int position, final int y, boolean adjustForHeader) {
    setSelectionFromTop(position, y);

    if (adjustForHeader) {
        post(new Runnable() {
            @Override
            public void run() {
                // do additional scrolling if a pinned view is displayed
                int pinnedOffset = (mPinnedSection == null ? 0 : mPinnedSection.view.getBottom() + getDividerHeight());
                if (pinnedOffset > 0) {
                    PinnedHeaderListView.super.setSelectionFromTop(position, y + pinnedOffset);
                }
            }
        });
    }
}

/**
 * Sets the currently selected item. If in touch mode, the item will not be selected but it will
 * still be positioned appropriately. If the specified selection position is less than 0, then
 * the item at position 0 will be selected.
 *
 * @param position Index (starting at 0) of the data item to be selected.
 */
@Override
public void setSelection(int position) {
    setSelectionFromTop(position, 0);
}
}

我的MainActivity with adapter:

public class MainActivity extends ListActivity implements OnClickListener {

public class SimpleAdapter extends BaseAdapter {

    private final int[] COLORS = new int[] {
            Color.BLUE, Color.RED,
            Color.GREEN, Color.YELLOW };

    ArrayList<Item> data = new ArrayList<>();
    LayoutInflater mInflater;

    public SimpleAdapter(Context context) {
        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        final int sectionsNumber = 4;
        prepareSections(sectionsNumber);

        int sectionPosition = 0;
        int listPosition = 0;
        for (char i=0; i<sectionsNumber; i++) {
            Item section = new Item(Item.SECTION, String.valueOf((char)('A' + i)));
            section.sectionPosition = sectionPosition;
            section.listPosition = listPosition++;
            onSectionAdded(section, sectionPosition);
            data.add(section);

            final int itemsNumber = (int) Math.abs((Math.cos(2f*Math.PI/3f * sectionsNumber / (i+1f)) * 25f));
            for (int j=0;j<itemsNumber;j++) {
                Item item = new Item(Item.ITEM, section.text.toUpperCase(Locale.ENGLISH) + " - " + j);
                item.sectionPosition = sectionPosition;
                item.listPosition = listPosition++;
                data.add(item);
            }

            sectionPosition++;
        }
    }

    @Override
    public Item getItem(int position){
        return data.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getCount() {
        return data.size();
    }

    protected void prepareSections(int sectionsNumber) { }
    protected void onSectionAdded(Item section, int sectionPosition) { }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Item item = getItem(position);

        if (item.type == Item.SECTION) {
            View headerView = convertView;
            TextView header = (TextView)findViewById(R.id.header);

            if (headerView == null) {
                headerView = mInflater.inflate(R.layout.header_list_item, parent);
            }
            if (position == 0) {
                header.setBackgroundResource(R.drawable.myHeaderImage);
                header.setText("Header1");
                header.setTextSize(TypedValue.COMPLEX_UNIT_PX, 100);
                header.setGravity(Gravity.BOTTOM | Gravity.END);
            }
            else {
                header.setHeight(400);
                header.setBackgroundColor(COLORS[item.sectionPosition % COLORS.length]);
            }
            return headerView;
        }
        else {
            View contentView = convertView;
            if (contentView == null) {
                contentView = mInflater.inflate(R.layout.content_list_item, parent);
            }

            ImageView icon = (ImageView)findViewById(R.id.icon);
            TextView content = (TextView)findViewById(R.id.content);
            content.setText(R.string.contentString);
            icon.setImageResource(R.drawable.contentIcon);

            return contentView;
        }

    }

    @Override public int getViewTypeCount() {
        return 2;
    }

    @Override public int getItemViewType(int position) {
        return getItem(position).type;
    }

    public boolean isItemViewTypePinned(int viewType) {
        return viewType == Item.SECTION;
    }

}

private boolean addPadding;
private boolean isShadowVisible = true;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (savedInstanceState != null) {
        addPadding = savedInstanceState.getBoolean("addPadding");
        isShadowVisible = savedInstanceState.getBoolean("isShadowVisible");
    }

    initializeAdapter();
    initializePadding();
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean("addPadding", addPadding);
    outState.putBoolean("isShadowVisible", isShadowVisible);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    return true;
}

private void initializePadding() {
    float density = getResources().getDisplayMetrics().density;
    int padding = addPadding ? (int) (16 * density) : 0;
    getListView().setPadding(padding, padding, padding, padding);
}


@SuppressLint("NewApi")
private void initializeAdapter() {
    setListAdapter(new SimpleAdapter(this));

}

@Override
public void onClick(View v) {
    v.setOnClickListener(null);
}


}

我的布局(这里没什么太复杂的):

activity_main:

<rsay.pinnedheader.PinnedHeaderListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:headerDividersEnabled="false"
android:footerDividersEnabled="false"
android:divider="#FFF"
android:dividerHeight="10dp"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:listSelector="@android:color/transparent"
android:cacheColorHint="@android:color/transparent"
/>

content_list_item:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/icon"
    android:layout_alignParentTop="true"
    android:layout_alignParentStart="true" />

<View
    android:layout_width="2dp"
    android:layout_height="match_parent"
    android:background="@android:color/darker_gray"
    android:layout_toLeftOf="@+id/content"
    android:layout_below="@+id/icon"/>

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:text="Medium Text"
    android:id="@+id/content"
    android:layout_alignParentTop="true"
    android:layout_toEndOf="@+id/icon" />
</RelativeLayout>

header_list_item:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
    android:id="@+id/header"
    android:layout_width="match_parent"
    android:layout_height="400dp" />

</LinearLayout>

错误日志:

E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: rsay.pinnedheader, PID: 17327
java.lang.UnsupportedOperationException: addView(View, LayoutParams) is not supported in AdapterView
        at android.widget.AdapterView.addView(AdapterView.java:482)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:512)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:414)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:365)
        at rsay.pinnedheader.MainActivity$SimpleAdapter.getView(MainActivity.java:89)
        at android.widget.AbsListView.obtainView(AbsListView.java:2344)
        at android.widget.ListView.measureHeightOfChildren(ListView.java:1270)
        at android.widget.ListView.onMeasure(ListView.java:1182)
        at android.view.View.measure(View.java:17440)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5465)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:430)
        at android.view.View.measure(View.java:17440)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5465)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1436)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:722)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:613)
        at android.view.View.measure(View.java:17440)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5465)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:430)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2560)
        at android.view.View.measure(View.java:17440)
        at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2031)
        at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1189)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1402)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1077)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5884)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
        at android.view.Choreographer.doCallbacks(Choreographer.java:580)
        at android.view.Choreographer.doFrame(Choreographer.java:550)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5312)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:696)

它引用的行是:

headerView = mInflater.inflate(R.layout.header_list_item, parent);

我尝试将此行更改为:

headerView = mInflater.inflate(R.layout.header_list_item, parent, false);

然后我得到了Null Pointer Exception

header.setBackgroundResource(R.drawable.myHeaderImage);

因为标题(标题xml中的TextView)为空。

1 个答案:

答案 0 :(得分:2)

重载inflate方法,以及您在此行中使用的双参数版本:

headerView = mInflater.inflate(R.layout.header_list_item, parent);

实际上会尝试夸大的视图添加到名为AdapterView的{​​{1}}(导致原始parent的原因)。如果你看一下source code for the LayoutInflater class(第364-366行):

UnsupportedOperationException

你可以看到为什么这是真的 - 因为你传入一个非空的根,你的原始代码相当于调用

public View inflate(int resource, ViewGroup root) {
    return inflate(resource, root, root != null);
}

你在行上看到的headerView = mInflater.inflate(R.layout.header_list_item, parent, true);

NullPointerException

并不奇怪。当你写

header.setBackgroundResource(R.drawable.myHeaderImage);

你可能真的想写

View headerView = convertView;
TextView header = (TextView)findViewById(R.id.header);

if (headerView == null) {
    headerView = mInflater.inflate(R.layout.header_list_item, parent);
}

后一代码在(保证非空)View headerView = convertView; if (headerView == null) { headerView = mInflater.inflate(R.layout.header_list_item, parent, false); } TextView header = (TextView)headerView.findViewById(R.id.header); 内搜索以找到合适的headerViewTextView本身不包含ID为AdapterView的{​​{1}}。