FBReaderJ学习笔记(二):PopWindow实现自定义阅读页菜单
2015-02-08 19:21
453 查看
很少写技术博客,最常用的博客还是Lofter这个:chacePM。非技术博客。
另外基于FBReaderJ本人开发了一个阅读器:悦读。有兴趣的朋友可以去安装看看,支持一下。
以下是实现的菜单截图。
MainMenuPopup
ConfigPopup
本文使用的是FBReaderJ-2.2.2.1。
[b] 注意:本文不涉及具体界面设计,只谈思路。[/b]
1.结构
在正式动手改代码前,我们最好先搞清楚几个PopWindow的结构和继承关系,因为FBReaderJ的封装严密,容易走入死胡同。
整个跟popup相关的类结构大概就是这样,其中红色的为抽象类。命名很清楚,我就不解释每一个是干嘛的了。
我们要实现自定义popwindow菜单,可以模仿NavigationPopup的做法,写一个MainMenuPopup继承自PopupPanel。
2.原理
我们打开assets目录,发现在下面的目录里定义了tapzones。
打开down.xml,发现内容是这样的。这下明白阅读页面的点击事件是怎么回事了吧,其实就是把整个区域划分为九宫格,每个格子响应不同的事件。为了让我们的菜单显示,只要把这里的menu全改成navigate,再在代码里实现navigate响应事件为显示我们的自定义菜单。其实我们看到这里是少了(1,1)区域的,不知道是原作者为什么弄成这样,为了更好的体验,我们需要加上(1,1)也响应navigate。
3.两个PopWindow同时显示的解决方案
在我们的菜单里,是同时包含顶部菜单和底部菜单,如何解决这个问题呢?
一种直观的方式当然是弄两个菜单,BottomMenuPopup和TopMenuPopup都继承自PopupPanel,我最开始也是这么想的,但是问题就来了,PopupPanel的显示是用的Application.showPopup(ID)。代码如下,发现在show之前会先调用hideActivePopup(),这样我们show第二个Popup的时候会先把第一个Popup隐藏掉。
我们当然可以把这行去掉,但这会导致我们每次显示其他Popup的时候都得先调用hideActivePopup,出于少改动代码的目的,我放弃了这种方法。
第二种方法是单独弄一个TopMenuPopup而不继承PopupPanel,但是我尝试了一下后发现会更麻烦,什么时候显示什么消失一团糟。
最后我想到的方法是,在PopupPanel里面增加一个myWindowTop。这样我们就有两个PopupWindow,但是都显示在一个PopupPanel子类里,我们命名为MainMenuPopup。
4.myWindowTop的问题
PopupWindow的构造函数是这样的。
关键在locaiton。我们发现location其实是一个枚举,在PopupWindow的构造函数根据对应的值把PopupWindow显示在不同的位置。我们需要增加一个Top。
同时在构造函数里面也实现Top。
这里注意还有一个问题,44~46行代码是把myWindowTop往下移状态栏高度,防止退出全屏时被状态栏遮住。获取状态栏高度的代码如下:
在网上找到很多获取状态栏高度的方法,但是就这个是真的可以获取到,据说这里用到了JAVA的反射。
另外setY(int)这个方法是API11才有的,低版本如何解决,我还没找到方法。
5.切换PopupPanel
上面解决了MainMenuPopup的显示,但是其实我们是包含多个PopupPanel的,这就涉及到切换问题。我们以MainMenuPopup和NavigationPopup的切换为例。
下面的代码是用来显示NavigationPopup的,上面说过,在showPopup(ID)方法里会先调用hideActivePopup(),所以这里的切换不成问题。
另一个问题是显示和隐藏Popup,因为点击书本,对应的都是navigate()方法,所以我们需要判断点击时到底该显示还是隐藏。好在FBReaderJ已经有一个方法Application.getActivePopup()来获得当前显示的Popup,只要判断是否为空即可。所以在MainMenuPopup里面的showMainMenu()方法是这样的。
将FBReader类中的navigate()函数修改如下,就可以显示MainMenuPopup了。
6.结语
现在的结构就是这样了,增加了一个MainMenuPopup。
大致的流程就是更改tapzones->新建MainMenuPopup->新增myPopWindowTop->判断显示/隐藏popup。
到此思路基本已经理清了,开头就说过,本文不涉及具体界面开发,开头的截图仅作演示,千万不要有上当受骗的感觉。
实现自定义菜单的方式有很多种。比如顶部的菜单可以用Actionbar来实现,FBReaderJ的ics版本就是这样做的;或者在XML里面直接在ZLViewWidget外放上底部和顶部菜单。本文的方法只是一种。我说的不明白的地方,以及不足之处,欢迎与我探讨交流。
另外,基于FBReaderJ本人开发了一个阅读器:悦读。有兴趣的朋友可以去安装看看,支持一下。
另外基于FBReaderJ本人开发了一个阅读器:悦读。有兴趣的朋友可以去安装看看,支持一下。
以下是实现的菜单截图。
MainMenuPopup
ConfigPopup
本文使用的是FBReaderJ-2.2.2.1。
[b] 注意:本文不涉及具体界面设计,只谈思路。[/b]
1.结构
在正式动手改代码前,我们最好先搞清楚几个PopWindow的结构和继承关系,因为FBReaderJ的封装严密,容易走入死胡同。
整个跟popup相关的类结构大概就是这样,其中红色的为抽象类。命名很清楚,我就不解释每一个是干嘛的了。
我们要实现自定义popwindow菜单,可以模仿NavigationPopup的做法,写一个MainMenuPopup继承自PopupPanel。
2.原理
我们打开assets目录,发现在下面的目录里定义了tapzones。
打开down.xml,发现内容是这样的。这下明白阅读页面的点击事件是怎么回事了吧,其实就是把整个区域划分为九宫格,每个格子响应不同的事件。为了让我们的菜单显示,只要把这里的menu全改成navigate,再在代码里实现navigate响应事件为显示我们的自定义菜单。其实我们看到这里是少了(1,1)区域的,不知道是原作者为什么弄成这样,为了更好的体验,我们需要加上(1,1)也响应navigate。
3.两个PopWindow同时显示的解决方案
在我们的菜单里,是同时包含顶部菜单和底部菜单,如何解决这个问题呢?
一种直观的方式当然是弄两个菜单,BottomMenuPopup和TopMenuPopup都继承自PopupPanel,我最开始也是这么想的,但是问题就来了,PopupPanel的显示是用的Application.showPopup(ID)。代码如下,发现在show之前会先调用hideActivePopup(),这样我们show第二个Popup的时候会先把第一个Popup隐藏掉。
public final void showPopup(String id) { hideActivePopup(); myActivePopup = myPopups.get(id); if (myActivePopup != null) { myActivePopup.show_(); } }
我们当然可以把这行去掉,但这会导致我们每次显示其他Popup的时候都得先调用hideActivePopup,出于少改动代码的目的,我放弃了这种方法。
第二种方法是单独弄一个TopMenuPopup而不继承PopupPanel,但是我尝试了一下后发现会更麻烦,什么时候显示什么消失一团糟。
最后我想到的方法是,在PopupPanel里面增加一个myWindowTop。这样我们就有两个PopupWindow,但是都显示在一个PopupPanel子类里,我们命名为MainMenuPopup。
protected volatile PopupWindow myWindow; protected volatile PopupWindow myWindowTop;
4.myWindowTop的问题
PopupWindow的构造函数是这样的。
public PopupWindow(Activity activity, RelativeLayout root, Location location)
关键在locaiton。我们发现location其实是一个枚举,在PopupWindow的构造函数根据对应的值把PopupWindow显示在不同的位置。我们需要增加一个Top。
public static enum Location { BottomFlat, Bottom, Floating, Top }
同时在构造函数里面也实现Top。
public PopupWindow(Activity activity, RelativeLayout root, Location location) { super(activity); myActivity = activity; setFocusable(false); final LayoutInflater inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); final RelativeLayout.LayoutParams p; switch (location) { default: case BottomFlat: inflater.inflate(R.layout.control_panel_bottom_flat, this, true); // setBackgroundColor(FBReader.ACTION_BAR_COLOR); p = new RelativeLayout.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ); p.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); myAnimated = true; break; case Bottom: inflater.inflate(R.layout.control_panel_bottom, this, true); p = new RelativeLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ); p.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); myAnimated = false; break; case Floating: inflater.inflate(R.layout.control_panel_floating, this, true); p = new RelativeLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ); p.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); myAnimated = false; break; case Top: inflater.inflate(R.layout.control_panel_bottom_flat, this, true); // setBackgroundColor(FBReader.ACTION_BAR_COLOR); p = new RelativeLayout.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ); p.addRule(RelativeLayout.ALIGN_PARENT_TOP); if(android.os.Build.VERSION.SDK_INT>=11){ this.setY(getStatusBarHeight()); } myAnimated = true; } p.addRule(RelativeLayout.CENTER_HORIZONTAL); root.addView(this, p); setVisibility(View.GONE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { initAnimator(); } }
这里注意还有一个问题,44~46行代码是把myWindowTop往下移状态栏高度,防止退出全屏时被状态栏遮住。获取状态栏高度的代码如下:
private int getStatusBarHeight(){ Class<?> c = null; Object obj = null; Field field = null; int x = 0, sbar = 0; try { c = Class.forName("com.android.internal.R$dimen"); obj = c.newInstance(); field = c.getField("status_bar_height"); x = Integer.parseInt(field.get(obj).toString()); sbar = getResources().getDimensionPixelSize(x); } catch (Exception e1) { e1.printStackTrace(); } return sbar; }
在网上找到很多获取状态栏高度的方法,但是就这个是真的可以获取到,据说这里用到了JAVA的反射。
另外setY(int)这个方法是API11才有的,低版本如何解决,我还没找到方法。
5.切换PopupPanel
上面解决了MainMenuPopup的显示,但是其实我们是包含多个PopupPanel的,这就涉及到切换问题。我们以MainMenuPopup和NavigationPopup的切换为例。
下面的代码是用来显示NavigationPopup的,上面说过,在showPopup(ID)方法里会先调用hideActivePopup(),所以这里的切换不成问题。
((NavigationPopup)Application.getPopupById(NavigationPopup.ID)).runNavigation();
另一个问题是显示和隐藏Popup,因为点击书本,对应的都是navigate()方法,所以我们需要判断点击时到底该显示还是隐藏。好在FBReaderJ已经有一个方法Application.getActivePopup()来获得当前显示的Popup,只要判断是否为空即可。所以在MainMenuPopup里面的showMainMenu()方法是这样的。
public void showMainMenu(){ if (myWindow == null || myWindow.getVisibility() == View.GONE){ if(Application.getActivePopup()==null){ Application.showPopup(ID); }else{ Application.hideActivePopup(); } }else{ Application.hideActivePopup(); } }
将FBReader类中的navigate()函数修改如下,就可以显示MainMenuPopup了。
public void navigate(){ ((MainMenuPopup)myFBReaderApp.getPopupById(MainMenuPopup.ID)).showMainMenu(); }
6.结语
现在的结构就是这样了,增加了一个MainMenuPopup。
大致的流程就是更改tapzones->新建MainMenuPopup->新增myPopWindowTop->判断显示/隐藏popup。
到此思路基本已经理清了,开头就说过,本文不涉及具体界面开发,开头的截图仅作演示,千万不要有上当受骗的感觉。
实现自定义菜单的方式有很多种。比如顶部的菜单可以用Actionbar来实现,FBReaderJ的ics版本就是这样做的;或者在XML里面直接在ZLViewWidget外放上底部和顶部菜单。本文的方法只是一种。我说的不明白的地方,以及不足之处,欢迎与我探讨交流。
另外,基于FBReaderJ本人开发了一个阅读器:悦读。有兴趣的朋友可以去安装看看,支持一下。
相关文章推荐
- PopWindow动画实现底部滑出菜单
- Android的PopWindow动画实现底部滑出菜单
- 如何自定义菜单(点击菜单键,弹出一个popwindow)
- [置顶] Android:PopWindow — 对Android的底部弹窗、顶部弹窗菜单及自定义界面的使用封装
- 用view实现popwindow效果,弹出菜单.
- JS实现自定义右键菜单
- PopupWindow进阶用法——android上实现类似UCweb的自定义menu,完全模拟系统事件
- 在vb中实现真正锁定的带自定义菜单的文本控件
- 屏蔽FLEX右键菜单以及实现自定义的FLEX右键功能
- 在vb中实现真正锁定的带自定义菜单的文本控件
- JS实现自定义右键菜单
- VSTO Excel开发(二):完美实现自定义Excel菜单
- 给EditCtrl添加自定义菜单并实现Copy,Cut和Paste功能
- 【iPhone Demo】地图自定义大头针如何利用Android TabHost+ActivityGroup+Broadcast实现类似于iPhone的底部菜单
- IDocHostUIHandler::ShowContextMenu添加自定义菜单的另类实现
- c#+Winform实现自定义的“复制、粘贴”右键快捷菜单,多个控件共享使用一个右键菜单。
- Menu菜单(二)————利用GridView实现自定义的OptionMenu
- 屏蔽FLEX右键菜单以及实现自定义的FLEX右键功能
- javascript+css实现自定义网页右键菜单
- 图形的绘制,如何使用自定义画笔(颜色,线宽,线形)。如何为程序中添加选项菜单和选项设置对话框,如何使用标准颜色对话框,如何使用字体对话框,在选项对话框中实现预览功能。实现选项对话框和窗口类中的数据交换。如何改变对话框和控件的背景色,如何改变控件的文本颜色,