nighty

折腾的年华
posts - 37, comments - 143, trackbacks - 0, articles - 0

保存你的RCP视图状态

Posted on 2007-10-30 15:51 寒武纪 阅读(8663) 评论(0)  编辑  收藏 所属分类: Eclipse

    每当做开发的时候,你有可能在一次调试程序的过程中打开很多个编辑器,或是对Eclipse默认的视图布局不满意,手工作了一些调整。如果在工作的过程你因为有事离开或是不小心把Eclipse关掉了,不用担心,在你下次打开的时候,Eclipse仍然会为您记住上次的工作场景。下面是一个例子场景:
    
      Eclipse会记住“包资源管理器”的当前展开元素、被选中元素、视图大小、位置、过滤、排序等状态。不止于视图和编辑器,Eclipse还提供了对全局状态的持久化机制。包括向导,Action等
      GUI状态持久化不是必须的,只是一种可选的增强功能。一个完整的复杂的产品,特别是针对IDE类型的RCP,这个特征就显得比较人性化。
      下面介绍一些个人的实践心得,愿大家有所收获,主要是针对视图(IViewPart)。
       一般视图都是继承自ViewPart类或是实现IViewPart接口,在ViewPart中可以发现有
        

/**
     * Initializes this view with the given view site.  A memento is passed to
     * the view which contains a snapshot of the views state from a previous
     * session.  Where possible, the view should try to recreate that state
     * within the part controls.
     * <p>
     * This method is automatically called by the workbench shortly after the part 
     * is instantiated.  It marks the start of the views's lifecycle. Clients must 
     * not call this method.
     * </p>
     *
     * 
@param site the view site
     * 
@param memento the IViewPart state or null if there is no previous saved state
     * 
@exception PartInitException if this view was not initialized successfully
     
*/

    
public void init(IViewSite site, IMemento memento) throws PartInitException;

    
/**
     * Saves the object state within a memento.
     *
     * 
@param memento a memento to receive the object state
     
*/

    
public void saveState(IMemento memento);

    这二个方法就是实现界面状态持久化的关键,注意saveState方法是在IPersistable接口中定义的。这些我们暂时忽略,主要是看如何使用这些机制。
    如果去看JDT的实现或是org.eclipse.ui.ide一些视图的实现,你可以发现一些比较常用的实现形式。下面先看一段示例

    private IMemento fmemento;
    
    
/* 视图持久化状态标识 */
    
private static final String TAG_SELECTION = "selection"//$NON-NLS-1$
    private static final String TAG_EXPANDED = "expanded"//$NON-NLS-1$
    private static final String TAG_ELEMENT = "element";//$NON-NLS-1$
    private static final String TAG_PATH = "path"//$NON-NLS-1$
    
    
public void init(IViewSite site, IMemento memento) throws PartInitException {
        
super.init(site, memento);
        
this.fmemento = memento;
    }

    这是重写你自定义视图的init方法,定义一个全局的IMemento对象,初始化的时候为它赋值。并定义一系列的标识常量,用来表示你要保存的数据的标识。这一点与IDialogSettings是一致的。
    下面是保存视图实现代码,如果你第一次接触可能有点晕,不过没关系,熟悉了以后你会发现Eclipse的内部实现也是类似的。下面的代码就是从Eclipse里面改过来的。

