在前四节中,我们实现了给List绑定数据,动态加载数据,处理List的一些事件,以及解决了List一些让用户感觉不舒服的默认显示问题,也许这样使用List已经符合我们的基本需求了,功能都能通过前面几节的内容实现。但是现在又有这样一个需求:用户希望能够通过改变选中项的Label颜色来标识这一项是选中的,可能这样说有点拗口。我举个例子,一个web页面中有很多超链接,当用户移上超链接时,超链接的颜色会改变,而手机用户同样希望达到这种效果。
前面的例子中,每个List的项都有两个Label,一个显示图片,一个显示名字,我们这里简单点,只对显示名字的Label的样式进行改变。
也许你认为,有了前面的例子,这实现起来有什么难度,只要获取Renderer类中的Label就可以了,然后你可能举出两种途径:
1、直接在SelectionListener事件中做选中的效果。
2、在ListCellRender中进行判断。
如果直接把前面的代码拿过来,然后通过上面两种途径是不能实现上面那种效果的。
首先,我证明一下为什么不能够通过上述两种方法实现那种效果。
途径1:
personList.addFocusListener(new FocusListener() { public void focusGained(Component c) { oldCon = (Renderer)(personList.getSelectedItem()); //当List获得焦点时,改变第一项的name颜色 oldCon.name.getStyle().setFgColor(0xCC0000); } public void focusLost(Component c) { } }); personList.addSelectionListener(new SelectionListener() { public void selectionChanged(int oldSelection, int newSelection) { //当更改List的选项时,把上一项的name改为默认颜色(黑色),把当前选中的改为红色 oldCon.name.getStyle().setFgColor(0x000000); newCon = (Renderer)personList.getSelectedItem(); newCon.name.getStyle().setFgColor(0xCC0000); oldCon = newCon; } });
其中oldCon,newCon被声明为字段,它们的类型是Renderer类型。
虽然上述代码在编译时是没有错误,但是在执行时却报错了。错误的原因在于:无法把personList.getSelectedItem()转换成Renderer类型,因为我们在createList方法中,传入的Model是Person数组,它只能转换成Person类型,这条路明显是行不通的。
途经2:
class Renderer extends Container implements ListCellRenderer { public Label name = new Label(""); public Label pic = new Label(""); public Label focus = new Label(""); public Renderer() { setLayout(new BorderLayout()); addComponent(BorderLayout.WEST, pic); name.getStyle().setBgTransparency(0); addComponent(BorderLayout.CENTER, name); } public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected) { Person person = (Person) value; name.setText(person.getName()); pic.setIcon(person.getPhoto()); //在这里做判断 if(isSelected){ name.getStyle().setFgColor(0xCC0000); }else{ name.getStyle().setFgColor(0x000000); } return this; } public Component getListFocusComponent(List list) { return focus; } }
注意红色部分的代码,看起来这一部分是没有逻辑错误,不仅编译通过,而且还能够执行,但是执行的结果却不是我们所期望的,它竟然把List中的所有name的颜色都改变了!具体原因:在这里面首先我们的返回值是this,this是什么,是Container,而那个boolean Selected的选中并不是针对Container的,而是Person,从途经1中我们就知道在createList的时候,我们的Model部分是Person数组,选中的状态与否都是针对Person类的。那一句name.getStyle().setFgColor(0xCC0000),List的每一项都有name,而Renderer类它本身根本就不知道这个name是属于哪个Container的,所以显示的结果就是所有的name颜色都会变成红色。
既然以上两种途经都不可行,但我们解决问题的思路仍然是:找到List中的每一项的Container,然后再找到Container中的name Label。关键的问题是以上两种途径的Model部分都是Person数组,而我们要获取的是Container,这样问题就迎刃而解了,我们何不把Container作为Model的部分呢?这样我们获取List的selectedItem的时候,取到的就是Container类型,取得Container中的Label就很容易了。class Renderer extends Container implements ListCellRenderer,既然要取得Container,那我们完全可以把这一个类拆分为两个类,一个类继承Container类(这是我们想要的,而且这个Container不是复合类型),另一个类继承ListCellRenderer接口。
然后看看实现这些的关键代码:
//模拟数据 public Person[] getContacts() { Person[] personArray = new Person[5]; Image photo = null; try { photo = Image.createImage("/smallphoto.jpg"); } catch (IOException ex) { ex.printStackTrace(); } for (int i = 0; i < personArray.length; i++) { personArray[i] = new Person("Sunny Peng" + (++start), photo); } return personArray; } //传入Person数组,然后创建一个ContactContainer数组 private ContactContainer[] createContainers(Person[] persons) { ContactContainer[] cons = new ContactContainer[persons.length]; for (int i = 0; i < cons.length; i++) { Person p = persons[i]; ContactContainer con = new ContactContainer(p); cons[i] = con; } return cons; } //这里虽然变化不大,但是Model变了,以ContactContainer数组作为Model private List createList(ContactContainer[] cons) { List list = new List(cons); list.setListCellRenderer(new ContactRenderer()); return list; } //Container类,作为Model class ContactContainer extends Container { private Label pic; private Label name; //传入Person数据,给Label赋值 public ContactContainer(Person p) { setLayout(new BoxLayout(BoxLayout.X_AXIS)); pic = new Label(p.getPhoto()); name = new Label(p.getName()); addComponent(pic); addComponent(name); } } class ContactRenderer implements ListCellRenderer { private Label focus = new Label(); public ContactRenderer() { } public Component getListCellRendererComponent(List list, Object value, int index, boolean selected) { //这个方法返回的不再是this了,而是ContactContainer return (ContactContainer) value; } public Component getListFocusComponent(List list) { return focus; } }
上面的Person类我没写,前面的例子中有。
然后再看看调用部分:
public ListForm(){ setLayout(new BorderLayout()); Person[] persons = getContacts(); final ContactContainer[] conArr = createContainers(persons); final List personList = createList(conArr); addComponent(personList); personList.addFocusListener(new FocusListener() { public void focusGained(Component c) { int index = personList.getSelectedIndex(); conArr[index].name.getStyle().setFgColor(0xCC0000); } public void focusLost(Component c) { } }); personList.addSelectionListener(new SelectionListener() { public void selectionChanged(int oldIndex, int newIndex) { conArr[oldIndex].name.getStyle().setFgColor(0x000000); conArr[newIndex].name.getStyle().setFgColor(0xCC0000); } }); }
效果图我就不发了。