Load more using gridview removing previous items after fetching new data - java

I want to implement loadmore with gridview and I am facing problems.
I am able to fetch new data on scroll and I am able to see new views itself. but previous views are disappearing.
I used notifyDataSetChanged().
But still this problem exists help me.
Here is my activity code
setting on scroll method in oncreate
grid.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int lastInScreen = firstVisibleItem + visibleItemCount;
if ((lastInScreen == totalItemCount) && !(loadingMore)) {
if (stopLoadingData == false) {
// FETCH THE NEXT BATCH OF FEEDS
listBikes();
}
}
}
}
listbikes is server call to fetch data on scroll.
In Processlistener method i will set the data to adapter.
bikeList = (CollectionResponseBike) result;
mCursor = bikeList.getNextPageToken();
items = (ArrayList) bikeList.getItems();
Collections.sort(items, new Comparator<Bike>() {
#Override
public int compare(Bike lhs, Bike rhs) {
String id1 = ((Bike) lhs).getTitle();
String id2 = ((Bike) rhs).getTitle();
return id1.compareTo(id2);
}
});
grid.setAdapter(new BikeCustomGrid(BikeGridList.this,items));
loadingMore = false;
progressBar.setVisibility(View.GONE);
Adapter code is here
public BikeCustomGrid(Context c, ArrayList<Bike> mItems) {
mContext = c;
items = mItems;
mLayoutInflater= LayoutInflater.from(c);
VolleySingleton mVolley= VolleySingleton.getInstance(mContext);
mImageLoader=mVolley.getImageLoader();
notifyDataSetChanged();
}
Please let me know the solution for this. thank you in adavance.

