ZLLibrary包下的core子包提供了要使用的各个抽象类,如ZLApplication。
其实例化了三个类,分别做数据库,图片,library的操作,这些类并未使用,但我们可以使用它们。
数据库,首先创建或读取数据库,如果是新建则根据版本进行版本兼容,初始化增删改查的语句以供使用。(此处是通过构造方法的方式进行初始化的)
Bug的处理
自己捕获异常,并处理,当发生无法捕捉的异常(如运行时异常时)会转到这个Handler执行。
Thread.setDefaultUncaughtExceptionHandler( new UncaughtExceptionHandler(this) |
这里的UncaughtExceptionHandler就是异常处理类(继承自同名类)
在这个里面,我们可以开启一个activity用于处理和显示页面
该Activity配置如下:
<activity android:name="org.geometerplus.android.fbreader.crash.FixBooksDirectoryActivity" android:configChanges="orientation|keyboardHidden" android:process=":crash" android:theme="@android:style/Theme.Dialog" > <intent-filter> <action android:name="android.fbreader.action.CRASH" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="CachedCharStorageException" /> intent-filter> activity> |
使用的是action+Uri的Intent方式,如果能够找到解决方案则进入Activity
如果不行,则进入BugReportActivity。
<activity android:name=".library.BugReportActivity" android:configChanges="orientation|keyboardHidden" android:label="FBReader crash" android:process=":crash" /> |
FBReader基础------------ZLAndroidActivity
该类是FBReader的父类,实现功能如下:
·转屏判断
·亮度判断
·电量判断
·wakeLock
从使用的View可以知道,该Activity指定的layout为R.layout.main,其主要操作的就是org.geometerplus.zlibrary.ui.android.view.ZLAndroidWidget
在org.geo.meterplus.zlibrary.ui.android.library包下,
进阶--FBReader
onCreate中设置占据屏幕大小,添加功能(action),添加3个popup到FBreaderApp。
onStart中检查屏幕大小是否符合,不符重开activity,
为Oncreate中添加的3个popup设置位置
ZLTreeResource树形资源
实现类似于树形XML的效果,能够加载资源
如errorMessage下的error
ZLResource.resource("errorMessage").getResource(“error”).getValue()
UIUtil UI提示工具类(提供Toast等提示)
一般来说我们对于这类小说阅读器应当有多种翻页以供使用,在本应用提供了四中翻页方式:none,curl,slide,shift
翻页方式的改变十分简单,只需要更改ScrollingPreferences.AnimationOption即可
其调用在ZLAndroidWiget中的getAnimationProvider方法中。
所以,如果我们想自定义一个翻页动画,可以通过如下几步完成:
1 自定义一个类继承自AnimationProvider
2 实现其方法(必要实现方法:
3 在ZLView的Animation枚举中添加自身标识
public static enum Animation { none, curl, slide, shift,my } |
4 在ScrollPreferences.AnimationOption中或在调用ZLAndroidWiget.getAnimationProvider()之前把该值改为Animation.my
5 在ZLAndroidWiget的getAnimationProvider()方法中switch方法判断后添加
case my: myAnimationProvider = new MyAnimationProvider(myBitmapManager); break; |
6 通过如上几步,就可以完成AnimationProvider的获取,接下来的使用工作由onDraw,onSizeChange等方法完成,无需我们参与了!
在该类中无时无刻都在画图,但画图分成两种,一种是静止状态画图,即没有在翻页中,正在浏览页面内容时的画图,另一种是流动状态画图,正在翻页途中的画图
通过AnimationProvider.inProgress()的返回Boolean值就可以知道当前是否处于静止或流动状态。
8 小说文本的显示
文本是通过画View位图的方式进行显示的,位图在通过BitmapManager类进行动态生成的,我们需要的就是指定要显示的文本以及页数,还有位图宽高。
/** * 静止中画 * @param canvas */ private void onDrawStatic(Canvas canvas) { //重新设置小说部分的宽高(主题部分的高是要减去底部 myBitmapManager.setSize(getWidth(), getMainAreaHeight()); //画出小说部分 canvas.drawBitmap(myBitmapManager.getBitmap(ZLView.PageIndex.current), 0, 0, myPaint); drawFooter(canvas); } |
如何翻页?
在onTouchEvent方法中获取点击位置的x,y值,如果是x值是从大到小,则说明是往前一页翻,如果x值是从小到大,则说明是往后一页翻。这样我们只需要传入不同的Mode即可。
所以我们需要在滚动动画结束后作出相应操作(如转到前页或后页)
要转页面可以调用ZLView的onScrollingFinished方法,该方法需要传入PageIndex。PageIndex有三个值current,pervious, next ,分别代表当前页面,前方页面,后方页面。
如下:ZLView.PageIndex.current
view.onScrollingFinished(ZLView.PageIndex.current); |
以上完成了翻页的工序,但是何时翻页呢?
需要在View.onTouchEvent()中进行判断。
在该方法中需要实现功能如下:
/**
* 滑动动作(重要)
* 1.点击控件下部中间 出现菜单
* 2.点击控件上部分中间出现导航(跳页的进度条)
* 3.点击左部分跳转前页,右部分跳转后页
* 4.滑动从左向右前页,否则后页
*/
具体计算方法,见代码ZLAndroidWiget.java。
代码详见ZLTextView。
在小说等作品中具有的文字是一页显示不完的,所以我们需要分页显示。每页的文字都显示在一个bitmap上,该bitmap的集合只需要保存2-3个,分别代表当前,前页,后页。
文字主要显示在ZLTextView上。所以当换页时我们需要在其上更换显示的文字,这三页分别都用ZLTextPage代表。
由ZLAndroidWiget我们知道,当页面更换时(哪怕没有换页成功【即滑动距离不足以换页】)都会调用ZLView的抽象方法onScrollingFinished方法。该方法判断当前的操作【是否需要换页,如果传入current则不换页,如果是next则换到下页】。
以下为其中传入pervious的部分代码。即要换到前页!
final ZLTextPage swap = myNextPage; myNextPage = myCurrentPage; myCurrentPage = myPreviousPage; myPreviousPage = swap; myPreviousPage.reset(); if (myCurrentPage.PaintState == PaintStateEnum.NOTHING_TO_PAINT) { preparePaintInfo(myNextPage); myCurrentPage.EndCursor.setCursor(myNextPage.StartCursor); myCurrentPage.PaintState = PaintStateEnum.END_IS_KNOWN; } else if (!myCurrentPage.EndCursor.isNull() && !myNextPage.StartCursor.isNull() && !myCurrentPage.EndCursor.samePositionAs(myNextPage.StartCursor)) { myNextPage.reset(); myNextPage.StartCursor.setCursor(myCurrentPage.EndCursor); myNextPage.PaintState = PaintStateEnum.START_IS_KNOWN; Application.getViewWidget().reset(); } |
可以发现首先把 当前换到后页,前页换到当前,为前页赋值后页,最后前页重置,并加载新的文本。
即ABC换成 XAB 【B为当前显示页,X为新加载页】
重新加载方式也很简单,调用preparePaintInfo
本部分是上面翻页的后续,从上面我们知道该小说是分成多个部分,每部分是做成bitmap进行显示的,每页都有自己的状态,正在显示的这页就是current,其前页就是pervious,后页就是next。而在其后页的后页,或前页的前页呢?通过观察ZLView内部类PageIndex发现,后页的后页为null,前页的前页也是null。即除了在显示页周围了2页其余皆为null
小说长点击和短点击的区分:
在View中有performLongClick()
点击下去,首先判断是否有短点击(之前),如果有,则去除,否则发送延迟消息判断是否为长点击。记录按下位置,设置触摸标识为true。
此时如果抬起,则说明
myPendingDoubleTap判断是否为双击
首先在ZLAndroidActivity及其子类的OnCreate中把需要的action通过fbReaderApp.addAction()的方式添加到FBReaderApp的map集合中。然后把想显示在该Activity上的菜单(就是刚刚addaction【此处actionid与之前addAction的必须相同】),添加到菜单中,并设置图标和名称以及设置点击事件,点击也十分简单,直接调用FBReaderApp的.doAction方法,该方法会自动查找map集合中是否存在该Action如果存在,则进行调用。
具体方式如下:
final FBReaderApp fbReader = (FBReaderApp)FBReaderApp.Instance(); fbReader.addAction(ActionCode.SHOW_LIBRARY,new ShowLibraryAction(this, fbReader)); |
@Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); addMenuItem(menu, ActionCode.SHOW_LIBRARY, R.drawable.ic_menu_library); return true; } |
FBReader下addMenuItem
private void addMenuItem(Menu menu, String actionId, int iconId) { final ZLAndroidApplication application = (ZLAndroidApplication)getApplication(); application.myMainWindow.addMenuItem(menu, actionId, iconId, null); } |
ZLAndroidWindow下addMenuItem:
public void addMenuItem(Menu menu, String actionId, Integer iconId, String name) { if (name == null) { //通过动作不同,获取该动作名称 name = ZLResource.resource("menu").getResource(actionId).getValue(); } final MenuItem menuItem = menu.add(name); if (iconId != null) { //为该菜单元素设置图标 menuItem.setIcon(iconId); } //设置点击事件 menuItem.setOnMenuItemClickListener(myMenuListener); myMenuItemMap.put(menuItem, actionId); } |
菜单监听:
private final MenuItem.OnMenuItemClickListener myMenuListener = new MenuItem.OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { //通过不同的动作名称做出不同的响应 //注意此处的getApplication()与Activity下的同名方法是不同的 //第二个获得的是应用的Application,第一个则是ZLApplication(自定义)的继承类 getApplication().doAction(myMenuItemMap.get(item)); return true; } }; |
FBReaderApp下doAction
public final void doAction(String actionId) { final ZLAction action = myIdToActionMap.get(actionId); if (action != null) { action.checkAndRun(); } } |
所以如果我们想自定义菜单,只需如下几部:
1自定义类,继承自FBAndroidAction,并实现run()方法
public class MyFirstAction extends FBAndroidAction {
MyFirstAction(FBReader baseActivity, FBReaderApp fbreader) { super(baseActivity, fbreader); } @Override protected void run() { System.out.println("我的菜单被点击"); Toast.makeText(BaseActivity, "我的菜单被点击", 1).show(); } } |
2为该Action设置一个独一无二的actionId,并写进ActionCode中
3在ZLAndroidActivity继承类中(Activity),获取FBReaderApp实例,并调用其addAction方法把该自定义动作加入
//我的菜单 fbReader.addAction("myMenu", new MyFirstAction(this, fbReader)); |
4 在菜单生成方法(OnCreateOptionsMenu)中通过addMenuItem(menu, ActionCode.SHOW_BOOKMARKS, R.drawable.ic_menu_bookmarks);
方法,传入动作actionId和图标图片id(如果没有则置空)
//添加我自己的菜单 addMenuItem(menu, "myMenu","我的菜单"); |
操作完毕!!
1.首先通过Context.openOrCreateDatabase的方式加载数据库文件 ,获取SqliteDatabase对象。
2.通过该对象的compileStatement方法,创建和编译SqliteStatement对象,该对象需要传递的参数可以用?代替
3.使用SqliteStatement,首先为?赋值,通过bindString,bindInt等方法可以完成。通过simpleQueryForString方法可以进行简单查询,并获取返回数据(复杂的不行),通过execute可以执行增加,修改,删除等不需要结果的操作。
void rotate() { View view = findViewById(R.id.main_view); if (view != null) { switch (getRequestedOrientation()) { case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: myOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; break; case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: myOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; break; default: if (view.getWidth() > view.getHeight()) { myOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; } else { myOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; } } setRequestedOrientation(myOrientation); } } |
根据当前宽高对比确定屏幕朝向,并改变。
运行如下代码即可:
此处的precent为1-100的值,当想让屏幕自定亮度,则attrs.screenBrightness可以传入-1f即可。
final WindowManager.LayoutParams attrs = getWindow().getAttributes(); attrs.screenBrightness = percent / 100.0f; getWindow().setAttributes(attrs); |
在实际应用中某个操作需要分成多个耗时操作执行,而这时我们一般需要一个进度对话框来显示进度。如正在登陆,正在加载列表····等等
所以可以建立一个Queue用于保存需要操作的信息
·每个耗时操作可以放到一个Runnable中执行,每个操作都有其自己的消息(如正在登陆)
所以可以建立一个Pair,用于保存这两份信息。
·执行一个Runnable完成后,才继续执行之后的任务,所以需要进行锁定
详情见QueueMessageUtil