自学HarmonyOS应用开发(69)- 获取并表示手机目录结构

和文件存储比较起来,一般用户更关心的是文件系统的目录结构。本文介绍获取和表示目录结构的方法。先看演示视频:
File类
这是一个Java中的标准类,提供跨平台的文件访问功能。本文主要使用文件类的下面几个功能:
| 方法名 | 功能 |
| listRoots | 列举文件系统中根目录,Linux系统中只有一个根目录 |
| listFiles | 列举指定目录中的所有目录和文件 |
| isDirectory | 判断当前文件是否问目录 |
BrowserItem类
目录项的基类,功能是封装下图中每个目录项的功能。

BrowerItem类的代码如下:
public abstract class BrowserItem {interface ItemListener{public void changeDir(File dir);}String name;Context context;public BrowserItem(Context context, String name) {this.context = context;this.name = name;}//取得项目名public String getName() {return name;}//设定项目名public void setName(String name) {this.name = name;}//生成项目列表项abstract public Component createUiComponent();//生成详情表示画面abstract public void buildView(ComponentContainer container);}
这个类的主要功能有:
定义了一个接受项目选择状态变化的ItemListener类
实现了getName和setName方法
定义了生成列表项组件的接口createUiComponent。
定义了生成项目详细信息表示画面的接口buildView。
生成目录项表示组件
BrowerItemProvider为目录项生成表示组件时会调用createUIComponent方法:
public Component getComponent(int i, Component component, ComponentContainer componentContainer) {BrowserItem item = list.get(i);final Component cpt;if (component == null) {cpt = item.createUiComponent();} else {cpt = component;}Text text = (Text) cpt.findComponentById(ResourceTable.Id_item_name);text.setText(item.getName());return cpt;}
切换当前目录
在FileListContainer构建BrowserItemProvider时,会同时构建一个ItemListener并登录到BrowserItemProvider的实例上:
public FileListContainer(Context context, AttrSet attrSet) {super(context, attrSet);//...sampleItemProvider = new BrowserItemProvider(context, new BrowserItem.ItemListener() {public void changeDir(File dir) {setSelectedItemIndex(-1);sampleItemProvider.setCurrentDir(dir);setItemProvider(sampleItemProvider);}});setItemProvider(sampleItemProvider);setItemSelectedListener(itemSelectedListener);}
ItemListener收到当前目录切换的通知之后会进行以下处理:
清除FileListContainer的选择状态
指定BrowserItemProvider当前目录
为FileListContainer重新设置BroswerItemProvider以更新ListContainer的内容。
BroswerItemProvider的setCurrnetDir方法如下:
public void setCurrentDir(File dir) {list.clear();File[] files = dir.listFiles();if(files != null) {if(dir.getParent() != null){list.add(new ParentItem(context, dir.getParentFile(), itemListener));}for (File file : files) {if (file.isDirectory()) {list.add(new DirItem(context, file, itemListener));} else {list.add(new FileItem(context, file));}}}}
代码取得当前目录的子项目之后,分别构建返回上级目录项目,目录项目和文件项目。在构建返回上级目录项目和目录项目时会将ListContainer生成的ItemListener同时传递给这两种列表项。
返回上级目录列表项
当用户进入某一级目录后,最上面的列表项是返回上级目录项。点击它右侧的<<按钮,可以切换回上级目录。
public class ParentItem extends BrowserItem {File dir = null;ItemListener listener = null;public ParentItem(Context context, File dir, ItemListener listener) {super(context, dir.toString());this.dir = dir;this.listener = listener;}public Component createUiComponent(){Component comp = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_parent_item, null, false);Button back = (Button) comp.findComponentById(ResourceTable.Id_extend);if(listener != null && dir.listFiles() != null){back.setClickedListener(new Component.ClickedListener() {public void onClick(Component component) {listener.changeDir(ParentItem.this.dir);}});}return comp;}}
createUiComponent在构建表示组件时,为[<<]组件指定了onClick方法。在这个方法中会调用listener对象的changeDir方法,而这个changeDir就是FileListContainer构建BrowserItemProvider时指定的那个。
DirItem
目录列表项和ParentItem的不同之处有:
使用了不同的布局
点击右侧[>>时向下级目录迁移
迁移对象目录为空时,设置迁移按钮无效
具体代码如下:
public class DirItem extends BrowserItem {File dir = null;ItemListener listener = null;public DirItem(Context context, File dir, ItemListener listener) {super(context, dir.toString());this.dir = dir;this.listener = listener;}public Component createUiComponent(){Component comp = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_dir_item, null, false);Button extend = (Button) comp.findComponentById(ResourceTable.Id_extend);if(listener != null && dir.listFiles() != null){extend.setClickedListener(new Component.ClickedListener() {public void onClick(Component component) {listener.changeDir(DirItem.this.dir);}});}else{extend.setTextColor(Color.LTGRAY);}return comp;}}
FileItem
表示单个文件列表项的FileItem就简单了:
public class FileItem extends BrowserItem {File file = null;public FileItem(Context context, File file) {super(context, file.getName());this.file = file;}public Component createUiComponent(){return LayoutScatter.getInstance(context).parse(ResourceTable.Layout_file_item, null, false);}}
用户选择目录时更新详细信息表示画面的内容请参照前一篇文章,本文不再重复说明
参考资料
File
https://developer.harmonyos.com/cn/docs/documentation/doc-references/file-0000001054119759
ListContainer
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-java-component-listcontainer-0000001060007847
FileSystems
https://developer.harmonyos.com/cn/docs/documentation/doc-references/filesystems-0000001054238505
FileSystem
https://developer.harmonyos.com/cn/docs/documentation/doc-references/filesystem-0000001054558507
参考代码
完整代码可以从以下链接下载:
https://github.com/xueweiguo/Harmony/tree/master/FileBrowser
作者著作介绍
《实战Python设计模式》是作者去年3月份出版的技术书籍,该书利用Python 的标准GUI 工具包tkinter,通过可执行的示例对23 个设计模式逐个进行说明。这样一方面可以使读者了解真实的软件开发工作中每个设计模式的运用场景和想要解决的问题;另一方面通过对这些问题的解决过程进行说明,让读者明白在编写代码时如何判断使用设计模式的利弊,并合理运用设计模式。

对设计模式感兴趣而且希望随学随用的读者通过本书可以快速跨越从理解到运用的门槛;希望学习Python GUI 编程的读者可以将本书中的示例作为设计和开发的参考;使用Python 语言进行图像分析、数据处理工作的读者可以直接以本书中的示例为基础,迅速构建自己的系统架构。
觉得本文有帮助?请分享给更多人。
关注微信公众号【面向对象思考】轻松学习每一天!
面向对象开发,面向对象思考!