Whenever you call your database just add the new data into same list(don't replace it) and don't call the constructor again and again make some method in adapter to provide list and after that just notify the adapter.
Something like this:
mItems.addAll(yourListData coming from server or local db)
adapter.addItems(mItems);
method in adapter:
public addItems(List list){
items.addAll(list)
notifyDataSetChanged();
}

Related

Displaying values in recyclerview (based on boolean value) returns same result for all items

I have a recyclerview with a lot of items. I use adapter to populate the recyclerview from server, i.e. name, date, time, and so on, and it works OK.
Only issue I have is when I try and populate it based on a boolean value. When I try to add image star (for favourites option), and when I try to set is closed text (for active option), I always get all items with the "favourite icon" and "is closed" message.
For example, what I am trying to do when the working hours are returning active as false is like this:
if (!restaurant.getActive()) {
holder.isClosed.setVisibility(View.VISIBLE);
}
Boolean works just fine when I am setting it from a RestaurantDescriptionActivity, it saves and updates on server as it should, and it also returns the correct value, so I am not sure where my mistake could be.
I am returning values for boolean like this in my RestaurantModel:
#SerializedName("favourites")
private boolean isFavourite;
#SerializedName("active")
private boolean active;
----
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(thumbnailUrl);
parcel.writeString(dateTime);
parcel.writeString(id);
parcel.writeValue(comments_enabled);
// I TRIED WRITING BOOLEAN TO PARCEL IN TWO WAYS, AS SHOWN BELOW
parcel.writeValue(isFavourite);
parcel.writeInt(active ? 1 : 0);
}
//// UPDATED PART
protected restaurant(Parcel in) {
...
isFavourite = (Boolean) in.readValue(Boolean.class.getClassLoader());
active = Boolean.parseBoolean(in.readString());
}
//// UPDATED PART
public boolean getActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
Adapter:
class restaurantsViewHolder extends RecyclerView.ViewHolder {
public TextView restaurantName, date, time, isClosed;
ImageView thumbNail, isFavourite;
restaurantsViewHolder(View itemView) {
super(itemView);
thumbNail = itemView.findViewById(R.id.thumbnail);
isFavourite = itemView.findViewById(R.id.imageViewIsFavourite);
restaurantName= itemView.findViewById(R.id.restaurantNameIcon);
date = itemView.findViewById(R.id.date);
time = itemView.findViewById(R.id.time);
isClosed= itemView.findViewById(R.id.restaurantIsClosed);
}
}
public restaurantsAdapter(List<restaurant> restaurantList, Context context) {
this.restaurantItems = restaurantList;
this.context = context;
}
#Override
public restaurantsAdapter.restaurantsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_row_restaurant, parent, false);
return new restaurantsViewHolder(itemView);
}
#Override
public void onBindViewHolder(restaurantsAdapter.restaurantsViewHolder holder, int position) {
// getting restaurant data for the row
restaurant restaurant = restaurantItems.get(position);
holder.restaurantName.setText(restaurant.getrestaurantName());
LocalDateTime ldt = LocalDateTime.parse(restaurant.getDateTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
String formattedDate = ldt.format(getLocalizedDateFormatter(Locale.getDefault()));
holder.date.setText(formattedDate);
//holder.date.setText(convertDate(restaurant.getDateTime())); //string dobiti u formatu, pretvoriti ga u localized i podijeliti na dva dijela
holder.time.setText(convertTime(restaurant.getDateTime()));
holder.isFavourite.clearColorFilter();
if (!restaurant.getActive()) {
holder.isClosed.setVisibility(View.GONE);
}
if (restaurant.getIsFavourite()) {
holder.isFavourite.setImageResource(R.drawable.ic_icon_star_ppdcolor);
}
}
You need to code else part inside RecyclerView item otherwise it will repopulate its previous view state.
if (!restaurant.getActive()) {
holder.isClosed.setVisibility(View.GONE);
} else{
holder.isClosed.setVisibility(View.VISIBLE);
}
if (restaurant.getIsFavourite()) {
holder.isFavourite.setImageResource(R.drawable.ic_icon_star_ppdcolor);
} else{
holder.isFavourite.setImageResource(R.drawable.your_icon_when_not_favourite);
}
make interface for the favourite click event and set that record value as true in main activity used below code ...
onItemClickListner onItemClickListner;
public interface onItemClickListner{
void onItemClick(restaurant restaurantobj); // you can pass or object or value to need to access in recycler view activity.
}
public void setOnItemClickListner(RecyclerViewAdpater.onItemClickListner onItemClickListner) {
this.onItemClickListner = onItemClickListner;
}
#Override
public void onBindViewHolder(ItemViewHolder holder, int position) {
if (!restaurant.getActive()) {
holder.isClosed.setVisibility(View.GONE);
} else{
holder.isClosed.setVisibility(View.VISIBLE);
}
if (restaurant.getIsFavourite()) {
holder.isFavourite.setImageResource(R.drawable.ic_icon_star_ppdcolor);
} else{
holder.isFavourite.setImageResource(R.drawable.your_icon_when_not_favourite);
}
holder.isFavourite.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onItemClickListner.onItemClick(restaurant);
}
});
}
and after when you define adapter object in mainactivity that used below code ...
adpater.setOnItemClickListner(new RecyclerViewAdpater.onItemClickListner() {
#Override
public void onItemClick(restaurant restaurantobj) {
restaurantobj.setActive(true);
restaurantList.add(restaurantobj);
adpater.notifyDataSetChanged();
}
});

Invert ListView Order and Display Without Scrolling

