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
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.
AdapterView is a ViewGroup that displays items loaded into an adapter. The most common type of adapter comes from an array-based data source.
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.
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;
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With