0

我有一个树控件,每个节点旁边都有复选框,允许节点上的选中、未选中和中间选中状态。单击节点时,将更新父节点和子节点。我发现的代码使用位移,我试图了解到底发生了什么。

有人可以解释以下代码吗?或者更好的是,重写这段代码以便更容易理解?

// click event handler
private function eventMessageTree_itemCheckHandler(event:TreeEvent):void {
  var node:ITreeNode = ITreeNode(event.item);
  var checkState:uint = TreecheckboxItemRenderer(event.itemRenderer).checkBox.checkState;
  updateParents(node, checkState);
  updateChilds(node, checkState);
}

private function updateChilds(item:ITreeNode, value:uint):void {
  var middle:Boolean = (value & 2 << 1) == (2 << 1);
  var selected:Boolean = (value & 1 << 1) == (1 << 1);

  if (item.children.length > 0 && !middle) {
    for each (var childNode:ITreeNode in item.children)     {
      childNode.checked = value == (1 << 1 | 2 << 1) ? "2" : value == (1 << 1) ? "1" : "0";
      updateChilds(childNode, value);
    }
  }
}

private function updateParents(item:ITreeNode, value:uint): void {
  var checkValue:String = (value == (1 << 1 | 2 << 1) ? "2" : value == (1 << 1) ? "1" : "0");
  var parentNode:ITreeNode = item.parent;
  if (parentNode) {
    for each (var childNode:ITreeNode in parentNode.children) {
      if (childNode.checked != checkValue) {
        checkValue = "2";
      }
    }
    parentNode.checked = checkValue;
    updateParents(parentNode, value);
  }    
}
4

2 回答 2

2

看起来控件中的 checkState 值可以是 1、2 或 4(或者可能是 0、2 和 4?):

public static const CONTROL_UNCHECKED:uint = 1; // not checked, and some descendants are
public static const CONTROL_CHECKED:uint = 2; // checked, and all descendants are
public static const CONTROL_MIDDLE:uint = 4; // not checked, but some descendants are

而节点中的检查值可以是 0、1 或 2:

public static const UNCHECKED:uint = 0; // not checked, and some descendants are
public static const CHECKED:uint = 1; // checked, and all descendants are
public static const MIDDLE:uint = 2; // not checked, but some descendants are

这真是令人困惑。理想情况下,这些将是同一组常量。

更新:

private function controlStateToNodeState(value:uint):uint {
   return value / 2;
}
   ...
   updateParents(node, controlStateToNodeState(checkState));
   updateChilds(node, controlStateToNodeState(checkState));
   ...

/** Updates the descendants of the node based on state:
 *  If value is CHECKED, all children are CHECKED
 *  If value is UNCHECKED, all children are UNCHECKED
 *  If value is MIDDLE, children are left alone
 */
private function updateChilds(item:ITreeNode, value:uint):void {
   if (value == MIDDLE) {
      return;  // if value is MIDDLE, children are left alone
   }

   // not middle, so update all children to my state
   for each (var childNode:ITreeNode in item.children)     {
      childNode.checked = value;
      updateChilds(childNode, value);
    }
  }
}

/**
 * Updates the ancestor nodes based on state:
 * If value is CHECKED, ancestors are made MIDDLE if not already checked
 * If value is MIDDLE, ancestors are made middle (they should not already be CHECKED)
 */
private function updateParents(item:ITreeNode, value:uint): void {
    ...
}
于 2009-03-19T16:11:42.410 回答
1

基本上,像这样的表达式:

var middle:Boolean = (value & 2 << 1) == (2 << 1);

是反直觉的。您通常通过将常量 1 向左移动来测试位,因为这样可以使移动的位数与位的索引相同,将 LSB(最右边)位计算为位号 0。

此外,使用 == 比较来测试结果是没有意义的,因为它总是为 0 或非零,因此如果您的语言需要,您至少可以测试一些更简单的东西。

在默认情况下将非零整数解释为“真”的 C 和 C++ 中,比较完全没有必要,只会引入混乱、重复并增加错误的风险。

我会这样写:

var middle:Boolean = (value & (1 << 2)) != 0;

额外的括号应该有助于更清楚地说明事物是如何分组的。注意“2 << 1”是如何被改写为“1 << 2”的。这不仅仅是一个“开关”,您需要计算正确的移位以获得相同的位值,在这种情况下为 4。

当然你可以把位测试放到子程序中并调用它们,以使代码更具可读性。

于 2009-03-19T10:26:34.387 回答