I am trying to invert a ListView so that the most recent items appear first. I have seen results that state to modify the getItem() method, however that requires me scrolling down and scrolling back up to see the new item. Is there a way to have the item appear at the top of the list without requiring the need to scroll?
public class ListAdapter extends ArrayAdapter<Comments> {
Firebase BaseRef = new Firebase(FIREBASE_URL);
Firebase PollsRef = mBaseRef.child(POLLS_LABEL);
Firebase UpdateRef = mPollsRef.child(mCurrentDateString).child(String.valueOf(mPollIndex + 1));
Firebase CommentsRef = mUpdateRef.child(COMMENTS_LABEL);
int pollCommentCount;
public ListAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
public ListAdapter(Context context, int resource, List<Comments> items) {
super(context, resource, items);
}
#Override
public int getCount() {
CommentsRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
pollCommentCount = (int) dataSnapshot.getChildrenCount();
Log.v("POLL_COMMENT_COUNT", "The poll comment count is " + pollCommentCount);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
return pollCommentCount;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi;
vi = LayoutInflater.from(getContext());
v = vi.inflate(R.layout.individual_comment, null);
}
Comments p = getItem(position);
if (p != null) {
TextView userID = (TextView) v.findViewById(R.id.user_ID);
TextView userComment = (TextView) v.findViewById(R.id.user_comment);
if (userID != null) {
userID.setText(p.getUserID());
}
if (userComment != null) {
userComment.setText(p.getUserComment());
}
}
return v;
}
}
You can sort the Comment list before creating your adapter. This way they are already in the order you want them to be in. I don't know what variable the Comment object contains that lets you know when it was modified, but assuming it is a date, you can sort the list like this:
Collections.sort(commentsList, new Comparator<Comment>() {
public int compare(Comment c1, Comment c2) {
return c1.getDate().compareTo(c2.getDate());
}
});
You can also simply reverse the list with Collections.reverse(commentList)
Calling notifyDataSetChanged() should update the list.
I realized that the .add() method actually inserts the item at a specific index. If I am always adding new items to index(0), then the items will naturally appear in reverse order.
I thought Google would have been more intuitive with the Android code and allowed for an insert() method, but the add() method at index(o) serves the purpose:
mUpdateRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
setImage(dataSnapshot);
setQuestion(dataSnapshot);
createInitialCommentIDArray(dataSnapshot);
mNumberOfCommentsAtPoll = (int) dataSnapshot.child(COMMENTS_LABEL).getChildrenCount();
for (int i = 0; i < mNumberOfCommentsAtPoll; i++) {
String commentID = (String) dataSnapshot.child(COMMENTS_LABEL).child(mCommentIDArrayList.get(i)).child("COMMENT").getValue();
Log.v("COMMENT_ID", "The comment ID is " + commentID);
String userID = (String) dataSnapshot.child(COMMENTS_LABEL).child(mCommentIDArrayList.get(i)).child("USER_ID").getValue();
Log.v("USER_ID", "The user ID is " + userID);
mCommentArrayList.add(0 , new Comments(mUserAvatar, userID, commentID));
mCommentAdapter.notifyDataSetChanged();
}
}

Automatic loading of new entries from server into ScrollView as user scrolls down

