【问题标题】:RelativeLayout: align view to child of sibling viewgroupRelativeLayout:将视图对齐到同级视图组的子视图
【发布时间】:2012-01-01 13:16:04
【问题描述】:

我正在尝试使用 RelativeLayout 和 RadioGroup 在 android 中创建一个标签栏,该标签栏有一个指示器,该指示器将滑动以指示 RadioGroup 中的活动 RadioButton。

这些是自定义的单选按钮,实际上是带有图标的矩形,所有内容居中,背景使用自定义状态选择器。

这是我当前的层次结构:

<RelativeLayout>       // Main view of the tab bar
  <RadioGroup>         // The buttons
    <RadioButton />
    <RadioButton />
    <RadioButton />
  </RadioGroup>
  <ImageView />        // The indicator
</RelativeLayout>

我的想法是,当单击单选按钮时,我会将指示器与所选单选按钮的顶部和底部对齐(并为其设置动画)。但是,layout_align&lt;Edge&gt; 似乎只适用于兄弟元素,而不适用于另一个视图组的成员,即我可以与 RadioGroup 本身对齐,但不能与其中的 RadioButton 对齐。

我曾想过将指示器作为 RadioGroup 的成员,但由于 RadioGroup 是 LinearLayout 的扩展,似乎没有办法将它放置在给定 RadioButton 的边缘。

谁能想到我可以如何解决这个问题?或者也许有比我的动画对齐按钮技术更好的解决方案?

更新 在@superjos 的帮助下,我设法很好地解决了这个问题。

我不得不给每个按钮一个已知的高度,而不是使用wrap_content(不理想,但对于这个项目来说它应该可以正常工作)。使指示器高度与按钮高度匹配。确保 RadioGroup 设置为包裹内容并在父视图中垂直居中。将指标设置为 alignToptoRightOf RadioGroup。然后,对于按钮点击监听器:

int prevY, newY;
int prevButtonIndex, newButtonIndex;

public void moveIndicator(button, indicator, index) {
  prevY = newY;
  newY = prevY + (index - prevButtonIndex) * button.getHeight();
  TranslateAnimation animation = new TranslateAnimation(0, 0, prevY, newY);
  animation.setDuration(500);
  animation.setFillAfter(true); // stay at final animation position
  indicator.setAnimation();
  animation.start();
}

【问题讨论】:

  • 太棒了!而且实现看起来也不错!

标签: android android-relativelayout tabbar viewgroup


【解决方案1】:

这是我的想法。以下是一些更详细的步骤。

这个想法是让 ImageView 有一个 RadioGroup 兄弟,就像在您的示例中一样。 ImageView 最初位于 RadioGroup 的右侧,其水平中间线与第一个(或任何选择的)RadioButton 对齐。

然后,单击 RadioButton 时,会在运行时构建/配置 TranslateAnimation,以便让 ImageView 垂直移动,直到其中间线与单击的按钮对齐。动画使用 fillAfter 设置,以便在完成后保持稳定。

步骤:

  • 创建资源以使 ImageView 和 RadioButtons 具有相同的高度,或者使用垂直填充以使它们具有相同的高度。

  • 最初要将 ImageView 与第一个 RadioButton 对齐,一些 XML 可以 就足够了:在 ImageView 中将 layout_toRightOflayout_alignTop 附加到 RadioGroup。为了考虑 RadioGroup 的顶部填充,您可以将其添加到初始 ImageView 顶部填充 XML 属性。或者作为上边距更好。

  • 单击 RadioButton 时,创建将应用于 ImageVew 的 TranslateAnimation,仅将 Y 坐标从 0 更改为其目标 toY 值(而 from/toX 属性将为 0)。这是由一个公式给出的,例如:

    toY = (ClickedRadioButton.Index - LastClickedRadioButton.Index) * RadioButton.Heigth
    

    这里 Index 是 RadioButton 在组中的序号位置(例如从 0 到 2)(警告:这是伪代码,不一定是名为 Index)。

  • 将动画应用到 ImageView 并启动它。

【讨论】:

  • 在大多数情况下,这看起来可能是使用的解决方案。该算法在大部分情况下都有效(尽管需要设置setFillAfter(true) 才能使其粘住),但使用起始坐标 0 会使动画始终从 原始 起点开始,而不是当前起点。我试过使用getTop()getBaseline()getLocation()[1]。但是这些不起作用,并导致起点成为第一个或最底部的位置。知道如何对此进行调整吗?
  • 看起来问题仅源于绘图位置的变化。为了解决这个问题,我对指示器应用了实际的 layout_marginTop 更改,该指示器是动画完成时基于选中按钮的偏移量。这很好用,但唯一的问题是它在完成动画时会闪烁。有没有办法简单地动画布局参数的变化?
  • new property animation 框架,它允许您对许多(全部?)视图属性进行动画处理,一种 WPF/Silverlight 属性与动画的绑定。它从 API 11 开始就已经存在,但我从未使用过它。
  • 顺便说一句,您是否尝试在动画开始后立即更新 marginTop(或者可能是 paddingTop 作为替代),而它仍在运行? (不确定这是否完全拧紧了效果)。其他替代方法:不要从 Y=0 开始反复播放动画。存储最后一个动画 toY 值,并将其用作新动画 fromY 值。
  • 在调用 animate 后立即设置边距似乎立即设置,无需等待动画开始。如果我把它放在onAnimationStart() 中,它似乎做的事情大致相同。有趣的是,当另一个动画与这个动画大致同时触发时,闪烁变得不再是闪烁而更像是跳跃。它向下跳到下一个位置,然后回到正确的位置。
【解决方案2】:

您可以自定义 RadioButton 样式以包含一个 9 补丁版本的指示器图像作为选定(可能会检查无线电......)状态的背景。或者作为可绘制的

但是,这取决于您希望动画的外观。

例如。

<style name="TabRadioButton" parent="@android:style/Widget.CompoundButton.RadioButton">
    <item name="android:background">@drawable/tab_bg</item>
    <item name="android:drawableLeft">@drawable/tab_indicator</item>
</style>

【讨论】:

  • 指示器基本上是一个三角形,从一个边缘指向按钮的中心,当单击按钮时,它会从一个按钮边缘中心直线滑动到另一个按钮边缘中心。我不确定将它作为按钮本身的可绘制对象会给我带来这种效果。