美文网首页
HeightListView - 自适应TextView高度

HeightListView - 自适应TextView高度

作者: phantomvk | 来源:发表于2018-02-06 13:42 被阅读0次

使用Adapter可能需要在一个item里包含ListView控件动态展示多个TextView。普通的ListView会出现所有的TextView只显示第一行而不是多行内容的问题。

问题的根源是ListView没有正确计算出每个TextView文字需要的高度,仅显示一行文字的高度。

重写的的类中,把计算高度的onItemsChanged()通过DataSetObserver绑定到数据对应的adapter,且重写onMeasure(int, int)。数据改变时回调DataSetObserver,新数据对应的TextView高度得到计算。

DataSetObserver在初始化ListView的时候已经创建完成,在setAdapter(ListAdapter adapter)中绑定到adapter

经过上面的处理,令HeightListViewListView用法无异。

/**
 * Auto height calculating ListView.
 * Calculates items' total height which includes some TextViews.
 * Use it just like a common ListView.
 * Author: PhantomVK
 */
public final class HeightListView extends ListView {

    private DataSetObserver mDataSetObserver; // The custom data set observer.

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

    public HeightListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        createObserver();
    }

    public HeightListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        createObserver();
    }

    /**
     * Overrides to set a suitable measure spec.
     *
     * @param widthMeasureSpec  widthMeasureSpec, not changed.
     * @param heightMeasureSpec heightMeasureSpec replaced with a custom one.
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int spec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, spec);
    }

    /**
     * Sets the data behind this ListView.
     * <p>
     * Unregisters the observer of the pre adapter before which will be replaced.
     * After the new adapter is setted, registers a observer.
     *
     * @param adapter Set the new listView adapter
     */
    @Override
    public void setAdapter(ListAdapter adapter) {
        if (adapter == null) throw new NullPointerException();

        if (getAdapter() != null) {
            getAdapter().unregisterDataSetObserver(mDataSetObserver);
        }

        adapter.registerDataSetObserver(mDataSetObserver);
        super.setAdapter(adapter);
    }

    /**
     * Creates a custom data set change observer.
     */
    private void createObserver() {
        if (mDataSetObserver != null) return;
        mDataSetObserver = new DataSetObserver() {
            @Override
            public void onChanged() {
                super.onChanged();
                onItemsChanged();
            }
        };
    }

    /**
     * Calculates the height of all sub items, includes divider height of each item.
     */
    private void onItemsChanged() {
        int count = getAdapter().getCount();
        if (count == 0) return;

        int height = 0;
        for (int i = 0; i < count; i++) {
            View v = getAdapter().getView(i, null, this);
            v.measure(0, 0);
            height += v.getMeasuredHeight();
        }

        ViewGroup.LayoutParams params = getLayoutParams();
        params.height = height + getDividerHeight() * (count - 1);
        setLayoutParams(params);
    }

    @Override
    public boolean isClickable() {
        return super.isClickable();
    }

    @Override
    public boolean isEnabled() {
        return super.isEnabled();
    }
}

最后还把isClickable()isEnabled()显式调用父类方法:itemListView展示的所有subitem都是可以点击的。要subitem把点击事件传递给item处理,则需要重写或设置上述两个方法的值为false

相关文章

网友评论

      本文标题:HeightListView - 自适应TextView高度

      本文链接:https://www.haomeiwen.com/subject/kbdgzxtx.html