我在Android应用程序中使用了几个基于枚举的状态机.虽然这些工作非常好,但我正在寻找的是如何优雅地接收事件,通常是从已注册的回调或从事件总线消息接收到当前活动状态的建议.在有关基于枚举的FSM的许多博客和教程中,大多数都提供了使用数据的状态机(例如解析器)的示例,而不是展示如何从事件驱动这些FSM.
我正在使用的典型状态机具有以下形式:
private State mState;
public enum State {
SOME_STATE {
init() {
...
}
process() {
...
}
},
ANOTHER_STATE {
init() {
...
}
process() {
...
}
}
}
...
Run Code Online (Sandbox Code Playgroud)
在我的情况下,一些状态会触发一项特定对象的工作,注册一个监听器.该工作完成后,该对象将异步回调.换句话说,只是一个简单的回调接口.
同样,我有一个EventBus.希望再次通知事件的类实现回调接口以及listen()EventBus上的那些事件类型.
因此,基本问题是状态机或其各个状态,或包含枚举FSM的类,或某些东西必须实现这些回调接口,以便它们可以表示当前状态的事件.
我使用的一种方法是整个enum实现回调接口.枚举本身在底部具有回调方法的默认实现,然后各个状态可以覆盖他们感兴趣的事件的回调方法.为此,每个状态必须在进入和退出时注册和取消注册,否则在不是当前状态的状态下发生回调的风险.如果我找不到更好的东西,我可能会坚持这一点.
另一种方法是包含类来实现回调.然后,它必须通过调用将这些事件委托给状态机mState.process( event ).这意味着我需要枚举事件类型.例如:
enum Events {
SOMETHING_HAPPENED,
...
}
...
onSometingHappened() {
mState.process( SOMETHING_HAPPENED );
}
Run Code Online (Sandbox Code Playgroud)
我不喜欢这样,因为(a)我需要对每个状态中switch的事件类型进行丑陋,并且process(event)(b)传递其他参数看起来很尴尬.
我想建议一个优雅的解决方案,而不需要使用库.
我能找到的最接近的问题是Android Studio 3.0 lint警告,用于引用活动,但它没有帮助.
使用AndroidStudio 3.0.1,我有一个DialogFragment常用的东西:
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
...
Run Code Online (Sandbox Code Playgroud)
我有一条皮带警告呻吟着我Argument 'getActivity()' might be null.
我理解为什么 getActivity()可能为null,我理解lint检查如何知道这一点(来自@Nullable注释).
我的问题是:这一切都非常好,getActivity()可能是空的,但实际上我应该如何优雅和整洁地处理这个问题呢?onCreateDialog 必须返回一个Dialog(因为超类' @Nullable注释)所以我必须有Activity上下文来创建它.
我可以假设,onCreateDialog如果DialogFragment没有附加到活动,将永远不会被调用,但仍然 - 我如何解决不整洁的lint警告?
我知道色带是一个问题的老栗子,之前已经讨论了很多次提供了各种解决方案(基本上归结为使用32位,或使用抖动).事实上不久前我问过并随后回答了我自己的问题.当时,我还以为我把在回答这个问题的解决方案(这是应用setFormat(PixelFormat.RGBA_8888)的Window,也给Holder在的情况下SurfaceView),在我的很好的应用已经解决了这个问题.至少解决方案使得渐变在我当时开发的设备上看起来非常好(很可能是Android 2.2).
我现在正在使用HTC One X(Android 4.0)和华硕Nexus 7(Android 4.1)进行开发.我试图做的是在a的整个区域应用灰色渐变SurfaceView.即使我认为确保包含Window和Holder配置为32位颜色,我得到可怕的绑定工件.事实上,在Nexus 7上,我甚至看到了文物的移动.这不仅发生在SurfaceView当然连续绘制的情况下,而且还发生在View我正常添加的法线中,以便为测试目的绘制完全相同的梯度,这将绘制一次.这些工件的存在方式当然看起来非常糟糕,实际上就像观看信号较差的模拟电视一样.无论是View和SurfaceView展览完全相同的器物,它们一起四处走动.
我的目的是在整个过程中使用32位,而不是使用抖动.我的印象Window是默认情况下在Android 4.0之前默认为32位.通过应用RGBA_8888在SurfaceView我本来期望一切都已经32位全境,从而避免任何文物.
我注意到在SO上还有其他一些问题,人们观察到RGBA_88884.0/4.1平台上似乎不再有效.
这是我的Nexus 7的截图,View顶部和SurfaceView下面都有法线,两者都使用相同的渐变Canvas.当然,它不会像在显示屏上那样显示工件,因此显示此屏幕抓取可能毫无意义.我想强调的是,在Nexus的屏幕上,条带确实看起来很糟糕.编辑:其实,真正的截图没有显示的文物在所有.我在Nexus 7上看到的文物并不是统一的条带; 它看起来很随意.

Activity用于创建以上内容的测试:
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Shader;
import android.os.Bundle; …Run Code Online (Sandbox Code Playgroud) 很抱歉不得不问另一个position:fixed相关的问题,但阅读各种其他问题和论坛帖子并没有帮助我解决这个问题.
下面的代码简要演示了我迄今为止position:fixed在项目中的使用方式.我最初(错误)的理解position:fixed是它最初修复了相对于第一个定位的父容器,然后无论视口滚动位置如何都保持在该位置.我现在意识到这是错误的:事实上,position:fixed相对于最外面的容器(即html标签)的position:absolute位置和相对于具有不同位置的第一个父容器的位置static.
通过阅读关于SO的各种其他问题,我意识到我试图实现的效果position:fixed是许多其他人尝试过的效果,但后来意识到只用CSS就不可能:也就是说,相对于容器定位一个元素,但是当页面滚动时,它会保持在相对于视口的位置.
令我困惑的是,上面的内容正是我似乎已经实现的 - 至少在FF和IE8上.使用下面的代码示例,"固定的右窗格内容"最初位于红色"中心可滚动内容"框的右侧,并且与中心内容的顶部垂直对齐.中心内容可以滚动,但右手内容保持原样,就好像它最初在正常文档流中静态定位但此后保持固定在视口中.
我现在意识到这似乎在IE8和FF中"工作"只是因为我没有为元素指定top/ bottom/ left/ right属性fixed.如果我这样做,那么我意识到fixed元素会立即相对于视口定位.
我曾经假设 - 也许是危险的 - 直到现在如果没有指定相对位置,那么position:fixed默认情况下将该元素放置在通常静态放置的位置.至少FF和IE8似乎正在这样做.
但是,在Safari中进行测试表明Safari似乎将该fixed元素放在其容器的左侧.换句话说,没有定位,我的position:fixed元素既不是静态放置的位置,也不是相对于视口位于0,0.
到目前为止,我是否一直依赖于定义非常糟糕的行为,我最好是依靠JavaScript解决方案来实现这种固定定位?或者,这种行为是否为IE/FF定义良好; 有人可以解释Safari的位置背后的逻辑吗?
<style type="text/css">
#content-centre {
margin: 0 auto;
width: 900px;
}
#header {
height: 55px;
position: fixed;
top: 0;
width: 990px;
}
#left-pane {
position: fixed;
top: 12px;
border: 1px green solid; …Run Code Online (Sandbox Code Playgroud)我见过一些人问过如何一次性缩放整个ViewGroup(例如RelativeLayout).目前,这是我需要实现的目标.对我来说,最明显的方法是将缩放比例因子保持为某个变量; 并且在每个子视图的onDraw()方法中,该比例因子将在绘制图形之前应用于Canvas.
然而,在这之前,我试图聪明(哈 - 通常是一个坏主意)并将RelativeLayout扩展到一个名为ZoomableRelativeLayout的类中.我的想法是,任何比例转换只能在重写的dispatchDraw()函数中应用于Canvas,因此绝对不需要在任何子视图中单独应用缩放.
这是我在ZoomableRelativeLayout中所做的.它只是RelativeLayout的一个简单扩展,其中dispatchDraw()被覆盖:
protected void dispatchDraw(Canvas canvas){
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScaleFactor, mScaleFactor);
super.dispatchDraw(canvas);
canvas.restore();
}
Run Code Online (Sandbox Code Playgroud)
mScaleFactor由同一类中的ScaleListener操作.
它确实有效.我可以捏缩放ZoomableRelativeLayout,并将所有视图保持在一起正确重新缩放.
除了有问题.其中一些子视图是动画的,因此我会定期对它们调用invalidate().当比例为1时,可以看到这些子视图定期重绘完全正常.当比例不是1时,这些动画子视图只能在其观看区域的一部分中更新 - 或者根本不会更新 - 具体取决于缩放比例.
我最初的想法是,当一个人子视图的无效()被调用,那么它可能被单独由系统重新绘制,而不是传递从父的RelativeLayout的dispatchDraw()画布,这意味着子视图最终刷新自己没有应用缩放比例.但奇怪的是,在屏幕上重绘的子视图的元素是正确的缩放比例.这几乎就好像系统决定在后备位图中实际更新的区域仍然未缩放 - 如果这有意义的话.换一种方式,如果我有一个动画儿童观和我逐渐成为1的初始规模进一步和进一步放大,如果我们把一个假想的地方发现孩子的看法是该区域时,缩放比例为1 ,然后调用invalidate()只会导致刷新该虚构框中的图形.但是,这被视为更新显卡都正在做正确的规模.如果放大到目前为止,子视图现在已经完全远离它的比例为1,那么它的任何部分都不会被刷新.我将举一个例子:想象我的孩子视图是一个通过在黄色和红色之间切换来动画的球.如果我放大一点使得球向右和向下移动,在某一点你只会看到球的左上角四分之一的颜色.
如果我不断放大和缩小,我会看到孩子正确完整地观看动画.这是因为正在重绘整个ViewGroup.
我希望这是有道理的; 我试图尽我所能解释.使用我的可缩放ViewGroup策略,我有点失败吗?还有另外一种方法吗?
谢谢,
崔佛
android android-layout android-relativelayout android-viewgroup
我已经成功开始使用GSON来序列化和反序列化我的Android应用程序中的对象层次结构.
一些被序列化的对象具有我必须标记为的成员transient(或者使用替代的GSON注释以防止它们被序列化),因为它们是对我不想作为输出JSON字符串的一部分进行序列化的对象的引用.这些引用是指必须通过其他方式单独构造的对象.
一旦将结构反序列化为Java对象,在某些时候我需要填写这些引用.我可以通过使用一系列setXXX()类型方法轻松地做到这一点,但在此之前,这些对象处于不完整状态.因此,我想知道是否有更强大的方法.
到目前为止我想到的方式:
如果物体RuntimeException处于不完整状态,物体会抛出(或更合适的物体); 也就是说,如果在没有调用某些初始化方法时要求他们做一些工作.
将可序列化的位分离为单独的数据模型对象.换句话说,拿出无法序列化的东西.在GSON反序列化之后,使用组合中的这些数据对象构建我的"真实"对象.这似乎在某种程度上打败了使用GSON的便利性.
为GSON编写一个自定义反序列化器来处理这些对象的特殊创建.
注意:虽然到目前为止(9月6日)提供的两个答案很有意思,但遗憾的是它们没有解决这个问题.
我的一个Android测试设备是HTC One X.这个设备以频繁杀死后台应用程序(甚至包括启动器,最令人愤怒)而闻名,因为它在RAM分配方面往往生活在边缘,可能是由于HTC英国媒体报道.但是,就我的目的而言,这非常有用,因为它有助于突出各种低内存情况的影响,并允许我改进我的应用程序以应对此类事件.例如,我学到的一件事是,即使保留了Backstack,也可以杀死Application实例和其他static资源Activity.因此,为了提供良好的用户体验,即使运行应用程序的单个进程及其static拥有的所有内容都已经消失,依赖堆栈仍然可以保留.出于这个原因,我的应用程序现在非常坚固,在优雅地检查状态,并在必要时对重新初始化进行操作,恢复任何所需的"Singleton"数据Activity.
为了解决我的具体问题,我一直看到一种罕见的症状,通过代码检查,我认为只能由static一个类的成员被杀死然后重新初始化,而另一个类中的另一个静态资源我的类库也没有被重新初始化.我很欣赏两个独立static资源之间的这种依赖性代表了我的不良设计,我将重构以避免这种情况.但是,我想知道我的分析中是否可能是正确的 - 也就是说,是否可以在保留后台堆栈的情况下使用,但只有一些 static资源被杀死,特别是在每个库/包的基础上?
编辑1我将提供有关这两个类的更多信息.
1级是我们称之为的课程Controller.它不是用作Singleton,而是包含一个static Map在所有实例中都是通用的数据.它初始化如下:
private static Map<String, String> sSomeMetaData;
static {
sSomeMetaData = new HashMap<String, String>();
}
Run Code Online (Sandbox Code Playgroud)
接下来,我有一个叫做的课程MyFlyweightFactory.这堂课住在一个单独的图书馆.这个类是一个单身人士:
private static MyFlyweightFactory instance = new MyFlyweightFactory();
public static synchronized MyFlyweightFactory getInstance(){
return instance;
}
private MyFlyweightFactory(){ }
TreeMap<String, MyParserRenderer> images = new TreeMap<String, MyParserRenderer>(); …Run Code Online (Sandbox Code Playgroud) 我有一个SurfaceView扩展,其中的骨架实现在Lunar Lander示例中.也就是说,run()绘图Thread方法基本上是:
public void run() {
while (mRun) {
Canvas c;
try {
c = mSurfaceHolder.lockCanvas();
synchronized (mSurfaceHolder) {
doDraw(c); // Main drawing method - not included in this code snippet
}
}
finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Thread …
我有一个占据整个屏幕的背景图像.我在背景上绘制画布并将其颜色设置为白色,因此您无法看到图像.我想要实现的是然后在白色画布上绘制一个透明的形状,并通过该形状的位置显示背景图像.我正在使用surfaceView并实现SurfaceView.Callback.
在我的应用程序中,我有一个Service负责管理与外部设备的蓝牙连接.这个Service类定期轮询数据的外部蓝牙设备,并补充说,最新数据记录到日志高速缓存中保存(或可能SD卡)的内存.
在Activity我拥有的各种类中,有一个特定的Activity代表主UI.它负责以图形形式显示基于缓存文件数据的记录数据.让我们把这个Activity的Dashboard.用户可以在该图表上来回滚动,以查看自应用程序启动以来在缓存中收集和记录的数据.
出于这个问题的目的,有两种操作模式需要考虑.用户可以选择"登录到SD卡"选项,由此应用程序必须继续轮询并记录到SD卡,即使所有Activity类都被杀死(例如,用户已经返回到启动器).在这种情况下,我Service开始使用.startService()并继续运行,并且只有在用户再次调用应用程序并禁用SD卡日志记录时才会停止.另一种模式是用户没有选择"登录到SD卡",在这种情况下,Service仍在管理蓝牙连接,轮询和记录到高速缓存,以便在图形上直观显示数据,但只需要在使用时这样做Dashboard Activity.
我现在所拥有的是Dashboard Activity最初绑定到Serviceusing bindService(),并unbindService()在onPause()方法中进行相应的调用(否则我当然会泄漏Service).
问题是Service需要维护蓝牙连接并在方向更改期间或当用户Activity通过顶部调用另一个时(例如检查电子邮件)继续记录.现在,如果用户选择了"登录到SD卡"而导致startService()当时的呼叫,则没有问题.问题当然是如何区分Activity被销毁然后由于方向(或一些其他配置)变化而再次创建,并且因为用户返回到启动器而被销毁.在前一种情况下,我不希望Service数据记录被中断.在后一种情况下,如果用户没有选择"登录SD卡" ,我希望Service停止.
我现在能想到的最佳解决方案是始终使用服务startService(),以便Dashboard在销毁时继续运行.然后我会做的就是实现内超时什么Service,从而Service会自行停止,除非连续SD卡启用了日志记录,或将DashboardIS onCreated重新进行5秒(说),并重新绑定内Service.这似乎有点粗糙,我不禁想到这一定是一个常见的设计问题,有一个我忽略的更好的解决方案.
android ×8
java ×4
surfaceview ×2
bit-depth ×1
canvas ×1
colors ×1
css ×1
css-position ×1
enums ×1
fsm ×1
gson ×1
json ×1
lint ×1
transparent ×1