I have problem with select 2. It dont show all Items, but only subset. I dont see on Select2Choice any method, which show all items. Can someone give me a poit how to show whole items.
Here is code:
originStationDropDown = new Select2Choice<>("originDgfStation", new PropertyModel<Station>(this, "originStation") , new StationsProvider(originCountryDD, productDD));
ComponentHelper.addLabelAndComponent(originStationDropDown, this, "originStation.label", ComponentOptions.REQUIRED);
private class StationsProvider extends ChoiceProvider<Station> {
private Select2Choice<Country> countryDD;
private DropDownChoice<Product> productDD;
public StationsProvider(Select2Choice<Country> countryDD, DropDownChoice<Product> productDD) {
this.countryDD = countryDD;
this.productDD = productDD;
}
#Override
public void query(String codeNameFragment, int i, Response<Station> response) {
if(codeNameFragment == null || "".equals(codeNameFragment)) {
List<Station> stations = stationDao.findByCountryAndProduct(countryDD.getModel().getObject(), productDD.getModel().getObject(), "code");
for(Station station : stations) {
response.add(station);
}
} else {
response.addAll(stationDao.findByCountryAndProductAndFragment(countryDD.getModel().getObject(), productDD.getModel().getObject(), codeNameFragment));
}
System.out.println(response.size());
}
#Override
public void toJson(Station station, JSONWriter jsonWriter) throws JSONException {
jsonWriter.key("id").value(station.getId()).key("text").value(station.getNameWithCode());
}
#Override
public Collection<Station> toChoices(Collection<String> collection) {
List<Station> stations = new ArrayList<>();
List<Station> stationList = stationDao.findAll();
for(String id : collection) {
for(Station station : stationList) {
if(station.getId().equals(Long.valueOf(id))) {
stations.add(station);
}
}
}
return stations;
}
}
You don't explain which items are shown and which are not.
I will guess that only the first N items are always shown. The second parameter of #query() method is int page (named i in your code). This parameter should be used to paginate the results. I.e. you should not always return 10000 items and let the JavaScript to deal with them but you have to return 0-20, 21-40, 41-60, etc.
Related
I am trying to develop custom source for parallel GCS content scanning. The naive approach would be to loop through listObjects function calls:
while (...) {
Objects objects = gcsUtil.listObjects(bucket, prefix, pageToken);
pageToken = objects.getNextPageToken();
...
}
The problem is performance for the tens of millions objects.
Instead of the single thread code we can add delimiter / and submit parallel processed for each prefix found:
...
Objects objects = gcsUtil.listObjects(bucket, prefix, pageToken, "/");
for (String subPrefix : object.getPrefixes()) {
scanAsync(bucket, subPrefix);
}
...
Next idea was to try to wrap this logic in Splittable DoFn.
Choice of RestrictionTracker: I don't see how can be used any of exiting RestrictionTracker. So decided to write own. Restriction itself is basically list of prefixes to scan. tryClaim checks if there is more prefixes left and receive newly scanned to append them to current restriction. trySplit splits list of prefixes.
The problem that I faced that trySplit can be called before all subPrefixes are found. In this case current restriction may receive more work to do after it was splitted. And it seems that trySplit is being called until it returns a not null value for a given RestrictionTracker: after certain number of elements goes to the output or after 1 second via scheduler or when ProcessContext.resume() returned. This doesn't work in my case as new prefixes can be found at any time. And I can't checkpoint via return ProcessContext.resume() because if split was already done, possible work that left in current restriction will cause checkDone() function to fail.
Another problem that I suspect that I couldn't achieve parallel execution in DirectRunner. As trySplit was always called with fractionOfRemainder=0 and new RestrictionTracker was started only after current one completed its piece of work.
It would be also great to read more detailed explanation about Splittable DoFn components lifecycle. How parallel execution per element is achieved. And how and when state of RestrictionTracker can be modified.
UPD: Adding simplified code that should show intended implementation
#DoFn.BoundedPerElement
private static class ScannerDoFn extends DoFn<String, String> {
private transient GcsUtil gcsUtil;
#GetInitialRestriction
public ScannerRestriction getInitialRestriction(#Element String bucket) {
return ScannerRestriction.init(bucket);
}
#ProcessElement
public ProcessContinuation processElement(
ProcessContext c,
#Element String bucket,
RestrictionTracker<ScannerRestriction, ScannerPosition> tracker,
OutputReceiver<String> outputReceiver) {
if (gcsUtil == null) {
gcsUtil = c.getPipelineOptions().as(GcsOptions.class).getGcsUtil();
}
ScannerRestriction currentRestriction = tracker.currentRestriction();
ScannerPosition position = new ScannerPosition();
while (true) {
if (tracker.tryClaim(position)) {
if (position.completedCurrent) {
// position.clear();
// ideally I would to get checkpoint here before starting new work
return ProcessContinuation.resume();
}
try {
Objects objects = gcsUtil.listObjects(
currentRestriction.bucket,
position.currentPrefix,
position.currentPageToken,
"/");
if (objects.getItems() != null) {
for (StorageObject o : objects.getItems()) {
outputReceiver.output(o.getName());
}
}
if (objects.getPrefixes() != null) {
position.newPrefixes.addAll(objects.getPrefixes());
}
position.currentPageToken = objects.getNextPageToken();
if (position.currentPageToken == null) {
position.completedCurrent = true;
}
} catch (Throwable throwable) {
logger.error("Error during scan", throwable);
}
} else {
return ProcessContinuation.stop();
}
}
}
#NewTracker
public RestrictionTracker<ScannerRestriction, ScannerPosition> restrictionTracker(#Restriction ScannerRestriction restriction) {
return new ScannerRestrictionTracker(restriction);
}
#GetRestrictionCoder
public Coder<ScannerRestriction> getRestrictionCoder() {
return ScannerRestriction.getCoder();
}
}
public static class ScannerPosition {
private String currentPrefix;
private String currentPageToken;
private final List<String> newPrefixes;
private boolean completedCurrent;
public ScannerPosition() {
this.currentPrefix = null;
this.currentPageToken = null;
this.newPrefixes = Lists.newArrayList();
this.completedCurrent = false;
}
public void clear() {
this.currentPageToken = null;
this.currentPrefix = null;
this.completedCurrent = false;
}
}
private static class ScannerRestriction {
private final String bucket;
private final LinkedList<String> prefixes;
private ScannerRestriction(String bucket) {
this.bucket = bucket;
this.prefixes = Lists.newLinkedList();
}
public static ScannerRestriction init(String bucket) {
ScannerRestriction res = new ScannerRestriction(bucket);
res.prefixes.add("");
return res;
}
public ScannerRestriction empty() {
return new ScannerRestriction(bucket);
}
public boolean isEmpty() {
return prefixes.isEmpty();
}
public static Coder<ScannerRestriction> getCoder() {
return ScannerRestrictionCoder.INSTANCE;
}
private static class ScannerRestrictionCoder extends AtomicCoder<ScannerRestriction> {
private static final ScannerRestrictionCoder INSTANCE = new ScannerRestrictionCoder();
private final static Coder<List<String>> listCoder = ListCoder.of(StringUtf8Coder.of());
#Override
public void encode(ScannerRestriction value, OutputStream outStream) throws IOException {
NullableCoder.of(StringUtf8Coder.of()).encode(value.bucket, outStream);
listCoder.encode(value.prefixes, outStream);
}
#Override
public ScannerRestriction decode(InputStream inStream) throws IOException {
String bucket = NullableCoder.of(StringUtf8Coder.of()).decode(inStream);
List<String> prefixes = listCoder.decode(inStream);
ScannerRestriction res = new ScannerRestriction(bucket);
res.prefixes.addAll(prefixes);
return res;
}
}
}
private static class ScannerRestrictionTracker extends RestrictionTracker<ScannerRestriction, ScannerPosition> {
private ScannerRestriction restriction;
private ScannerPosition lastPosition = null;
ScannerRestrictionTracker(ScannerRestriction restriction) {
this.restriction = restriction;
}
#Override
public boolean tryClaim(ScannerPosition position) {
restriction.prefixes.addAll(position.newPrefixes);
position.newPrefixes.clear();
if (position.completedCurrent) {
// completed work for current prefix
assert lastPosition != null && lastPosition.currentPrefix.equals(position.currentPrefix);
lastPosition = null;
return true; // return true but we would need to claim again if we need to get next prefix
} else if (lastPosition != null && lastPosition.currentPrefix.equals(position.currentPrefix)) {
// proceed work for current prefix
lastPosition = position;
return true;
}
// looking for next prefix
assert position.currentPrefix == null;
assert lastPosition == null;
if (restriction.isEmpty()) {
// no work to do
return false;
}
position.currentPrefix = restriction.prefixes.poll();
lastPosition = position;
return true;
}
#Override
public ScannerRestriction currentRestriction() {
return restriction;
}
#Override
public SplitResult<ScannerRestriction> trySplit(double fractionOfRemainder) {
if (lastPosition == null && restriction.isEmpty()) {
// no work at all
return null;
}
if (lastPosition != null && restriction.isEmpty()) {
// work at the moment only at currently scanned prefix
return SplitResult.of(restriction, restriction.empty());
}
int size = restriction.prefixes.size();
int newSize = new Double(Math.round(fractionOfRemainder * size)).intValue();
if (newSize == 0) {
ScannerRestriction residual = restriction;
restriction = restriction.empty();
return SplitResult.of(restriction, residual);
}
ScannerRestriction residual = restriction.empty();
for (int i=newSize; i<=size; i++) {
residual.prefixes.add(restriction.prefixes.removeLast());
}
return SplitResult.of(restriction, residual);
}
#Override
public void checkDone() throws IllegalStateException {
if (lastPosition != null) {
throw new IllegalStateException("Called checkDone on not completed job");
}
}
#Override
public IsBounded isBounded() {
return IsBounded.UNBOUNDED;
}
}
I have followed below link on how to implement Contextual action bar with Recycler View https://stackoverflow.com/a/45911314/6117355...Can any one tell me how to perform some action like (edit/delete) operations on rows of Recyclerview on based of button actions of Contextual action bar in xamarin Android?
Can any one tell me how to perform some action like (edit/delete) operations on rows of Recyclerview on based of button actions of Contextual action bar in xamarin Android?
Firstly, you need to modify PhotoAlbum.cs's Photo[] to a List<Photo> to let it support delete/update operation:
public class PhotoAlbum
{
// Built-in photo collection - this could be replaced with
// a photo database:
...
// Array of photos that make up the album:
private List<Photo> mPhotos;
// Random number generator for shuffling the photos:
Random mRandom;
// Create an instance copy of the built-in photo list and
// create the random number generator:
public PhotoAlbum ()
{
mPhotos =new List<Photo>( mBuiltInPhotos);
mRandom = new Random();
}
// Return the number of photos in the photo album:
public int NumPhotos
{
get { return mPhotos.Count; }
}
// Indexer (read only) for accessing a photo:
public Photo this[int i]
{
get { return mPhotos[i]; }
}
// Add this Method to support update operation
public void UpdateAt(int position, Photo photo)
{
if (photo != null)
mPhotos[position] = photo;
}
public Photo GetAt(int position)
{
return mPhotos[position];
}
// Add this Method to support delete operation
public void RemoveAt(int position)
{
mPhotos.RemoveAt(position);
}
// Pick a random photo and swap it with the top:
public int RandomSwap()
{
// Save the photo at the top:
Photo tmpPhoto = mPhotos[0];
// Generate a next random index between 1 and
// Length (noninclusive):
int rnd = mRandom.Next(1, mPhotos.Count);
// Exchange top photo with randomly-chosen photo:
mPhotos[0] = mPhotos[rnd];
mPhotos[rnd] = tmpPhoto;
// Return the index of which photo was swapped with the top:
return rnd;
}
// Shuffle the order of the photos:
public void Shuffle ()
{
// Use the Fisher-Yates shuffle algorithm:
for (int idx = 0; idx < mPhotos.Count; ++idx)
{
// Save the photo at idx:
Photo tmpPhoto = mPhotos[idx];
// Generate a next random index between idx (inclusive) and
// Length (noninclusive):
int rnd = mRandom.Next(idx, mPhotos.Count);
// Exchange photo at idx with randomly-chosen (later) photo:
mPhotos[idx] = mPhotos[rnd];
mPhotos[rnd] = tmpPhoto;
}
}
}
Then, modify the longClick Event to accept an position param:
public class PhotoViewHolder : RecyclerView.ViewHolder
{
public ImageView Image { get; private set; }
public TextView Caption { get; private set; }
// Get references to the views defined in the CardView layout.
public PhotoViewHolder (View itemView, Action<int> listener,Action<object,View.LongClickEventArgs,int> longClickListener)
: base (itemView)
{
// Locate and cache view references:
Image = itemView.FindViewById<ImageView> (Resource.Id.imageView);
Caption = itemView.FindViewById<TextView> (Resource.Id.textView);
// Detect user clicks on the item view and report which item
// was clicked (by position) to the listener:
itemView.Click += (sender, e) => listener (base.Position);
ItemView.LongClick +=(sender,e)=> longClickListener(sender,e,base.Position);
}
}
Then start action mode with position param and inside OnActionItemClicked call the Remove function added to the Adapter:
void OnLongClick(object sender, View.LongClickEventArgs args,int position)
{
mActionMode = new MyActionMode(mActivity,this,position);
mode=mActivity.StartActionMode(mActionMode);
((View)sender).Selected = true;
return;
}
...
public class MyActionMode : Java.Lang.Object, ActionMode.ICallback
{
private Context mContext;
private PhotoAlbumAdapter mAdapter;
private int currentPosition;
public MyActionMode(Context context):this(context,null,0)
{
}
public MyActionMode(Context context, PhotoAlbumAdapter adapter,int position)
{
mContext = context;
mAdapter = adapter;
currentPosition = position;
}
public bool OnActionItemClicked(ActionMode mode, IMenuItem item)
{
switch (item.ItemId)
{
case Resource.Id.itemOneId:
// do Delete
mAdapter.RemoveAt(currentPosition);
mAdapter.FinishActionMode();
return true;
case Resource.Id.itemTwoId:
// do Update
return true;
default:
return false;
}
}
public bool OnCreateActionMode(ActionMode mode, IMenu menu)
{
mode.MenuInflater.Inflate(Resource.Menu.ContextualMenu, menu);
return true;
}
public void OnDestroyActionMode(ActionMode mode)
{
mode.Dispose();
}
public bool OnPrepareActionMode(ActionMode mode, IMenu menu)
{
return false;
}
}
I've committed the codes to demo project.
Notes: It is the same logic that can apply to the update operation.
The goal is to filter the tableView .So, when I input something to filter it works just fine , but then when I hit backspace or remove the inputs on the textField Area to go back,the tableView shows no content in the table ,Then I have to restart the program to reload and show the data.The data is saved and loaded from an xml file.
Also, I put an ObservaleList of contacts on the Data Class to load and store the contactList ,But on the Controller I have a similar list for the filter then the controller extends the data class to getContacts from there and add the filtredList to it. I am pretty much sure that the problem comes from that
public Data() {
contacts = FXCollections.observableArrayList();
}
public ObservableList<Contact> getContacts() {
return contacts;
}
This is below the filter handle
public void filterContactList(String oldValue, String newValue) {
ObservableList<Contact> filteredList = FXCollections.observableArrayList();
if (filterInput == null || newValue.length() < oldValue.length() || newValue == null){
contactsTable.setItems(getContacts());
}else {
newValue = newValue.toUpperCase();
for (Contact contact: contactsTable.getItems()){
String filterFirstName = contact.getFirstName();
String filterLastName = contact.getFirstName();
if (filterFirstName.toUpperCase().contains(newValue) || filterLastName.toUpperCase().contains(newValue)){
filteredList.add(contact);
}
}
contactsTable.setItems(filteredList);
}
}
and this is the init listener
filterInput.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
filterContactList((String) oldValue, (String) newValue);
}
});
Do yourself a favor an use FilteredList for this:
private FilteredList<Contact> filteredContacts = new FilteredList<>(getContacts());
...
contactsTable.setItems(filteredList);
...
public void filterContactList(String oldValue, String newValue) {
if (newValue == null) {
filteredContacts.setPredicate(null);
} else {
final String lower = newValue.toLowerCase();
filteredContacts.setPredicte(contact -> contact.getFirstName().toLowerCase().contains(lower) || contact.getLastName().toLowerCase().contains(lower));
}
}
BTW: since this is called from a listener to filterInput's textProperty, I removed the check for null.
i'm creating one application in which i get gift images with id's from web server through JSON. When i click on any gift image, it goes on next page where it shows all information of that image (get image information with its id from web server through JSON).
Problem is: When i click on any gift image on page to see its relevant information, it gets the last gift image id every time, i want when i click on any image, it gets the specific image id which i click. How it is possible??
Screenshot of the page is : http://ugo.offroadstudios.com/gifts.png
Here is sample code:
public class Gifts extends MainScreen {
String giftsid;
BitmapField giftimg;
public Gifts(){
setTitle("Gift Store");
creategifts();
}
public void creategifts()
{
//Link URL
String strURL = "http://ugo.offroadstudios.com/api/frndgift/?loginusername=adil;deviceside=true";
webConnection wb = new webConnection();
String res = wb.getJson(strURL);
try {
JSONObject object = new JSONObject(res);
if(object.getString("status") == "error")
{
Dialog.alert("Invalid "+object.getString("status"));
}
else
{
int totalgifts;
totalgifts = object.getInt("totalgifts");
Bitmap listThumb;
JSONArray imagearr;
JSONArray giftsidarr;
String imgname;
Bitmap bmpResized;
for(int i=0; i < totalgifts; i++){
imagearr = object.getJSONArray("gifts_image");
imgname = imagearr.getString(i);
giftsidarr = object.getJSONArray("gifts_id");
giftsid = giftsidarr.getString(i);
listThumb = getImage.getImageFromUrl("http://ugo.offroadstudios.com/wp-content/plugins/bp-gifts-rebirth/includes/images/"+imgname+";deviceside=true");
bmpResized = GPATools.ResizeTransparentBitmap(listThumb, 80, 80,
Bitmap.FILTER_LANCZOS, Bitmap.SCALE_TO_FIT);
giftimg =new BitmapField(bmpResized,FOCUSABLE)
{
protected boolean navigationClick(int status, int time)
{
Dialog.alert("giftsid "+giftsid);
UiApplication.getUiApplication().pushScreen(new SendGift(giftsid));
return true;
}
};
add(giftimg);
}
}
}
catch (JSONException e) {
System.out.println("EX is "+e);
e.printStackTrace();
}
}
}
You are always getting the gift id of the last gift in the list because you have created your buttons with this code:
giftimg =new BitmapField(bmpResized,FOCUSABLE)
{
protected boolean navigationClick(int status, int time)
{
Dialog.alert("giftsid "+giftsid);
UiApplication.getUiApplication().pushScreen(new SendGift(giftsid));
return true;
}
};
Your navigationClick() method used the giftsid variable, which is a persistent member variable of your class. You assign this variable in your for loop, so the final value it keeps is the last value assigned in the loop (giftsidarr.getString(totalgifts)).
Although you declare the navigationClick() method in a loop where the giftsid is many different values, the navigationClick() method uses the value of giftsid when it is run. The last value.
There's many ways to fix it. You can use a separate constant value in your loop:
final String nextGiftsId = giftsid;
giftimg =new BitmapField(bmpResized,FOCUSABLE)
{
protected boolean navigationClick(int status, int time)
{
Dialog.alert("nextGiftsId= "+nextGiftsId);
UiApplication.getUiApplication().pushScreen(new SendGift(nextGiftsId));
return true;
}
};
Or, as Signare suggested, attach a cookie to each button that identifies its corresponding gift:
giftimg =new BitmapField(bmpResized,FOCUSABLE)
{
protected boolean navigationClick(int status, int time)
{
String giftId = (String)getCookie(); // read gift id from the cookie
Dialog.alert("giftId= "+giftId);
UiApplication.getUiApplication().pushScreen(new SendGift(giftId));
return true;
}
};
giftimg.setCookie(giftsid); // set the cookie after creating the field
Inside your for loop, add the following code -
giftimg[i].setChangeListener(this);
Then -
public void fieldChanged(Field field, int context) {
for(int i=0;i<totalgifts;i++) {
if(field == giftimg[i]) {
// you can trigger your event
}
}
EDIT :-
giftimg[i].setChangeListener(listener);
listener = new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
if ( field instanceof BitmapField ) {
for(int i=0;i<totalgifts;i++) {
if ( field == giftimg[i] ) {
// you can trigger your event
}
}
}
}
};
I'm rolling my own ActivatableCollection<T> for db4o but cribbing heavily from the builtin ActivatableList<T> implementation. I'm running into the problem where transparent persistence doesn't seem to be working correctly. In the test code below:
[Fact]
void CanStoreActivatableCollection()
{
var planets = new ActivatableCollection<Planet>();
var pagingMemoryStorage = new PagingMemoryStorage();
var config = Db4oEmbedded.NewConfiguration();
config.Common.Add(new TransparentActivationSupport());
config.Common.Add(new TransparentPersistenceSupport());
config.File.Storage = pagingMemoryStorage;
var objectContainer = Db4oEmbedded.OpenFile(config, "Memory.yap");
planets.Add(new Planet("Mercury"));
objectContainer.Store(planets);
planets.Add(new Planet("Venus"));
planets.Add(new Planet("Earth"));
objectContainer.Commit();
objectContainer.Close();
config = Db4oEmbedded.NewConfiguration();
config.Common.Add(new TransparentActivationSupport());
config.Common.Add(new TransparentPersistenceSupport());
config.File.Storage = pagingMemoryStorage;
objectContainer = Db4oEmbedded.OpenFile(config, "Memory.yap");
planets = objectContainer.Query<ActivatableCollection<Planet>>().FirstOrDefault();
Assert.NotNull(planets);
Assert.Equal(3, planets.Count);
objectContainer.Close();
}
The planet "Mercury" is stored, but not "Venus" and "Earth". If I change from ActivatableCollection to ActivatableList, then all 3 planets are stored.
What am I missing? My ActivatableCollection is just minimal implementation of ActivatableList as best as I can tell.
Below is my implementation of ActivatableCollection:
public class ActivatableCollection<T>
: ICollection<T>
, IActivatable
, INotifyCollectionChanged
{
List<T> _list;
List<T> List
{
get
{
if (_list == null)
_list = new List<T>();
return _list;
}
}
public ActivatableCollection()
{
}
public int Count
{
get
{
ActivateForRead();
return List.Count;
}
}
public bool IsReadOnly
{
get
{
ActivateForRead();
return ((IList) List).IsReadOnly;
}
}
public void Add(T t)
{
ActivateForWrite();
List.Add(t);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, t));
}
public void Clear()
{
ActivateForWrite();
List.Clear();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public bool Contains(T t)
{
ActivateForRead();
return List.Contains(t);
}
public void CopyTo(T[] array, int index)
{
ActivateForRead();
List.CopyTo(array, index);
}
public IEnumerator<T> GetEnumerator()
{
ActivateForRead();
return List.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public bool Remove(T t)
{
ActivateForWrite();
bool removed = List.Remove(t);
if (removed)
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, t));
return removed;
}
[Transient]
private IActivator _activator;
public virtual void Bind(IActivator activator)
{
if (_activator == activator)
return;
if (activator != null && _activator != null)
throw new InvalidOperationException();
_activator = activator;
}
public virtual void Activate(ActivationPurpose purpose)
{
if (_activator == null)
return;
_activator.Activate(purpose);
}
protected virtual void ActivateForRead()
{
Activate(ActivationPurpose.Read);
}
protected virtual void ActivateForWrite()
{
Activate(ActivationPurpose.Write);
}
[Transient]
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
CollectionChanged(this, e);
}
}
I've also tried copying the code from GenericTypeHandlerPredicate and registering my ActivatableCollection to use the GenericCollectionTypeHandler. That results in a crash in GenericTypeFor() throwing an InvalidOperationException() when "Mercury" is being stored.
Just want to mention my answers from the db4o forums also here, for people with a similar problem:
First part of the issue:
From db4o's point of view nothing has changed in the 'ActivatableCollection' object and therefore no changes are stored. This is what is happening:
When you add the items, the ActivatableCollection is marked as changed.
When you commit the changes are stored. However the ' ActivatableCollection' holds the reference to the same object. db4o only stores the changes in the ActivatableCollection-object, which is the reference to the List. Since it is the same, no actual change is stored.
The List of the ActivatableCollection is never updated, because it wasn't marked as 'changed'
So the transparent activation doesn't see the changes in the list. You can fix your issue simply by using an ActivatableList in you're ActivatableCollection implementation. Just change the List with a IList interface and instantiate a ActivatableList instead of an List.
The second part of the issue: Why doesn't it work even when registering the GenericCollectionTypeHandler for this type? Here we hit a implementation detail. The GenericCollectionTypeHandler has an internal list of supported types, which doesn't include the self made 'ActivatableCollection'. GenericCollectionTypeHandler is not really part of the public API and intendet for internal use only.
Workaround / Fix
Just use an ActivatableList<T> instead of a List<T>. then everything works fine.