Let me briefly explain my activity structure. When the user enters the app, they are immediately sent to a fragment displaying a ScrollView, containing a series of layouts. The layouts are populated from data pulled from a server, with the first ten or so loaded on the creation of the fragment. As the user scrolls down the ScrollView, I would like new layouts to be added to the bottom of the ScrollView so that all of the data does not have to be gathered from the server at once, and so that the list of entries does not exceed the size it needs to be.
An example of this would be Facebook's news feed on their android app, or Instagram's scrolling pictures. The Gmail app also has this behavior, loading emails on an as needed basis. In all cases, new data is loaded as the user scrolls down the page.
My question is, how best can this type of behavior be implemented?
I am defining best as:
The most efficient
The most portable (functional on the most phones and most API versions)
The easiest to maintain and expand upon
Follows accepted Android standards and conventions
To be clear, I am not simply looking for a solution, but the best solution. I am aware that there are multiple ways to implement this behavior, but I am looking for a definitive solution. Please include a few sentences explaining why your method is the best. Thank you.
you can use this class for load more items from server
public class LoadMoreListView extends ListView implements OnScrollListener {
private static final String TAG = "LoadMoreListView";
/**
* Listener that will receive notifications every time the list scrolls.
*/
private OnScrollListener mOnScrollListener;
private LayoutInflater mInflater;
// footer view
private RelativeLayout mFooterView;
// private TextView mLabLoadMore;
private ProgressBar mProgressBarLoadMore;
// Listener to process load more items when user reaches the end of the list
private OnLoadMoreListener mOnLoadMoreListener;
// To know if the list is loading more items
private boolean mIsLoadingMore = false;
private int mCurrentScrollState;
public LoadMoreListView(Context context) {
super(context);
init(context);
}
public LoadMoreListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public LoadMoreListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// footer
mFooterView = (RelativeLayout) mInflater.inflate(
R.layout.load_more_footer, this, false);
/*
* mLabLoadMore = (TextView) mFooterView
* .findViewById(R.id.load_more_lab_view);
*/
mProgressBarLoadMore = (ProgressBar) mFooterView
.findViewById(R.id.load_more_progressBar);
addFooterView(mFooterView);
super.setOnScrollListener(this);
}
#Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
}
/**
* Set the listener that will receive notifications every time the list
* scrolls.
*
* #param l
* The scroll listener.
*/
#Override
public void setOnScrollListener(AbsListView.OnScrollListener l) {
mOnScrollListener = l;
}
/**
* Register a callback to be invoked when this list reaches the end (last
* item be visible)
*
* #param onLoadMoreListener
* The callback to run.
*/
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
mOnLoadMoreListener = onLoadMoreListener;
}
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (mOnScrollListener != null) {
mOnScrollListener.onScroll(view, firstVisibleItem,
visibleItemCount, totalItemCount);
}
if (mOnLoadMoreListener != null) {
if (visibleItemCount == totalItemCount) {
mProgressBarLoadMore.setVisibility(View.GONE);
// mLabLoadMore.setVisibility(View.GONE);
return;
}
boolean loadMore = firstVisibleItem + visibleItemCount >= totalItemCount;
if (!mIsLoadingMore && loadMore
&& mCurrentScrollState != SCROLL_STATE_IDLE) {
mProgressBarLoadMore.setVisibility(View.VISIBLE);
// mLabLoadMore.setVisibility(View.VISIBLE);
mIsLoadingMore = true;
onLoadMore();
}
}
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
// bug fix: listview was not clickable after scroll
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
view.invalidateViews();
}
mCurrentScrollState = scrollState;
if (mOnScrollListener != null) {
mOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
public void onLoadMore() {
Log.d(TAG, "onLoadMore");
if (mOnLoadMoreListener != null) {
mOnLoadMoreListener.onLoadMore();
}
}
/**
* Notify the loading more operation has finished
*/
public void onLoadMoreComplete() {
mIsLoadingMore = false;
mProgressBarLoadMore.setVisibility(View.GONE);
}
/**
* Interface definition for a callback to be invoked when list reaches the
* last item (the user load more items in the list)
*/
public interface OnLoadMoreListener {
/**
* Called when the list reaches the last item (the last item is visible
* to the user)
*/
public void onLoadMore();
}
}
this is the custom List view for loading more items from server.you should use LoadMoreListView class for this work.and u can call this class in xml like this
<com.broadpeak.nvoice.utils.LoadMoreListView
android:id="#+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
lmlv = (LoadMoreListView) view.findViewById(R.id.lv);
lmlv.setOnLoadMoreListener(new OnLoadMoreListener() {
#Override
public void onLoadMore() {
}
});
if any query then you can contact freely....
The method you are trying to use is called LazyLoading
As a suggested way in Android development you can make use of recycler views and RecyclerAdapter for attaining this.
First define an Recycler adapter and views and
then,you can set an onScrollListener to the recycle adapter and it should go like (Original Source)
public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
public static String TAG = EndlessRecyclerOnScrollListener.class.getSimpleName();
private int previousTotal = 0; // The total number of items in the dataset after the last load
private boolean loading = true; // True if we are still waiting for the last set of data to load.
private int visibleThreshold = 5; // The minimum amount of items to have below your current scroll position before loading more.
int firstVisibleItem, visibleItemCount, totalItemCount;
private int current_page = 1;
private LinearLayoutManager mLinearLayoutManager;
public EndlessRecyclerOnScrollListener(LinearLayoutManager linearLayoutManager) {
this.mLinearLayoutManager = linearLayoutManager;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = mLinearLayoutManager.getItemCount();
firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
// like Load from server
current_page++;
onLoadMore(current_page);
loading = true;
}
}
public abstract void onLoadMore(int current_page);
}
As you can see you can make server requests at onScrolled method as commented which i think you can do with libraries like RetroFit or okHttp

How to store selected items of an ExpandableListView in an ArrayList

