I have an app which displays the Matchday of some soccer competitions depending date of the match. I am using a spinner to re-create the adapter of my RecyclerView depending on the User selection with some dummyData(). But sometimes while the RecyclerView is first initialized or recycled it displays literally the format of my .xml.
I managed to reduce the level of visual glitch by adding a custom animation on the Adapter but still sometimes it just happens, like 10% of the time I change Matchday.
format_home.xml
This is the placeholder I created to design this format.
Gif showing problem:
HomeFragment.java
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
mRecyclerView = view.findViewById(R.id.home_recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
mSpinner = view.findViewById(R.id.spinner_home);
List<String> matchDay = new ArrayList<>();
for (int i = 0; i < 20; i++){
matchDay.add("Matchday " + (i + 1));
}
HomeSpinnerAdapter dataAdapter = new HomeSpinnerAdapter(getContext(), R.layout.format_home_spinner, matchDay);
mSpinner.setAdapter(dataAdapter);
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mHomeItems = new ArrayList<>();
mDisposable.add(Observable.fromArray(mHomeItems)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(homeItems -> {
Map<String, List<Matchday>> hashMap = toMap(dummyData());
// Map Key
for (String date : hashMap.keySet()) {
Header header = new Header(date);
homeItems.add(header);
for (Matchday matchday : hashMap.get(date)) {
MatchItem matchItem = new MatchItem(matchday);
homeItems.add(matchItem);
}
}
mAdapter = new HomeAdapter(homeItems, getActivity(), mTeamViewModel);
mRecyclerView.setAdapter(mAdapter);
}));
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
return view;
}
// Map Value List<T>
private Map<String,List<Matchday>> toMap(List<Matchday> matchdays) {
Map<String, List<Matchday>> map = new TreeMap<>();
for (Matchday matchday : matchdays){
List<Matchday> value = map.get(matchday.getDate());
if (value == null) {
value = new ArrayList<>();
map.put(matchday.getDate(), value);
}
value.add(matchday);
}
return map;
}
public List<Matchday> dummyData(){
mMatchdayList = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 20; i ++){
mMatchdayList.add(new Matchday((i+1), buildRandomDateInCurrentMonth(), 1, random.nextInt(36), 0, 0, random.nextInt(36)));
}
return mMatchdayList;
}
If you have any feedback or need any other activity, let me know! TY
Thanks to #jantursky and some deep digging in the web I came across this series of articles and managed to identify that my RecyclerView was recreating all views in the absence of setStableIds(true) because in recyclers Scrap lists are the first place where the RecyclerView looks when searching for a ViewHolder.
So to resolve the problem:
Created an interface to retrieve List IDS.
Created a refreshAdapter() method in the Adapter.
Initialized and instantiate the Adapter outside the ` mSpinner.setOnItemSelectedListener() with setHasStableIds(true).
Inside the mSpinner.setOnItemSelectedListenercall refreshAdapter()
Related
I have the following custom array adapter of my custom model -
public class ColorAttributeArrayAdapter extends ArrayAdapter<ProductAttributeModel> {
private List<ProductAttributeModel> titles;
private Context context;
private MarketApiCalls marketApiCalls;
public ColorAttributeArrayAdapter(#NonNull Context context, List<ProductAttributeModel> titles) {
super(context, R.layout.product_attribute_spinner_row_item, R.id.product_attribute_spinner_row_item_textview, titles);
this.titles = titles;
this.context = context;
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_PORTAL_PRODUCTION_URL)
// .baseUrl(BASE_PORTAL_STAGE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
marketApiCalls = retrofit.create(MarketApiCalls.class);
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View listItem = convertView;
if (listItem == null) {
listItem = LayoutInflater.from(context).inflate(R.layout.product_attribute_spinner_row_item, parent, false);
}
// String currentString = titles.get(position).getAttributeValues().get(position);
List<String> attributeValues = titles.get(position).getAttributeValues();
for (int i = 0; i < attributeValues.size(); i++) {
String currentString = attributeValues.get(i);
//Setting the image color
ImageView imageView = listItem.findViewById(R.id.product_attribute_spinner_row_item_image_view);
Map<String, String> htmlStandardColorMap = ColorUtil.getHtmlStandardColorMap();
if (htmlStandardColorMap.containsKey(currentString)) {
imageView.setBackgroundColor(Color.parseColor(htmlStandardColorMap.get(currentString)));
} else {
String colorURL = COLORS_API.concat(Uri.encode(currentString, "UTF-8"));
Picasso.get().load(colorURL).resize(90,90).into(imageView);
}
TextView value = listItem.findViewById(R.id.product_attribute_spinner_row_item_textview);
value.setText(currentString);
}
return listItem;
}
#Override
public View getDropDownView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
return getView(position, convertView, parent);
}
}
public class ProductAttributeModel {
private String attributeName;
private List<String> attributeValues;
public ProductAttributeModel(String attributeName, List<String> attributeValues) {
this.attributeName = attributeName;
this.attributeValues = attributeValues;
}
public String getAttributeName() {
return attributeName;
}
public void setAttributeName(String attributeName) {
this.attributeName = attributeName;
}
public List<String> getAttributeValues() {
return attributeValues;
}
public void setAttributeValues(List<String> attributeValues) {
this.attributeValues = attributeValues;
}
#Override
public String toString() {
return "ProductAttributeModel{" +
"attributeName='" + attributeName + '\'' +
", attributeValues=" + attributeValues +
'}';
}
}
and I am starting my adapter from the following function -
#Override
public void setProductPurchaseAttributes() {
selectedProductAttributesMap = selectedProduct.getAttributesList();
/*Starting index is the index in which we start to add the dynamic linear layouts that represents products attributes.
This number should be incremented by 1 every time we do any changes to `activity_product_page.xml` file otherwise the dynamic views
will be created in the wrong place.
*/
int startingIndex = 7;
if (!isProductAvailable) return;
ArrayList<ProductAttributeModel> productAttributeModels = new ArrayList<>();
Spinner spinner = new Spinner(this);
for (Map.Entry<String, List<String>> entry : selectedProductAttributesMap.entrySet()) {
//Key and value for each iteration
String key = entry.getKey();
List<String> value = entry.getValue();
//creating the linear layout
LinearLayout linearLayout = new LinearLayout(this);
linearLayout.setOrientation(LinearLayout.HORIZONTAL);
//creating the layout params
LinearLayout.LayoutParams attributeLayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
//setting margins
/*These margins values are different than the values in the XML of the activity because this is a dynamically created view*/
attributeLayoutParams.setMargins(48, 30, 48, 0);
textParams.setMargins(0, 60, 0, 0);
linearLayout.setLayoutParams(attributeLayoutParams);
//creating the text view
TextView textView = new TextView(this);
textView.setText(key.concat(":"));
textView.setLayoutParams(textParams);
//creating the spinner
spinner.setLayoutParams(spinnerParams);
//attribute list adapter
productAttributeModels.add(new ProductAttributeModel(key, value));
ColorAttributeArrayAdapter adapter = new ColorAttributeArrayAdapter(this, productAttributeModels);
spinner.setOnItemSelectedListener(this);
spinner.setAdapter(adapter);
//adding to the linear layout
linearLayout.addView(textView);
linearLayout.addView(spinner);
//adding linear layout to root view
productDetailsViewGroup.addView(linearLayout, startingIndex);
startingIndex++;
}
}
for some reason, I get only one result instead of multiple results.
Moving the adapter instantiation to the end of the for loop won't help, tried that already.
When doing debugging, I saw that the object of the adapter holds 4 items inside the list but I am showing only the last one. Seems like I am writing the object and not adding it.
What am I missing?
Move these lines outside your for loop completely
ColorAttributeArrayAdapter adapter = new ColorAttributeArrayAdapter(this, productAttributeModels);
spinner.setOnItemSelectedListener(this);
spinner.setAdapter(adapter);
You are currently creating a new adapter in each run of the for loop with the latest attribute model and attaching it to the spinner, thus the spinner only has the last adapter with the last attribute model when it reaches the end. Your adapter created outside the for loop will have all the attributes you added to the productAttributeModels list.
In the parent activity, I have an edit text in a toolbar, and user can make a search through the data displayed by the recyclerview.
When the user push enter key down, the string in the edittext is sent to the fragment by :
Bundle bundle = new Bundle();
bundle.putString(predResult, placeid);
MapFragment mapFragment = new MapFragment();
ListRestFragment listRestFragment = new ListRestFragment();
mapFragment.setArguments(bundle);
listRestFragment.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.map, mapFragment)
.replace(R.id.list_frag, listRestFragment)
.commit();
but, unfortunatly, the recyclerview is not resfreshed while my adapter is notified the data is changed as it shown below:
private void queryToList(Query query) {
query.addSnapshotListener((queryDocumentSnapshots, e) -> {
restaurantList = queryDocumentSnapshots.toObjects(Restaurant.class);
if (!myPrediction.contains("myPrediction")) {
System.out.println(myPrediction);
for (Restaurant item : restaurantList) {
if (item.getRestaurantID().contains(myPrediction)) {
restaurantListPred = new ArrayList<>();
restaurantListPred.add(item);
updateUI(restaurantListPred);
}
}
} else updateUI(restaurantList);
});
}
private void updateUI(List<Restaurant> restaurants) {
configureFab();
configureRecyclerView(restaurants);
}
private void configureRecyclerView(List<Restaurant> restaurant) {
this.adapter = new RestaurantAdapterClassic(restaurant);
this.recyclerView.setAdapter(this.adapter);
this.recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL);
recyclerView.addItemDecoration(new SimpleItemDecorator(getContext()));
}
the new List is updated automatically when the user makes his request, but the recyclerView doesn't display the new data.
if you want to check my adapter implementation:
public class RestaurantAdapterClassic extends RecyclerView.Adapter<RestaurantViewHolder> {
private List<Restaurant> restaurantsList;
// CONSTRUCTOR
public RestaurantAdapterClassic(List<Restaurant> restaurants) {
this.restaurantsList = restaurants;
}
#Override
public RestaurantViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// CREATE VIEW HOLDER AND INFLATING ITS XML LAYOUT
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.listview_pattern, parent, false);
return new RestaurantViewHolder(view);
}
#Override
public void onBindViewHolder(RestaurantViewHolder viewHolder, int position) {
viewHolder.updateWithRestaurant(this.restaurantsList.get(position));
}
// RETURN THE TOTAL COUNT OF ITEMS IN THE LIST
#Override
public int getItemCount() {
return this.restaurantsList.size();
}
public Restaurant getRestaurant(int position) {
return this.restaurantsList.get(position);
}
public void filterList(List<Restaurant> filteredList) {
restaurantsList = filteredList;
notifyDataSetChanged();
}
}
where is my error or my misunderstanding?
EDIT SOLUTION -
Create an Interface
Actually, to send the new data data from my Parent Activity to my Fragment with a listener to observe when the data changes.
Keep The data reference sent to the adapter
Actually, the main big problem that I had was my adapter doesn't refresh when I sent new array. The reason was an adapter creates a reference with the list/array. if you want to refresh it, you need to keep this reference by get the list, erase it, and put/add new data inside by the method addALL for example.
First of all, adapter.notifyDataSetChanged(); doesn't have any effect in the code as inside updateUI you create the adapter every time you call it.
I think, the main problem is in here:
if (item.getRestaurantID().contains(myPrediction)) {
restaurantListPred = new ArrayList<>();
restaurantListPred.add(item);
updateUI(restaurantListPred);
}
This block doesn’t execute at all. Thats why the list not updated.
I developing news feed like twitter, facebook and etc.
I have a server that gives me portions of articles for 10 pieces.
When I scroll down, new articles are loaded, added to the database and displayed in RealmRecyclerView.
I added SwipeRefreshLayout to update the data (in the case when new quotes came in).
I have a problem with adding new items to the top when i use SwipeRefreshLayout.
My idea was to sort new articles on id. In that case, when I scroll down the items with the smaller ones the id will be at the bottom, and when I use SwipeRefreshLayout new articles with higher id are download and adding in top list.
MyFragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_view_all_quotes, container, false);
swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh_layout);
swipeRefreshLayout.setOnRefreshListener(this);
Realm.init(getActivity());
realm = Realm.getDefaultInstance();
recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
adapter = new QuoteAdapter(realm.where(QuoteObject.class).findAllSortedAsync("id", Sort.DESCENDING), getActivity());
recyclerView.setAdapter(adapter);
return view;
}
#Override
public void onRefresh() {
int position = realm.where(QuoteObject.class).findAll().size();
DownloadDataFromServer.DownloadQuotes downloadQuotes =
new DownloadDataFromServer.DownloadQuotes();
try {
DownloadDataFromServer.DownloadQuotes.FlagQuotes = true;
downloadQuotes.execute(position+1).get();
}catch (Exception e){
Log.e(TAG, "Error", e);
}finally {
swipeRefreshLayout.setRefreshing(false);
}
}
RealmRecyclerViewAdapter
class QuoteAdapter extends RealmRecyclerViewAdapter<QuoteObject, QuoteAdapter.AllQuotesViewHolder> {
QuoteAdapter(OrderedRealmCollection<QuoteObject> quotes, Context context) {
super(quotes, true);
this.context = context;
if (quotes.size() == 0) downloadData(0);
}
.
.
.
#Override
public void onBindViewHolder(final QuoteAdapter.AllQuotesViewHolder viewHolder, int position) {
if (position == getData().size() - 1) downloadData(position);
....
}
But it does not work, the new data is loaded but does not add to top
Since I'm doing this for the first time, maybe I did not choose the right approach.
If you did this, tell how to create a news feed correctly.
If you don't have a updated field by which you can do
findAllSortedAsync("updated", Sort.DESCENDING)
Then you might want to consider adding a RANK field, and sort based on that.
For example,
// when downloading new items by "SwipeToRefresh" from top
// inside Realm transaction
RealmResults<FeedItem> feedItems = dao.findAll(realm);
for(int i = feedItems.size() - 1; i >= 0; i--) {
FeedItem feedItem = feedItems.get(i);
feedItem.setRank(feedItem.getRank() + response.size());
}
long rank = 1;
FeedItem defaultInstance = new FeedItem();
for(FeedItemDTO dto : response) {
FeedItem realmObject = mapper.toRealmObject(realm, dto, defaultInstance);
realmObject.setRank(rank++);
dao.insertOrUpdate(realm, realmObject);
}
And
// when download new items by scrolling to bottom
// inside Realm transaction
long count = dao.count(realm);
long rank = count + 1;
FeedItem defaultInstance = new FeedItem();
for(FeedItemDTO dto : response) {
FeedItem realmObject = mapper.toRealmObject(realm, dto, defaultInstance);
realmObject.setRank(rank++);
dao.insertOrUpdate(realm, realmObject);
}
public FeedItem toRealmObject(Realm realm, FeedItemDTO dto, FeedItemInterface defaultInstance) {
if(defaultInstance == null) {
defaultInstance = new FeedItem();
}
defaultInstance.setId(dto.getId());
RealmList<FeedCategory> categoryIds = new RealmList<FeedCategory>();
if(dto.getCategoryIds() != null) {
for(Long categoryId : dto.getCategoryIds()) {
FeedCategory feedCategory = feedCategoryRepository.findOne(realm, categoryId);
if(feedCategory != null) {
categoryIds.add(feedCategory);
}
}
}
defaultInstance.setCategoryIds(categoryIds);
defaultInstance.setCreatedTime(new Date(dto.getCreatedTime()));
defaultInstance.setUpdatedTime(new Date(dto.getUpdatedTime()));
defaultInstance.setTitle(dto.getTitle());
defaultInstance.setShortDescription(dto.getShortDescription());
defaultInstance.setNumberOfLikes(dto.getNumberOfLikes());
defaultInstance.setIsLiked(dto.getIsLiked());
defaultInstance.setIsFavorited(dto.getIsFavorited());
defaultInstance.setIsImportant(dto.getIsImportant());
defaultInstance.setImageUrl(dto.getImageUrl());
defaultInstance.setTypeCode(dto.getTypeCode());
defaultInstance.setResultTypeCode(dto.getResultTypeCode());
return defaultInstance;
}
I have an app that has 10 tabs (categorys) Each tab requires a different set of data to be called from a PHP/MySQL WebService on a remote server.
i'm passing category id (catid) to doinBackground function in parameter then i retrive the json and build the Array iList:
#Override
protected List doInBackground(String... params) {
int catid = Integer.parseInt(params[0]);
ProductApi prd = new ProductApi();
try {
List<ProductDetails> arrayproduct = prd.ProductApi(catid);
ArrayList<Data> al = new ArrayList<Data>();
for (ProductDetails prditem : arrayproduct) {
al.add(new Data(new String[] {
"Product",
prditem.getProductName(),
prditem.getProductPrice()},
new int[] { R.drawable.popularity_img5 }));
}
iList.addAll(al);
return arrayproduct;
} catch (IOException e) {
e.printStackTrace();
return null;
}
the problem i'm facing
is that i have the same product list in all tabs , because i don't know where i have to execute my AsyncTask on every horizontal Scroll , retrieve the current CatId (Category Id) and load new data on the current tab the user is viewing .
do i have to create many adapter as categories tabs i have ?
i Can get the CatId ( Category Id ) on instantiateItem but it don't let me execute the loaddata method getItemCount() in this case return Array List 0
#Override
public Object instantiateItem(ViewGroup container, int pos) {
int CatId = tinydb.getInt("CategoryId"+pos);
Log.w("PageAdapter", "-------------------------------------instantiateItem pos:"+pos+" CatId: "+CatId);
final View v = getLayoutInflater(null).inflate(R.layout.pager_card_view, null);
RecyclerView recList = (RecyclerView) v.findViewById(R.id.cardList);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
recList.setHasFixedSize(true);
//LinearLayoutManager llm = new LinearLayoutManager(getActivity());
StaggeredGridLayoutManager llm = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
llm.setOrientation(StaggeredGridLayoutManager.VERTICAL);
// use a linear layout manager
recList.setLayoutManager(llm);
// create an Object for Adapter
CardAdapter ca = new CardAdapter();
// set the adapter object to the Recyclerview
recList.setAdapter(ca);
((MainActivity) getActivity()).enableActionBarAutoHide(recList);
container.addView(v,
android.view.ViewGroup.LayoutParams.MATCH_PARENT,
android.view.ViewGroup.LayoutParams.MATCH_PARENT);
return v;
}
I have been trying to import multiple Listviews inside another "mother" Listview.
I have a class where the ListViews are created and then the Activity class which call these views. The only thing I managed to do is fill the "mother" with ListPerms# entries(the objects I guess)
Here is my class for multiple ListViews
public ListPerms(Context context, String a, int docid) {
super(context);
table=a;
did=docid;
list= (ListView) findViewById(R.id.specific_perm_list);
getdata(list,ar_list);
}
private void getdata( ListView list, ArrayList<String> arlist){
Database openHelper = new Database(getContext());
myDB = openHelper.getReadableDatabase();
myDB=SQLiteDatabase.openDatabase("data/data/com.example.login2/databases/aeglea", null, SQLiteDatabase.OPEN_READONLY);
try{fill(list,arlist);}catch(Exception e){Log.e("NAT EXISTANT","THIS->"+e);}
}
private void fill( ListView list, ArrayList<String> arlist){
Cursor temp = null;
Cursor buffer = null;
String type_from_table = null;
String[] items = null;
if(table=="med") {type_from_table = "medication";}
if(table=="test") {type_from_table = "test";}
if(table=="all") {type_from_table = "allergy";}
if(table=="proc") {type_from_table = "procedure";}
if(table=="cond") {type_from_table = "condition";}
if(table=="vacc") {type_from_table = "vaccine";}
temp = fetchOption("SELECT * FROM permission WHERE did="+did+" AND type='"+type_from_table+"'");
if(temp.getCount()>0){
buffer = fetchOption("SELECT * FROM user_"+table+" WHERE id="+temp.getString(temp.getColumnIndex("fileid")));
items = new String[] {buffer.getString(buffer.getColumnIndex("name"))};
arlist.addAll( Arrays.asList(items) );
listAdapter = new ArrayAdapter<String>(getContext(), R.layout.item, arlist);
for(int i=1;i<temp.getCount();i++){
temp.moveToNext();
buffer = fetchOption("SELECT * FROM user_"+table+" WHERE id="+temp.getString(temp.getColumnIndex("fileid")));
listAdapter.add(buffer.getString(buffer.getColumnIndex("name")));
}
list.setAdapter(listAdapter);
}else{
items = new String[] { "None."};
arlist.addAll( Arrays.asList(items) );
listAdapter = new ArrayAdapter<String>(getContext(), R.layout.item, arlist);
list.setAdapter(listAdapter);
}
}
And the Activity class follows where(now) I have an ExpandableListView, but I'm really noob to understand how ELV works
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.docp);
general = (ExpandableListView) findViewById(R.id.Tot_perm_list);
//random data
}
protected void onResume(){
super.onResume();
ListPerms me = new ListPerms(getApplicationContext(),"med",did);
ListPerms te = new ListPerms(getApplicationContext(),"test",did);
ListPerms all = new ListPerms(getApplicationContext(),"all",did);
ListPerms proc = new ListPerms(getApplicationContext(),"proc",did);
ListPerms cond = new ListPerms(getApplicationContext(),"cond",did);
ListPerms vacc = new ListPerms(getApplicationContext(),"vacc",did);
me.setActivated(isChild());
te.setActivated(isChild());
all.setActivated(isChild());
proc.setActivated(isChild());
cond.setActivated(isChild());
vacc.setActivated(isChild());
list.add(me);
list.add(te);
list.add(all);
list.add(proc);
list.add(cond);
list.add(vacc);
general.setAdapter(listAdapter);
general.addChildrenForAccessibility(list);
//what do I do
}
Advice on how to do ListView{LV,Lv ... } or ExpandableListView{LV, LV ....}
Per my understanding it's not a good idea to incorporate two or more list views into one. You'll definitely have issues with scrolling (of course, some dirty hacks can help to fix it) and probably, performance will be way worst than for single list.
I would suggest You to go with ExpandableList and ExpandableListAdapter. To make lists into one You will need to compose it's adapters.
e.g. it could looks like the following:
...
// Your implementation of ExpandableListAdapter
// To store all 'child' adapters, it should be filled by the owner
SparseArray<BaseAdapter> mChildAdapters = new SparseArray<BaseAdapter>();
#Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
final BaseAdapter childAdapter = mChildAdapters.get(groupPosition);
View view = null;
if (childAdapter != null) {
view = childAdapter.getView(childPosition, convertView, parent);
}
return view;
}
#Override
public int getChildrenCount(int groupPosition) {
int childrenCount = 0;
final BaseAdapter childAdapter = mChildAdapters.get(groupPosition);
if (childAdapter != null) {
childrenCount = mChildAdapter.getCount();
}
return childrenCount;
}
...
Please note, the above code is just a draft, but I hope the idea is clear from it. The only trick You've might face with this approach - convertView You've received in one childAdapter might be from another, so some (quite trivial) checks should be applied before reusing convertView.
Also, expandable list would give you nice possibility to provide every list with the header (via groupView).