Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating an Adapter to a CustomView

I have been trying to look online for any solutions or examples on how to do that, but could not manage to find anything what resembles my problem.

I have a LinearLayout where I want to add/remove Views when ArrayList data changes.

As far as I understand, the only way is creating a CustomView by extending AdapterView and using ArrayAdapter.

Unfortunately I don't understand the proper data-flow to solve this problem.

Where do I specify in CustomView which View is the container? Can I just cast CustomView on LinearLayout when I will have implemented it?

EDIT: I emphasize - I do not need a ListView. I need it for a CustomView

like image 417
Arturs Vancans Avatar asked Jan 27 '13 17:01

Arturs Vancans


People also ask

What is adapter in Android studio?

An Adapter object acts as a bridge between an AdapterView and the underlying data for that view. The Adapter provides access to the data items. The Adapter is also responsible for making a View for each item in the data set. See also: ArrayAdapter.

What is View adapter?

AdapterView is a ViewGroup that displays items loaded into an adapter. The most common type of adapter comes from an array-based data source.

What is Custom ArrayAdapter in Android?

ArrayAdapter is a type of Adapter which acts a bridge between UI component and data source that helps us to fill data in UI component. It expects a Layout with a single TextView and for more customization in grid items or list items, we use custom adapters.


2 Answers

You do not need to extend AdapterView to generate your views from adapter in your custom view. You can extend LinearLayout and handle adapter. The simplest solution would look like:

public class CustomAdapterView extends LinearLayout {

    private Adapter adapter;
    private final DataSetObserver observer = new DataSetObserver() {

        @Override
        public void onChanged() {
            refreshViewsFromAdapter();
        }

        @Override
        public void onInvalidated() {
            removeAllViews();
        }
    };

    public CustomAdapterView(Context context) {
        super(context);
    }

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

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

    public Adapter getAdapter() {
        return adapter;
    }

    public void setAdapter(Adapter adapter) {
        if (this.adapter != null) {
            this.adapter.unregisterDataSetObserver(observer);
        }
        this.adapter = adapter;
        if (this.adapter != null) {
            this.adapter.registerDataSetObserver(observer);
        }
        initViewsFromAdapter();
    }

    protected void initViewsFromAdapter() {
        removeAllViews();
        if (adapter != null) {
            for (int i = 0; i < adapter.getCount(); i++) {
                addView(adapter.getView(i, null, this), i);
            }
        }
    }

    protected void refreshViewsFromAdapter() {
        int childCount = getChildCount();
        int adapterSize = adapter.getCount();
        int reuseCount = Math.min(childCount, adapterSize);

        for (int i = 0; i < reuseCount; i++) {
            adapter.getView(i, getChildAt(i), this);
        }

        if (childCount < adapterSize) {
            for (int i = childCount; i < adapterSize; i++) {
                addView(adapter.getView(i, null, this), i);
            }
        } else if (childCount > adapterSize) {
            removeViews(adapterSize, childCount);
        }
    }
}

As the code above is only a simple example it does not handle situation where adapter returns views of different types (e.g. Adapter#getViewTypeCount() returns number greater than 1).

Of course all methods defined LinearLayout for adding/removing views are available so they may collide with your adapter handling. You could disable them by throwing UnsupportedOperationException:

    @Override
    public void addView(View child) {
        throw new UnsupportedOperationException(
                "You cannot add views directly without adapter!");
    }

(and so on for all other add/remove methods), or by overriding them to manipulate adapter's backing dataset (which should be forced to implement your custom defined interface allowing such modifications next to Adapter interface). In both cases remember to call add remove methods from superclass in your code for adapter handling.

EDIT: And simple implementation extending LinearLayout with support for Adapter's viewTypesCount:

class CustomAdapterViewTypedImpl extends LinearLayout {

    private Adapter adapter;
    private SparseArray<List<View>> typedViewsCache = new SparseArray<List<View>>();
    private final DataSetObserver observer = new DataSetObserver() {

        @Override
        public void onChanged() {
            refreshViewsFromAdapter();
        }

        @Override
        public void onInvalidated() {
            removeAllViews();
        }
    };

    public CustomAdapterViewTypedImpl(Context context) {
        super(context);
    }

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

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

    public Adapter getAdapter() {
        return adapter;
    }

    public void setAdapter(Adapter adapter) {
        if (this.adapter != null) {
            this.adapter.unregisterDataSetObserver(observer);
        }
        this.adapter = adapter;
        if (this.adapter != null) {
            this.adapter.registerDataSetObserver(observer);
        }
        initViewsFromAdapter();
    }

    protected void initViewsFromAdapter() {
        typedViewsCache.clear();
        removeAllViews();
        View view;
        if (adapter != null) {
            for (int i = 0; i < adapter.getCount(); i++) {
                view = adapter.getView(i, null, this);
                addToTypesMap(adapter.getItemViewType(i), view, typedViewsCache);
                addView(view, i);
            }
        }
    }

    protected void refreshViewsFromAdapter() {
        SparseArray<List<View>> typedViewsCacheCopy = typedViewsCache;
        typedViewsCache = new SparseArray<List<View>>();
        removeAllViews();
        View convertView;
        int type;
        for (int i = 0; i < adapter.getCount(); i++) {
            type = adapter.getItemViewType(i);
            convertView = shiftCachedViewOfType(type, typedViewsCacheCopy);
            convertView = adapter.getView(i, convertView, this);
            addToTypesMap(type, convertView, typedViewsCache);
            addView(convertView, i);
        }
    }

    private static void addToTypesMap(int type, View view, SparseArray<List<View>> typedViewsCache) {
        List<View> singleTypeViews = typedViewsCache.get(type);
        if(singleTypeViews == null) {
            singleTypeViews = new ArrayList<View>();
            typedViewsCache.put(type, singleTypeViews);
        }
        singleTypeViews.add(view);
    }

    private static View shiftCachedViewOfType(int type, SparseArray<List<View>> typedViewsCache) {
        List<View> singleTypeViews = typedViewsCache.get(type);
        if(singleTypeViews != null) {
            if(singleTypeViews.size() > 0) {
                return singleTypeViews.remove(0);
            }
        }
        return null;
    }
}
like image 125
Tomasz Gawel Avatar answered Oct 08 '22 13:10

Tomasz Gawel


Have a look into AdapterView implementation . For you its mainly Extending DataSetobserver line 794.

Just to give you a rough idea,
1) Create class extending DataSetObserver inside your Custom View class.

2)Declare your own Interface from your custom view for getting View/Data details and let your CustomAdapter implement it.

3)Whenever there's a change in your Array call onChanged()/onInvalidated() of DatSetObserver in your custom .

If you are really serious about AdapterView then follow this link.

like image 23
manjusg Avatar answered Oct 08 '22 15:10

manjusg