I have a list of users inside of an ExpandableListView, for now I have 2 groups of list, now I'm trying to create an ArrayList that will add data as I click on the users, so if have 2 groups of schools and I click on a student of each one I should have 2 positions in my array, one for each group containing its respective users, my problem is, my array has 2 positions but it is not separating the students:
What I want is this:
School A:
student1 selected
student2
student3 selected
School B:
student4
student5 selected
Resulting in this:
[0]-> student 1,3 [1] ->student 5
Here is what I tried so far:
mGpsEscolas = new GPSEscolas();
mArrayEscolas = new ArrayList<GPSEscolas>();
aMap = new HashMap<String, GPSEscolas>();
ExpandList.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
#Override
public boolean onChildClick(final ExpandableListView parent, View v, final int groupPosition, final int childPosition, final long id) {
ExpAdapter.setClicked(groupPosition, childPosition);
index = parent.getFlatListPosition(ExpandableListView.getPackedPositionForChild(groupPosition, childPosition));
parent.setItemChecked(index, true);
parent.setSelectedChild(groupPosition, childPosition, true);
parent.getChildAt(index);
IdAlunos = String.valueOf(mMainRest.mArrayList.get(groupPosition).getalunos().get(childPosition).getId_aluno());
IdEscola = String.valueOf(mMainRest.mArrayList.get(groupPosition).getId_escola());
ids_alunos.add(IdAlunos);
notificar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int groupCount = ExpandList.getExpandableListAdapter().getGroupCount();
for (int group = 1; group <= groupCount; group++) {
int gcount = ExpandList.getExpandableListAdapter().getChildrenCount(groupPosition);
mArrayEscolas = new ArrayList<GPSEscolas>();
for (int child = 1; child <= gcount; child++) {
mGpsEscolas.setIds_alunos(String.valueOf(IdAlunos).substring(1));
mGpsEscolas.setId_escola(Integer.valueOf(IdEscola));
mGpsEscolas.setLatitude(latitudeEscola);
mGpsEscolas.setLongitude(longitudeEscola);
mGpsEscolas.setDistancia(mMainRest.RaioEscola);
mArrayEscolas.add(mGpsEscolas);
if (ExpAdapter.isChildSelectable(groupPosition, childPosition)) {
aMap.put(ExpandList.getExpandableListAdapter().getChildId(group, child), mArrayEscolas);
}
}
}
}
});
return false;
}
});
An easy solution is to create a new class SelectableObject:
class SelectableObject<T>
{
boolean sel; T obj;
public SelectableObject<T>(T obj) { this.obj=obj; this.sel=false; }
public void select() { this.sel=true; }
public void deselect() { this.sel=false; }
public boolean isSelected() { return this.sel; }
public T getObject() { return this.obj; }
}
then create your ExpandableListView like this
public void setChildData()
{
ArrayList<SelectableObject<GPSEscolas>> child
= new ArrayList<SelectableObject<GPSEscolas>>();
child.add(new SelectableObject<GPSEscolas>(new GPSEscolas(..)));
..
childItems.add(child);
}
Then we need to make the onSelect listener function call select function
mExpandableList.setOnChildClickListener(new OnChildClickListener() {
#Override public boolean onChildClick(ExpandableListView parent,
View v,int groupPosition, int childPosition, long id) {
SelectableObject<GPSEscolas> item = (SelectableObject<GPSEscolas>)
parent.getExpandableListAdapter().getChild(groupPosition,childPosition);
if(!item.isSelected()) item.select();
else item.deselect();
..
})
Then we can query selected items like this
public static ArrayList<GPSEscolas> getSelectedChildren(ExpandableListView listView)
{
ArrayList<GPSEscolas> list = new ArrayList<GPSEscolas>();
int count = listView.getGroupCount();
for (int group = 1; group <= count; group++)
{
int gcount = listView.getChildrenCount(position);
for (int child = 1; child <= gcount; child++)
{
SelectableObject<GPSEscolas> item = (SelectableObject<GPSEscolas>)
listView.getExpandableListAdapter().getChild(groupPosition,childPosition);
// Here is where you can see the solution beauty
if (item.isSelected())
{
list.add(item.getObject());
}
}
}
return list;
}
There is one more way to go. Instead of creating wrapper object you can create Map<group,List<child>> selected items and add items to this list pretty much the same way, by listening this event:
mExpandableList.setOnChildClickListener(new OnChildClickListener() {
#Override public boolean onChildClick(ExpandableListView parent,
View v,int groupPosition, int childPosition, long id) {
//toggle selections code here
}
So here is important part: (and full working github repo )
#Override
protected void onCreate(Bundle savedInstanceState) {
...// some init part
final MyAdapter adapter = new MyAdapter(schools, students);
listView.setAdapter(adapter);
listView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
#Override
public boolean onChildClick(ExpandableListView parent,
View v, int groupPosition, int childPosition, long id) {
adapter.toggleSelection(groupPosition, childPosition);
adapter.notifyDataSetInvalidated();
return false;
}
});
}
We have several options where to put such selected items map, but in my project I use it in custom adapter class. There is no real need for custome adapter, and it is possible to put Map<G, List<C>> selectedItems; and related functions (toggleSelection, isSelected, getSelectedItems) in Activity, but we still need to highlight selected sells, so adapter usually is the best place to put it.
private class MyAdapter<G, C> extends BaseExpandableListAdapter {
private List<G> groups;
private Map<G, List<C>> childMap;
private Map<G, List<C>> selectedItems;
public MyAdapter(List<G> groups, Map<G, List<C>> childMap){
this.groups = groups;
this.childMap = childMap;
this.selectedItems = new HashMap<>();
}
public boolean isSelected(int groupPosition, int childPosition){
G group = groups.get(groupPosition);
// getChild is adapter Fn and is the same as
// G group = groups.get(groupPosition)
// C child = childMap.get(group).get(childPosition);
C child = getChild(groupPosition, childPosition);
List<C> sel = selectedItems.get(group);
return sel != null && sel.contains(child);
}
public void toggleSelection(int groupPosition, int childPosition){
G group = groups.get(groupPosition);
C child = getChild(groupPosition,childPosition);
List<C> sel = selectedItems.get(group);
if (sel == null){
sel = new ArrayList<>(); // Lasy arrays creation
//can init all arrays in constructor and never check for nulls
selectedItems.put(group, sel);
}
if (sel.contains(child)) sel.remove(child);
else sel.add(child);
}
... // Adapter fns can find in git repo
And convert result Map to List will be an easy task:
private ArrayList<String> selectedAsList(Map<String, List<String>> selectedItems){
ArrayList<String> result = new ArrayList<>();
for(List<String> students: selectedItems.values())
result.addAll(students);
return result;
}
or something similar.
PS. You can also play with Map<group,List<child>> .It can be pretty much any
data structure you want it to be. 2 arrays or maybe just 1 single array if you don't have duplicates in your group data. You can control it, limit the number of selections and so on...

Listview using Endless adapter

I have DB table with 10,000 rows which I want to display in the listview. I want to display first 20 and when the user scrolls down to the last item the next 20 should be loaded (and so on.). it really takes a lot of time to load all the datas in the listview so thats why i want it to load 20 datas first..
inside onCreate() Method the code is:
dbHelper = new WordDbAdapter(this);
dbHelper.open();
//Generate ListView from SQLite Database
displayListView();
then on the displayListView() method the code is like this:
#SuppressWarnings("deprecation")
private void displayListView() {
final Cursor cursor = dbHelper.fetchAllWords();
// The desired columns to be bound
String[] columns = new String[] {
WordDbAdapter.KEY_WORD,
WordDbAdapter.KEY_ROWID,
};
// the XML defined views which the data will be bound to
int[] to = new int[] {
R.id.Word,
R.id.imgStar,
};
// create the adapter using the cursor pointing to the desired data
//as well as the layout information
dataAdapter = new SimpleCursorAdapter(
this, R.layout.word_info,
cursor,
columns,
to
);
ListView listView = (ListView) findViewById(R.id.Diclist);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
listView.setOnScrollListener(new OnScrollListener(){
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int lastInScreen = firstVisibleItem + visibleItemCount;
if(cursor != null){
if(lastInScreen == totalItemCount && isLoadingMore == false){
isLoadingMore = true;
loadedPage ++;
new LoadWords().execute();
}
}
}
public void onScrollStateChanged(AbsListView view, int scrollState) {}
});
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> listView, View view,
int position, long id) {
// Get the cursor, positioned to the corresponding row in the result set
Cursor cursor = (Cursor) listView.getItemAtPosition(position);
// Get the word name from this row in the database.
String wordSelected =
cursor.getString(cursor.getColumnIndexOrThrow("word"));
String wordSyllabication =
cursor.getString(cursor.getColumnIndexOrThrow("syllabication"));
String wordPartofSpeech =
cursor.getString(cursor.getColumnIndexOrThrow("partofspeech"));
String wordMeaning =
cursor.getString(cursor.getColumnIndexOrThrow("meaning"));
String wordSpeak =
cursor.getString(cursor.getColumnIndexOrThrow("speakword"));
EditText TextDic = (EditText) findViewById(R.id.TextDic);
TextDic.setText(wordSelected);
speakMeaning = wordMeaning;
speakSyllabication = wordSyllabication;
speakPartOfSpeech = wordPartofSpeech;
speakWord = wordSpeak;
speakGetWord = wordSelected;
//Toast.makeText(getApplicationContext(),
// wordSyllabication + "\n" + wordPartofSpeech + "\n" + wordMeaning , Toast.LENGTH_SHORT).show();
}
});
EditText TextDic = (EditText) findViewById(R.id.TextDic);
TextDic.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
speakWord = "";
speakMeaning = "";
}
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
public void onTextChanged(CharSequence s, int start,
int before, int count) {
dataAdapter.getFilter().filter(s.toString());
}
});
dataAdapter.setFilterQueryProvider(new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
return dbHelper.fetchWordsByWord(constraint.toString());
}
});
}
then my AsyncTask is like this:
private class LoadWords extends AsyncTask<String, Void, Void> {
private final ProgressDialog dialog = new ProgressDialog(DictionaryActivity.this);
Cursor cursor = dbHelper.fetchAllWords();
#Override
protected void onPreExecute() {
this.dialog.setMessage("Loading books...");
this.dialog.show();
}
public void execute() {
// TODO Auto-generated method stub
}
#Override
protected Void doInBackground(String... arg0) {
try{
cursor = dbHelper.fetchAllWords();
}catch(Exception e){
e.printStackTrace();
}
return null;
}
#SuppressWarnings("deprecation")
#Override
protected void onPostExecute(final Void unused){
if(cursor != null){
if(dataAdapter == null){
startManagingCursor(cursor);
String[] columns = new String[] {
WordDbAdapter.KEY_WORD,
WordDbAdapter.KEY_ROWID,
};
int[] to = new int[] {
R.id.Word,
R.id.imgStar,
};
getListView().setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
dataAdapter = new SimpleCursorAdapter(DictionaryActivity.this, R.layout.word_info, cursor, columns, to);
ListView listView = (ListView) findViewById(R.id.Diclist);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
}else{
dataAdapter.notifyDataSetChanged();
}
}
if(dialog != null && dialog.isShowing()){
dialog.dismiss();
}
isLoadingMore = false;
}
private AbsListView getListView() {
// TODO Auto-generated method stub
return null;
}
}
The adapter doesn't load everything at once, and that should not be the reason you're seeing poor performance. ListView and SimpleCursorAdapter are fully capable of scrolling a list of only 10,000 items. The adapter only loads items as the user scrolls through the list. From the code that you've posted, I would say that your performance issues come from
dbHelper.deleteAllWords();
dbHelper.insertSomeWords();
If you post the code for these methods and dbHelper.fetchAllWords(), perhaps we can offer more help. Additionally, you can solve user interface problems by executing these long running tasks on a background thread (check out AsyncTask) and using a ProgressDialog to inform the user what is going on.
Take a look at Endless Adapter from the great Mark Murphy. It makes it really easy. You'll have your dataset that contains just the items you're displaying. In the adapter you can then tell it to grab the next set from your database and add it to the dataset.

Categories