有人可以解释这个位操作代码吗?

时间:2009-03-19 10:17:06

标签: flex actionscript-3 actionscript bit-manipulation

我有一个树形控件,每个节点旁边都有复选框,允许在节点上进行选中,取消选中和中间检查状态。单击节点时,将更新父节点和子节点。代码我发现这个技巧使用了位移,我试图理解究竟发生了什么。

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

// 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);
  }    
}

2 个答案:

答案 0 :(得分: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 {
    ...
}

答案 1 :(得分:1)

基本上,这样的表达式:

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

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

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

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

我会这样写:

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

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

当然,您可以将位测试放入子程序并调用它们,以使代码更具可读性。