欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

复杂ListView及多层嵌套界面卡顿问题终极解决NoCacheListView

程序员文章站 2022-03-31 08:05:29
ListView是Android曾经也是现在最常用的控件,item复用和界面卡顿等问题伴随着ListView一直没有断过,后来RecyclerView的出现解决了一些缓存复用的问题,确实RecyclerView拥有一套比较完善的缓存机制。但是我们今天的主角不是它,而是一个全新的控件。 有些时候我需要列表中在滑动的时候,一个view都不要重新绘制,我不需要item之间的相互复用,这样的情况下,外层一个ScrollView,里面一个LinearLayout,似乎就满足的条件,但是单纯的Line......
    ListView是Android曾经也是现在最常用的控件,item复用和界面卡顿等问题伴随着ListView一直没有断过,后来RecyclerView的出现解决了一些缓存复用的问题,确实RecyclerView拥有一套比较完善的缓存机制。但是我们今天的主角不是它,而是一个全新的控件。
    有些时候我需要列表中在滑动的时候,一个view都不要重新绘制,我不需要item之间的相互复用,这样的情况下,外层一个ScrollView,里面一个LinearLayout,似乎就满足的条件,但是单纯的LinearLayout去当做ListView使用的时候还是要自己写不少逻辑。这次文章介绍的就是已经写好的一个自定义LinearLayout,完全可以当做一个无缓存的ListView使用,子界面不去频繁的重绘,就节省了很大一笔内存开销,界面滑动就会流畅,再复杂的列表嵌套,经测试也不会卡顿。好了,闲话少说,咱们直接看代码,首先就是重要的自定义控件NoCacheListView。

代码比较简洁,大家稍微看下,里面包含的配套的setAdapter方法,这是大家比较关心的,此类提供了两种,

1、setAdapter(),这个需要自己定义一个类继承本类中提供的Adapter,或者直接new NoCacheListView.Adapter()也可以;

2、setAdapter2(),这个方法就可以使用我们平时用的Adapter,也就是继承android.widget.BaseAdapter的适配器;

下面是NoCacheListView的完整代码,可以直接使用,

public class NoCacheListView extends LinearLayout implements View.OnClickListener {
    
    boolean mLayoutFrozen = false;
    
    private OnItemClickListener mListener;
    
    private Adapter<?> mAdapter;
    private android.widget.BaseAdapter mAdapter2;
    
    private DataSetObserver mDataSetObserver = new DataSetObserver() {
        @Override
        public void onChanged() {
            refreshLayout();
        }
    
        @Override
        public void onInvalidated() {
            super.onInvalidated();
        }
    };
    private static final int INVALID_POSITION = -1;
    
    public NoCacheListView(Context context) {
        this(context, null);
    }
    
    public NoCacheListView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
    
    public NoCacheListView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOrientation(LinearLayout.VERTICAL);
    }
    
    public void setAdapter(Adapter<?> adapter) {
        if (adapter == null) {
            return;
        }
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }
        mAdapter = adapter;
        mAdapter.registerDataSetObserver(mDataSetObserver);
        refreshLayout();
    }
    
    /**
     * 支持通用BaseAdapter支持
     * 由于此View无缓存,getView中的convertView始终为null
     * @param adapter
     */
    public void setAdapter2(android.widget.BaseAdapter adapter) {
        if (adapter == null) {
            return;
        }
        if (mAdapter2 != null && mDataSetObserver != null) {
            mAdapter2.unregisterDataSetObserver(mDataSetObserver);
        }
        mAdapter2 = adapter;
        mAdapter2.registerDataSetObserver(mDataSetObserver);
        refreshLayout();
    }
    
    private void refreshLayout() {
        mLayoutFrozen = false;
        removeAllViews();
        if (mAdapter != null && mAdapter.getCount() > 0) {
            for (int i = 0; i < mAdapter.getCount(); i++) {
                addView(mAdapter.getView(this, i));
            }
            refreshListener();
        } else if (mAdapter2 != null && mAdapter2.getCount() > 0) {
            for (int i = 0; i < mAdapter2.getCount(); i++) {
                addView(mAdapter2.getView(i, null, this));
            }
            refreshListener();
        }
        requestLayout();
    }
    
    private void refreshListener() {
        if (mListener != null) {
            int childCount = getChildCount();
            if (childCount > 0) {
                for (int i = 0; i < childCount; i++) {
                    View view = getChildAt(i);
                    view.setOnClickListener(this);
                }
            }
        }
    }
    
    public void setOnItemClickListener(OnItemClickListener listener) {
        if (listener != null) {
            mListener = listener;
        }
        refreshListener();
    }
    
    @Override
    public void onClick(View v) {
        int position = getPositionForView(v);
        if (position == INVALID_POSITION || (mAdapter == null && mAdapter2 == null)) {
            // Cannot perform actions on invalid items.
            return;
        }
        mListener.onItemClick(v, position);
    }
    
    public int getPositionForView(View view) {
        View listItem = view;
        try {
            View v;
            while ((v = (View) listItem.getParent()) != null && !v.equals(this)) {
                listItem = v;
            }
        } catch (ClassCastException e) {
            // We made it up to the window without find this list view
            return INVALID_POSITION;
        }
    
        // Search the children for the list item
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            if (getChildAt(i).equals(listItem)) {
                return i;
            }
        }
        // Child not found!
        return INVALID_POSITION;
    }
    
    public interface OnItemClickListener {
        void onItemClick(View view, int position);
    }
    
    public static abstract class Adapter<T> {
    
        private final DataSetObservable mDataSetObservable = new DataSetObservable();
        
        private void registerDataSetObserver(DataSetObserver observer) {
            mDataSetObservable.registerObserver(observer);
        }
    
        private void unregisterDataSetObserver(DataSetObserver observer) {
            mDataSetObservable.unregisterObserver(observer);
        }
        
        public void notifyDataSetChanged() {
            mDataSetObservable.notifyChanged();
        }
    
        /**
         * 获取当前条目类型
         * @param position 当前位置
         * @return
         */
        public abstract int getItemViewType(int position);
    
        /**
         * 获取展示条目总数
         * @return
         */
        public abstract int getCount();
    
        /**
         * 获取当前条目对象
         * @param position 当前位置
         * @return
         */
        public abstract T getItem(int position);
    
        /**
         * 获取要渲染的View
         * @param parent 父布局
         * @param position 当前位置
         * @return
         */
        public abstract View getView(ViewGroup parent, int position);
    }
}

 

本文地址:https://blog.csdn.net/qiqigeermumu/article/details/110188797