zoukankan      html  css  js  c++  java
  • FBReaderJ学习笔记(二):PopWindow实现自定义阅读页菜单

      很少写技术博客,最常用的博客还是Lofter这个:chacePM。非技术博客。

      另外基于FBReaderJ本人开发了一个阅读器:悦读。有兴趣的朋友可以去安装看看,支持一下。

      以下是实现的菜单截图。

      MainMenuPopup

      

      ConfigPopup

      

      本文使用的是FBReaderJ-2.2.2.1

     注意:本文不涉及具体界面设计,只谈思路。

      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。

     1     public PopupWindow(Activity activity, RelativeLayout root, Location location) {
     2         super(activity);
     3         myActivity = activity;
     4 
     5         setFocusable(false);
     6 
     7         final LayoutInflater inflater =
     8             (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     9         final RelativeLayout.LayoutParams p;
    10         switch (location) {
    11             default:
    12             case BottomFlat:
    13                 inflater.inflate(R.layout.control_panel_bottom_flat, this, true);
    14 //                setBackgroundColor(FBReader.ACTION_BAR_COLOR);
    15                 p = new RelativeLayout.LayoutParams(
    16                     ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
    17                 );
    18                 p.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
    19                 myAnimated = true;
    20                 break;
    21             case Bottom:
    22                 inflater.inflate(R.layout.control_panel_bottom, this, true);
    23                 p = new RelativeLayout.LayoutParams(
    24                     ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
    25                 );
    26                 p.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
    27                 myAnimated = false;
    28                 break;
    29             case Floating:
    30                 inflater.inflate(R.layout.control_panel_floating, this, true);
    31                 p = new RelativeLayout.LayoutParams(
    32                     ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
    33                 );
    34                 p.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
    35                 myAnimated = false;
    36                 break;
    37             case Top:
    38                 inflater.inflate(R.layout.control_panel_bottom_flat, this, true);
    39 //                setBackgroundColor(FBReader.ACTION_BAR_COLOR);
    40                 p = new RelativeLayout.LayoutParams(
    41                     ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
    42                 );
    43                 p.addRule(RelativeLayout.ALIGN_PARENT_TOP);
    44                 if(android.os.Build.VERSION.SDK_INT>=11){
    45                     this.setY(getStatusBarHeight());
    46                 }
    47                 myAnimated = true;
    48         }
    49         p.addRule(RelativeLayout.CENTER_HORIZONTAL);
    50         root.addView(this, p);
    51 
    52         setVisibility(View.GONE);
    53 
    54         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    55             initAnimator();
    56         }
    57     }

      这里注意还有一个问题,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本人开发了一个阅读器:悦读。有兴趣的朋友可以去安装看看,支持一下。

  • 相关阅读:
    Axel linux下多线程下载工具
    使用Scala编写Spark程序求基站下移动用户停留时长TopN
    编写Spark的WordCount程序并提交到集群运行[含scala和java两个版本]
    使用Eclipse编译运行MapReduce程序 Hadoop2.6.0_Ubuntu/CentOS
    Eclipse上Hadoop插件中Run On Hadoop原理[转]
    apache官方中文hadoop说明文档地址
    如何在Windows下面运行hadoop的MapReduce程序
    通过web界面查看hadoop集群运行日志的地址
    linux命令-查看当前目录下所有文件的大小:“ll -h”
    BZOJ3979 : [WF2012]infiltration
  • 原文地址:https://www.cnblogs.com/chace/p/4279841.html
Copyright © 2011-2022 走看看