/**
     * 保存视图状态
     * <p>在此没有过滤和排序功能,所以只要保存展开节点的状态和选中节点的状态就够了</p>
     
*/

    
public void saveState(IMemento memento) {
        System.out.println(
"保存视图");
         
//save visible expanded elements
        Object expandedElements[] = viewer.getVisibleExpandedElements();
        
if (expandedElements.length > 0{
            IMemento expandedMem 
= memento.createChild(TAG_EXPANDED);
            
for (int i = 0; i < expandedElements.length; i++{
                
if (expandedElements[i] instanceof ITestNode) {
                    System.out.println(
"展开元素 " +((ITestNode)expandedElements[i]).getPath());
                    IMemento elementMem 
= expandedMem
                            .createChild(TAG_ELEMENT);
                    elementMem.putString(TAG_PATH,
                            ((ITestNode) expandedElements[i]).getPath());
                }

            }

        }

        
//save selection
        Object elements[] = ((IStructuredSelection) viewer.getSelection())
                .toArray();
        
if (elements.length > 0{
            IMemento selectionMem 
= memento.createChild(TAG_SELECTION);
            
for (int i = 0; i < elements.length; i++{
                
if (elements[i] instanceof ITestNode) {
                    System.out.println(
"选中元素 " +((ITestNode)elements[i]).getPath());
                    IMemento elementMem 
= selectionMem
                            .createChild(TAG_ELEMENT);
                    elementMem.putString(TAG_PATH,
                            ((ITestNode) elements[i]).getPath());
                }

            }

        }

        System.out.println(
"保存视图完成");
    }

    要保存的数据是key--value形式,可以定义Boolean Integer Float String等基本类型对应的值,这些值就以key--value形式持久化到文件中。
    上面的实现内容其实就是把视图中的树组件中的二部分状态转换成数据:展开元素和被选中元素。通过把它们转换成String类型(其实就是树路径)保存起来。IMemento的具体用法请查找Eclipse帮助文档。
    完成了保存,下面我们看如何重新把数据取出来,使得打开的时候还原之前关闭时的状态。
    init()方法调用的时候界面控件是还没有被创建的,所以不能在init()方法中添加还原的方法,应用在控件都创建完成后还原。
    

/**
     * 创建视图的内容面板
     
*/

    
public void createPartControl(Composite parent) {
 ..
 
if (fmemento != null{
            System.out.println(
"开始还原");
            restoreState(fmemento);
        }

        fmemento 
= null;
 ..
}

    完成控件创建后就调用restoreState方法,这是我们自定义的用于还原视图状态的方法。下面看一下,内容基本上就是saveState方法的逆向。

    /**
     * 还原视图的状态
     * 
@param memento
     
*/

    
private void restoreState(IMemento memento) {
        System.out.println(
"还原视图");
        WorkSpaceNode wsn 
= (WorkSpaceNode)viewer.getInput();
        IMemento childMem 
= memento.getChild(TAG_EXPANDED);
        
if (childMem != null{
            ArrayList elements 
= new ArrayList();
            IMemento[] elementMem 
= childMem.getChildren(TAG_ELEMENT);
            
for (int i = 0; i < elementMem.length; i++{
                System.out.println(
"还原展开路径 " + elementMem[i].getString(TAG_PATH));
                Object element 
= NodeUtil.findNodeByPath(elementMem[i]
                        .getString(TAG_PATH), wsn);
                System.out.println(
"还原展开节点 " + element);
                
if (element != null{
                    elements.add(element);
                }

            }

            viewer.setExpandedElements(elements.toArray());
        }

        childMem 
= memento.getChild(TAG_SELECTION);
        
if (childMem != null{
            ArrayList list 
= new ArrayList();
            IMemento[] elementMem 
= childMem.getChildren(TAG_ELEMENT);
            
for (int i = 0; i < elementMem.length; i++{
                Object element 
= NodeUtil.findNodeByPath(elementMem[i]
                        .getString(TAG_PATH), wsn);
                
if (element != null{
                    list.add(element);
                }

            }

            viewer.setSelection(
new StructuredSelection(list));
        }

        System.out.println(
"还原视图完成");
    }

    这些工作就可以完成视图状态的保存与还原,还有一个特别要注意的地方,你必须对插件的WorkbenchAdvisor类的public void initialize(IWorkbenchConfigurer configurer)方法进行重写,添加configurer.setSaveAndRestore(true);这个方法就可以打开保存机制。如果没有这个步骤,上面的工作不会起任何效果。
    GUI状态的持久化都保存在运行时生成的.metadata\.plugins目录下的xml文件,你可以手动去打开这些文件查看数据结果是否跟你预期的一样。IEditorPart以及Perspective和其它的全局信息持久化的也是同样的原理。只是复杂度不一样。建议可以查看org.eclipse.ui.ide中的实现原理。

 



刚进场的时候戏就落幕

只有注册用户登录后才能发表评论。


网站导航: