I am developing an FMX (Android) app using a ListView in Delphi 10.2.3. I have the Listview live(bound) to a ClientDataSet with (Synch->*). This works very well, and any changes in Listview are propagated to the ClientDataSet, including the ClientDataSet's event handlers, such as BeforeUpdate, Post and AfterScroll.
Now when I move the record pointer in the ClientDataSet programmatically, the Listview does not synch with the change. It seems the Livebinding only works "one way" (from the UI to the Dataset).
How can I make the Listview follow the ClientDataSet, the way it does in the VCL when using a DataSource?
// here I expect the see the selected item start at the first item
// in the UI in index order and move quickly down through the
// list until it stops at the last one. This doesn't happen. The UI remains
// unaffected.
ClientModule.CDSData.First;
while not ClientModule.CDSData.Eof do
begin
ClientModule.CDSData.Next;
Sleep(100);
end;
The easy answer to this question is to perform a
if ClientModule.CDSData.Locate('PKID', VarArrayOf([PKIDValue]), []) then
It seems that while moving the record pointer using CDSData.Next doesn't sync back to the Live(Bound) Listview, using a locate does.
Related
I wrote a very simple FMX Adroid App, the function is:
Show Form 2 then write something to record(include title and detail text),
close Form 2 to Main Form, then make a checkbox in Main Form with the title we just recorded in Form 2.
if user check the checkbox, then press "del" buttn then delete the record file and checkbox.
the problem is:
when closed Form 2 and in MainForm::OnActivate we can add a new checkbox for the record.
if we checked checkbox then clicked delete, free the pointer of checked checkbox, the checkbox still in main form until I reopen the APP.
I tried:
Invalidate();
Application->ProcessMessages();
BeginUpdate();
EndUpdate();
Still can't work
does anyone know what's going on ? why FMX TForm member has no "Repaint()" or "Update()" "Refresh()" ? just like VCL has.
If you want your TCheckBox* (or any other control) disappear from a Form, you need to set its Parent property to nullptr before deleting it. If you created your control in runtime using new please remember to call delete.
//init
TCheckBox* checkBox = new TCheckBox(Form2);
//delete
checkBox->Parent = nullptr;
delete checkBox;
Answering the second part of your question, you can call Invalidate() function to repaint your whole Form (but first see first part of this answer). But I think it will run properly without calling this function.
Your controls have Repaint() member and it may be better to call them instead, ie. if your checkbox was placed in TPanel*, repainting only this panel is better idea than repainting whole form.
this is obviously too much data to display in an android app.. what can i do to mitigate this problem ?
I tried to show all 10000 rows in a list view but i got poor performance. I also tried to display all 10000 rows of data in the list view but that just made it slow
Why do you need that method ? WS method's approach is wrong. First of all, you must change method signature like this (int pageNumber, int recordNumber ) and make pagination enabled. If you use a listview, you can show the first 10 record. Then, if the user need more than that, could click to show more button.
I have an activity that extends ListActivity, a list of "concepts" (let's call this list "C") and an onItemClickListener defined for this list. Whenever I click a "concept", no matter which one, the app must display another list. I have the following code to change the displayed list:
if(position == 0) change_list("adapter1");
else if (position == 1) change_list("adapter2");
else if (position == 2) change_list("adapter3");
else if (position == 3) change_list("adapter4");
else if (position == 4) change_list("adapter5");
Where position is the position of the clicked element in C
The function change_list performs setListAdapter(parameter) depending on the parameter I pass.
If I click the first element of C (the first concept), a list related to the first concept must appear. However, after calling setListAdapter(adapter), the data related to this concept is displayed, and also part of the C's list data.
For example: let's suppose C has these concepts:
A B C D E
and I click "A", which would lead to display a list with the following data: {a1,a2}
That's the final result:
a1 a2 C D E
And then, when I interact with another element on screen or I scroll down the list, the "ghost" data disappears and only the correct data remains on screen, just like this:
a1 a2
To make things worse, when I want to display list C again, nothing strange happens. Everything is displayed correctly.
At any time incorrect data is stored where it doesn't have to. One function my app must allow is to generate a txt file , and the generated txt file contains exactly the data I introduced. No data is corrupted or duplicated. I also tried using notifyDataSetChanged() and other functions, but I didn't solve the problem.
EDIT :
Here goes the xml code used for the main list of the activity:
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#FF0000"
android:layout_below="#+id/afegir"/>
And an example of code in which I determine which contents must be displayed on screen:
else if(comprovar_concepte_actiu() == 1){
pnt = mydbhandler.getStoredValues("despeses1");
pnt.moveToFirst();
if(pnt.moveToFirst()){
do{
adapter_mostrar.add(pnt.getString(pnt.getColumnIndex("nom")));
}while(pnt.moveToNext());
}
adapter_mostrar.notifyDataSetChanged();
}
Where comprovar_concepte_actiu() returns and integer that tells which concept has been clicked in the main list C and adapter_mostrar is the single adapter I'm using now, instead of using multiple adapters (which made me use setListAdapter)
At the beginning of the activity, I call this.setListAdapter(adapter_mostrar). That's all I have.
EDIT 2 :
https://www.dropbox.com/s/7twgy043lkxb2x5/conceptes.java?dl=0
Here is a link to my conceptes.java activity. Press CTRL+F once opened and search "this is where I call.. " and you will directly get to the function where the change of list displayed on screen starts
I haven't found a solution yet. Any idea will be totally appreciated
The problem here is that - when you set a new adapter - the old data is still drawn. In other words, there has been no command to "refresh" the listView. However, the new adapter will be commanded to draw its own views. What ultimately occurs is that the old items are still there, the new items are redrawn, but when scrolled away the new adapter won't redraw/recreate the old items.
The solution is to simply refresh the adapter. However, there are two ways to go about this:
Add a new adapter every time and use myListView.invalidateViews(); or something similar [This is probably the easiest solution to implement, although probably not the best in the long run]
Change the dataset of the adapter and use notifyDataSetChanged() [on the adapter]
The latter option is a far better idea. You should use a single adapter and simply change its data over time. Once its dataset is changed, then tell the adapter that such a thing happened so it refreshes. However, you should read more here on all the different thoughts and processes about it, rather than take my opinion on it.
Edit:
There's apparently some very nicely, thought out answers around. Here's another one, that tells you more specifically about the differences between these two:
Is there any difference between ListView.invalidateViews() and Adapter.notifyDataSetChanged()?
Edit2:
With the onClickListener in mind, invalidateViews() will most likely not work, as it'll probably still draw the old views to "finish" the click (ie, draw the highlighting).
Changing the data directly inside a single adapter and using Adapter.notifyDataSetChanged() is your best bet, as it'll know to redraw everything from a single adapter and use only the current data defined by this single adapter.
Best to leave the data specifics (and defining what to draw based off of that data) up to what actually knows the data, rather than a higher up container that knows nothing specific about the actual data.
I have been developing an app that has a listview in which items may be removed by the user.
Now I have been trying to use the swipe to dismiss library in my project, by Roman Nurik: https://github.com/romannurik/Android-SwipeToDismiss.
I've successfully implemented a button which removes a row (click), however I am unable to use the swipe to dismiss functionality of the library (swipe). This is all because of the canDismiss check I need to do.
I have a cursor (see ContentProvider and LoaderCallbacks) which I was trying to iterate over and use to determine if a row can be dismissed. However this does not seem to work, 'cause when I fling fast/hard it displays the second to last row twice (it's normal position and the last position) and it does the same thing for the first and second rows.
Has anyone ever done something similar? And how did you solve this issue?
My code:
Cursor cursor = (Cursor) mAdapter.getItem(position);
if (cursor.getInt(Card.CARD_REMOVABLE_COLUMN_INDEX) == 1) {
return true;
}
But everytime I try to use the cursor, the issue seems to appear.
Alright, I guess there is no good solution, other than fetching the required information from the view(s) itself. Unlike with web development, getting the information from the database all the time, is not an option.
I guess you could get the information from the viewtag if you would so desire. I've done something different, using the position I get from the library, I lookup the row in the listview, and check if certain views are visible (every row can be removable, hence they all have the same header(s), I check if a header is visible or not, and handle this).
Currently, I'm using AsyncTask to handle Http connection and retrieve data as JSON format.
Loading all data is trivial but it consumes too much time, so I decided to switch to load 10 items at a time using LIMIT OFFSET (mysql).
Next I set up the event onScroll for my list view to create a new AsyncTask each time user scroll. However, from what I read, AsyncTask is stored in a thread pool which is limited 5 threads at a time, so I'm not sure this is a right approach. I'm newbie to client/server app, so could I anyone give me an advice on this issue? Any related article, documentation would be greatly appreciated.
Here are few useful links for it,
Android: Implementing progressbar and "loading..." for Endless List like Android Market
Endless Listview with current Async Task
Android Endless List
http://www.androidguys.com/2009/10/21/tutorial-autogrowing-listview/
http://mylifewithandroid.blogspot.com/2010/03/progressively-loading-listviews.html
In simple steps,
As user scrolls – detect the end of the list 1)Display a progress
notification 2)Ask for update 3)Receive update (asynchronously) and
extend list
A typical approach would be e.g. to load 25 initially and then have a footer in the list that displays e.g. the current count and the total count and upon pressing loads another 25 and so on. That would be a paged sort of loading.
When you do that you have to keep the current position and notify the adapter that the list has changed.
If you are using a ListView, I believe I can safely assume that you must be using some sort of ListAdapter. Instead of starting a new AsyncTask in the onScroll event, you should maintain just one single AsyncTask to retrieve data from the server, add that data to the ListAdapter dataset and then call notifyDatasetChanged on the ListAdapter.
The ListAdapter and ListView will take care of the rest.