Model - Represents the data for the component (list), the model can tell us exactly how many items are in it and which item resides at a given offset within the model. This differs from a simple Vector (or array) since all access to the model is controlled (the interface is simpler) and unlike a Vector/Array the model can notify us of changes that occur within it.
View - The view draws the content of the model. It is a "dumb" layer that has no notion of what is displayed and only knows how to draw. It tracks changes in the model (the model sends events) and redraws itself when it changes.
Controller - The controller accepts user input and performs changes to model which in turn cause the view to refresh.
LWUIT's List component uses the MVC paradigm to separate its implementation. List itself is the Controller (with a bit of View mixed in). The ListCellRenderer interface is a View and the ListModel is (you guessed it by now) the model.
When the list is painted it iterates over the visible elements in the model and asks for them, it then draws them using the renderer.
Why is this useful?
Since the model is a lightweight interface it can be implemented by you and replaced in runtime if so desired, this allows several very cool use cases:
1. A list can contain thousands of entries but only load the portion visible to the user. Since the model will only be queried for the elements that are visible to the user it won't need to load into memory a very large data set until the user starts scrolling down (at which point other elements may be offloaded from memory).
2. A list can cache efficiently. E.g. a list can mirror data from the server into local RAM without actually downloading all the data. Data can also be mirrored from RMS for better performance and discarded for better memory utilization.
3. No need for state copying. Since renderers allow us to display any object type, the list model interface can be implemented by the applications data structures (e.g. persistence/network engine) which would return internal application data structures saving you the need of copying application state into a list specific data structure.
4. Using the proxy pattern (as explained in a previous post) we can layer logic such as filtering, sorting, caching etc. on top of existing models without changing the model source code.
5. We can reuse generic models for several views e.g. a model that fetches data from the server can be initialized with different arguments to fetch different data for different views. View objects in different Form's can display the same model instance in different view instances thus they would update automatically when we change one global model.
Most of these use cases work best for lists that grow to a larger size or represent complex data which is what the list object is designed to do.
To show this off lets create a list with one million entries... What I am trying to prove here is that a list and a model have a very low overhead when used properly. Most of the overhead for rendering a list is in the renderer and the model implementation, both of which you can optimize to your hearts content. This is a very small price to pay for something as flexible, powerful and customizable as the LWUIT list!
class Contact {
private String name;
private String email;
private Image pic;
public Contact(String name, String email, Image pic) {
this.name = name;
this.email = email;
this.pic = pic;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public Image getPic() {
return pic;
}
}
class ContactsRenderer extends Container implements ListCellRenderer {
private Label name = new Label("");
private Label email = new Label("");
private Label pic = new Label("");
private Label focus = new Label("");
public ContactsRenderer() {
setLayout(new BorderLayout());
addComponent(BorderLayout.WEST, pic);
Container cnt = new Container(new BoxLayout(BoxLayout.Y_AXIS));
name.getStyle().setBgTransparency(0);
email.getStyle().setBgTransparency(0);
cnt.addComponent(name);
cnt.addComponent(email);
addComponent(BorderLayout.CENTER, cnt);
}
public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected) {
Contact person = (Contact) value;
name.setText(index + ": " + person.getName());
email.setText(person.getEmail());
pic.setIcon(person.getPic());
return this;
}
public Component getListFocusComponent(List list) {
return focus;
}
}
String[][] CONTACTS_INFO = {
{"Nir V.","Nir.Vazana@Sun.COM"},
{"Tidhar G.","Tidhar.Gilor@Sun.COM"},
{"Iddo A.","Iddo.Arie@Sun.COM"},
{"Ari S.","Ari.Shapiro@Sun.COM"},
{"Chen F.","Chen.Fishbein@Sun.COM"},
{"Yoav B.","Yoav.Barel@Sun.COM"},
{"Moshe S.","Moshe.Sambol@Sun.COM"},
{"Keren S.","Keren.Strul@Sun.COM"},
{"Amit H.","Amit.Harel@Sun.COM"},
{"Arkady N.","Arcadi.Novosiolok@Sun.COM"},
{"Shai A.","Shai.Almog@Sun.COM"},
{"Elina K.","Elina.Kleyman@Sun.COM"},
{"Yaniv V.","Yaniv.Vakrat@Sun.COM"},
{"Nadav B.","Nadav.Benedek@Sun.COM"},
{"Martin L.","Martin.Lichtbrun@Sun.COM"},
{"Tamir S.","Tamir.Shabat@Sun.COM"},
{"Nir S.","Nir.Shabi@Sun.COM"},
{"Eran K.","Eran.Katz@Sun.COM"}
};
int contactWidth= 36;
int contactHeight= 48;
int cols = 4;
Resources images = Resources.open("/images.res");
Image contacts = images.getImage("people.jpg");
Image[] persons = new Image[CONTACTS_INFO.length];
for(int i = 0; i < persons.length ; i++){
persons[i] = contacts.subImage((i%cols)*contactWidth, (i/cols)*contactHeight, contactWidth, contactHeight, true);
}
final Contact[] contactArray = new Contact[persons.length];
for (int i = 0; i < contactArray.length; i++) {
int pos = i % CONTACTS_INFO.length;
contactArray[i] = new Contact(CONTACTS_INFO[pos][0], CONTACTS_INFO[pos][1], persons[pos]);
}
Form millionList = new Form("Million");
millionList.setScrollable(false);
List l = new List(new ListModel() {
private int selection;
public Object getItemAt(int index) {
return contactArray[index % contactArray.length];
}
public int getSize() {
return 1000000;
}
public int getSelectedIndex() {
return selection;
}
public void setSelectedIndex(int index) {
selection = index;
}
public void addDataChangedListener(DataChangedListener l) {
}
public void removeDataChangedListener(DataChangedListener l) {
}
public void addSelectionListener(SelectionListener l) {
}
public void removeSelectionListener(SelectionListener l) {
}
public void addItem(Object item) {
}
public void removeItem(int index) {
}
});
l.setListCellRenderer(new ContactsRenderer());
l.setFixedSelection(List.FIXED_NONE_CYCLIC);
millionList.setLayout(new BorderLayout());
millionList.addComponent(BorderLayout.CENTER, l);
millionList.show();
0 comments:
Post a Comment