月度归档:2016年06月

RadioGroup的一个bug

如果设置了默认选择,也没有给RadioButton添加ID,那么将没办法取消选择了。

<RadioGroup android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal">

    <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content"/>

    <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:checked="true"/>

</RadioGroup>

这个bug的原因:

RadioGroup在设置选择项的时候,RadioButton没有自动生成ID,导致选择项的ID为-1,等同于没选择。

创建流程:

  1. RadioButton创建
  2. RadioButton设置选中状态
  3. RadioGroup设置选中项(setCheckedId)
  4. RadioButton创建完成,添加到RadioGroup中

RadioButton创建的时候,回调onCheckedChanged()

private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener {
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        // prevents from infinite recursion
        if (mProtectFromCheckedChange) {
            return;
        }

        mProtectFromCheckedChange = true;
        if (mCheckedId != -1) {
            setCheckedStateForView(mCheckedId, false);
        }
        mProtectFromCheckedChange = false;

        int id = buttonView.getId();//这里是-1
        setCheckedId(id);
    }
}

RadioButton创建完成,添加到RadioGroup中

@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
    if (child instanceof RadioButton) {
        final RadioButton button = (RadioButton) child;
        if (button.isChecked()) {
            mProtectFromCheckedChange = true;
            if (mCheckedId != -1) {
                setCheckedStateForView(mCheckedId, false);
            }
            mProtectFromCheckedChange = false;
            setCheckedId(button.getId());
        }
    }

    super.addView(child, index, params);
}

// 这里才生成ID (在add/removeView的时候回调)
public void onChildViewAdded(View parent, View child) {
    if (parent == RadioGroup.this && child instanceof RadioButton) {
        int id = child.getId();
        // generates an id if it's missing
        if (id == View.NO_ID) {
            id = View.generateViewId();
            child.setId(id);
        }
        ((RadioButton) child).setOnCheckedChangeWidgetListener(
                mChildOnCheckedChangeListener);
    }

    if (mOnHierarchyChangeListener != null) {
        mOnHierarchyChangeListener.onChildViewAdded(parent, child);
    }
}

最简单的解决办法:

给RadioButton加个ID

如果是动态添加RadioButton:参考这里(stackoverflow.com)

App开发如何保持迭代速度

前提

  1. 固定人员投入
  2. 固定上班时间
  3. 产品功能确定,不考虑功能是否有前景

以上条件相同的情况下,如何保持迭代的速度(更快的发布给用户使用、更快的反馈改进)

App开发的特点

App开发,涉及多个角色的协作,客户端依赖所有端:产品端、服务端、设计端、测试端。App的一个业务界面,可以做1天,也可以做1周(5天)。完全取决于产品设计、服务端接口、交互和视觉。

以一个按钮(button)为例

社交类产品,有新好友通知列表,可以:添加、同意、邀请好友三种操作。
最简的方式:默认button
经过设计的:每个操作,都不同的颜色来区分。

来看看这两种方式工作量的差别:

按钮文字(1)文字颜色(2)按钮背景(2)宽(1)高(1)总计
默认Add/Accept/Invite默认默认默认默认1
AddAdd22默认15
AcceptAccept22默认15
InviteInvite22默认15

吓尿了,5*3/1 = 15 倍,效果也确实”好看”。
p_i_t

产品设计

  1. 约定迭代周期(比如:2周或者1个月)
  2. 对于要实现的功能,在迭代周期内进行拆分(这个能力非常重要:优先级和紧急程度,再重要的功能也是要有个排序)
  3. 最简产品规划,但为未来做预留,但不为未来而实现(比如:推送消息,为了避免骚扰用户,进行发送频率限制?)
  4. 迭代内,不要变更需求了

服务端接口

  1. 优先定义好接口,接口就是客户端的血液。
  2. 接口的稳定,接口的改变导致Android、iOS都需要修改,两倍工作量额。
  3. 接口的异常情况说明
  4. 为未来做架构,根据需要实现
  5. 开发环境与测试环境分离

交互和视觉

  1. 风格统一,不要随性设计
  2. 多借鉴,少抄袭
  3. 界面的交互和视觉,紧密结合产品特点(否则一个界面展示的内容,得调用好几个API,扯着蛋了)

客户端

  1. 多封装代码,方便使用(app本质就是数据、UI、每个界面共同的东西比较多)
  2. 对于View慢慢封装不变的东西,把变的东西做薄。
  3. 除了View控件,对于View的界面慎重复用

Agera官方Wiki翻译完成

Agera-Wiki-中文版:Agera Wiki 中文版

专有名词解释

由于一些专有的单词,翻译不出来,在这里统一解释,以便后文的理解。

Updatable(Observer)

翻译为:观察者
用途:接收事件通知,更新数据。
说明:观察者模式中的Observer,在Agera中使用Updatable

public interface Updatable {
void update();
}

Observable

翻译为:被观察者、事件源
用途:作为事件源,通知观察者更新数据,可以注册、注销观察者。
说明:当事件发生的时候,调用dispatchUpdate()通知观察者。

public interface Observable {
void addUpdatable(@NonNull Updatable updatable);
void removeUpdatable(@NonNull Updatable updatable);
}

Supplier

翻译为:数据提供者
用途:get()新数据。

public interface Supplier {
@NonNull
T get();
}

Repository

翻译为:Repository、数据仓库
用途:接收事件源、提供数据源的结合体。

public interface Repository extends Observable, Supplier {
}

Reactive programming

翻译为:响应式编程
说明:一种面向数据流和变化传播的编程范式。
这意味着在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
例如:在命令式编程环境中,a = b + c 表示将表达式的结果赋给a,而之后改变b或c的值不会影响a。但在响应式编程中,a的值会随着b或c的更新而更新。

Activation lifecycle

翻译为:激活生命周期
用途:注册观察者:数据仓库变为激活状态;注销观察者:数据仓库变为非激活状态。

Active

翻译为:激活状态

Inactive

翻译为:非激活状态

worker Looper thread

翻译为:有Looper的线程
说明:Agera 内部消息是通过Handler来完成的,所有线程需要有Looper,
更多参考:Android-Handler

Data Processing Flow

翻译为:数据处理流程

When, Where, What

翻译为:什么时候(时机),什么线程上,什么数据

Agera-Wiki-中文版:Agera Wiki 中文版