【问题标题】:Android Spinner Reveal Animation Material DesignAndroid Spinner 显示动画材料设计
【发布时间】:2015-07-15 05:41:30
【问题描述】:

我只需要一些非常简单的东西。 在网上找不到任何可以帮助我实现 following behaviour 的指南/资源:

所以有3件事:

  1. 似乎与视图绑定的波纹
  2. 按比例放大弹出窗口动画(找不到自定义方法)
  3. 文本从 Spinner 字段转移到实际弹出窗口

感谢任何帮助。

编辑

涟漪实际上是可以在文档中找到的直截了当的东西。弹出下拉菜单的放大动画是我最感兴趣的。如果我只能获得对该弹出窗口的引用,我可以为它制作任何我想要的动画......任何人的想法?

【问题讨论】:

  • 您能否详细说明 2(放大动画)的含义?我在视频中看不到这一点。
  • 微调器的弹出窗口从触摸点位置(或从编辑文本视图位置,取决于你如何看待它)有点放大

标签: android android-animation material-design


【解决方案1】:

在过去 >48 小时内,我一直在努力解决这个问题(我有一些额外的时间不要怪我),我可以肯定地告诉你这些:

  1. 使用 AppCompat 主题和普通的Spinner 为您提供免费的涟漪(耶!)。如果你有一个自定义的Spinner,请确保扩展AppCompatSpinner,你会得到一些很好的主题。引用AppCompatSpinner javadoc:

支持旧版本平台兼容功能的 Spinner,包括:

  • 允许通过 ViewCompat 中的背景着色方法对其背景进行动态着色。
  • 允许使用 backgroundTint 和 backgroundTintMode 设置背景色调。
  • 允许使用 popupTheme 设置弹出窗口主题。

这将在您在布局中使用 Spinner 时自动使用。您应该只需要在编写自定义视图时手动使用此类。

  1. 没有没有方法来检索通过扩展 ANY 类显示的PopupWindow。有很多嵌套类有助于该功能(剧透:它们大多是私有的)。

  2. 可以在您的主题中自定义PopupWindow...静态的进入和退出过渡(哇哦!)。我目前正在使用 AppCompat v23 并进行了一些调查,发现:

    • 自定义ListPopupWindowLOLLIPOP及以上的样式

      <style name="Widget.Material.ListPopupWindow">
          <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
          <item name="popupBackground">@drawable/popup_background_material</item>
          <item name="popupElevation">@dimen/floating_window_z</item>
          <item name="popupAnimationStyle">@empty</item>
          <item name="popupEnterTransition">@transition/popup_window_enter</item>
          <item name="popupExitTransition">@transition/popup_window_exit</item>
          <item name="dropDownVerticalOffset">0dip</item>
          <item name="dropDownHorizontalOffset">0dip</item>
          <item name="dropDownWidth">wrap_content</item>
      </style>
      
    • 在 LOLLIPOP 之前对其进行自定义的样式:

      <style name="Base.Widget.AppCompat.ListPopupWindow" parent="">
          <item name="android:dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
          <item name="android:popupBackground">@drawable/abc_popup_background_mtrl_mult</item>
          <item name="android:dropDownVerticalOffset">0dip</item>
          <item name="android:dropDownHorizontalOffset">0dip</item>
          <item name="android:dropDownWidth">wrap_content</item>
      </style>
      
    • 这就是您在主题中设置的内容:

      // From the constructor listPopupWindowStyle is the 
      // theme attribute you're looking for.
      public ListPopupWindow(Context context, AttributeSet attrs) {
          this(context, attrs, R.attr.listPopupWindowStyle);
      }
      

也就是说,我仍在获取您的详细信息,但我正在通过 克隆 2 个类来解决它:AppCompatSpinnerListPopupWindow 并添加一个公共 getPopup() getter到克隆的ListPopupWindow 代码,因此它可以在我的克隆AppCompatSpinner 中访问。这带来了几个机会。一个例子是我的centerPopup() 方法,它应该根据 spinnerItem(在微调器视图本身中显示的选定项目)和 spinnerDropdownItem(在落下)。然后在调用super.show() 之后立即在DropdownPopup.show() 中调用centerPopup() 方法

private void centerPopup(boolean updateListSelection) {
    boolean aboveAnchor = getPopup().isAboveAnchor();

    int[] spinnerLocation = new int[2];
    int[] popupLocation = new int[2];

    ListView listView = getListView();

    /*
        Popup is anchored at spinner TOP LEFT when it is set to overlap else at BOTTOM LEFT

        Also seems the windowlayoutparams generated for popup is wrapcontent for width and height
        As a result popup locationOnScreen returns [0, 0] which is relative to the window.
        We can take it as relative to spinner location and add spinnerLocation X value to give
        left edge of popup
     */

    MaterialSpinner.this.getLocationInWindow(spinnerLocation);
    int spMidX = spinnerLocation[0] + (MaterialSpinner.this.getWidth() / 2);
    int spMidY = spinnerLocation[1] + (MaterialSpinner.this.getHeight() / 2);

    Rect spinnerBgdPadding = new Rect();
    MaterialSpinner.this.getBackground().getPadding(spinnerBgdPadding);

    // ----- BUG - returns erroneous height
    // Ideally should measure one of the drop down list children and give a height
    // exactly as it wouldwhen laid out eventually.
    // Works only for lists with homogenously tall content
    View child = listView.getAdapter().getView(0, null, listView);

    child.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));

    child.measure(
            MeasureSpec.makeMeasureSpec(listView.getWidth() - listView.getPaddingLeft() - listView.getPaddingRight(), MeasureSpec.AT_MOST),
            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
    );
    int listItemHeight = child.getMeasuredHeight();
    // ----- /BUG

    getPopup().getContentView().getLocationInWindow(popupLocation);
    int popMidX = spinnerLocation[0] + (getPopup().getWidth()) / 2;
    int popMidY = spinnerLocation[1] + (listItemHeight / 2);

    int hoff = getHorizontalOffset();
    int voff = getVerticalOffset();

    int xoff = spMidX - popMidX - hoff - spinnerBgdPadding.left;
    int yoff = spMidY - popMidY - voff;

    getPopup().update(spinnerLocation[0] + xoff, spinnerLocation[1] + yoff, -1, -1, true);

    if (updateListSelection)
        listView.setSelectionFromTop(MaterialSpinner.this.getSelectedItemPosition(), 0)
    ;

    // getPopup().update(MaterialSpinner.this, xoff, yoff, -1, -1);
    // int numVisItems = listView.getLastVisiblePosition() - getFirstVisiblePosition();

}

如果返回了正确的 listItemHeight,弹出窗口不仅应该出现在微调器上,而且文本中心也应该对齐。 (由于背景和填充相关问题,可能会偏离一些像素,但可以解决)。

一旦居中,下一步就是将 dropDownList selectedPosition View 滚动到 popupWindow 的中心,并相应地更新 yoff Y 偏移量。

popupEnterTransition 属性仅限于LOLLIPOP,但如果这不是问题,您可以在此处插入放大转换,这应该可以获得您想要的效果。不过我还没有尝试过,但我也认为更好的动画应该是微调器中心的显示效果(看起来像你的规格视频中的那个)。

但所有这些都很好...理论上 x_x 哈哈

【讨论】:

  • 尊重你所做的工作。赞成。我仍然认为通过 ViewTreeObserver 检索弹出窗口是可能的,即使它是超级hacky。 developer.android.com/reference/android/view/… 。顺便说一句,您将在这里进行自定义,即使它们有点小。事实上,我最终实现了自己的弹出系统,它的行为如我所愿。但遗憾的是,谷歌没有直接的方法来实施自己的指导方针。
  • 是的,我知道它的定制,但我认为它比从头开始创建自己的弹出系统更简单。就是说,即使我没有探索其他方法,如果我亲自这样做,这就是我最初会做的事情,然后如果这不足(储备食物和能量饮料以及 x_x)尝试自己实现它.另外我不知道如何使用ViewTreeObserver.OnGlobalLayoutListener 获取弹出窗口
  • 理论上使用 ViewTreeObserver ,您可以尝试识别窗口层次结构中的新视图,并假设(或智能预测)新视图是您的弹出窗口。理论上...
  • 所以让我在这里猜测一下:在这种情况下,您将在单击微调器时将视图树观察者侦听器注册到Window。它会拾取刚刚添加到窗口的弹出窗口,您可以取消注册 vto?我想知道因为我相信 Popup 是在它自己的窗口中创建的
  • True :D 那是理论上的假设。
【解决方案2】:

1) 可以通过两种方式实现涟漪:一种是使用 CardView 作为每个列表项的父元素,另一种方法是创建一个选择器并将其设置为单个列表项的背景。

2) 您可以尝试使用元素转换来相应地为您的视图设置动画

3)您应该将您的文本作为额外数据从您的购物信息列表项传递到内部下拉列表,或者您只需保存并维护所选位置

您可能还想为此使用第三方库。 检查http://android-arsenal.com 找到一个!

【讨论】:

  • 理论上,你是对的。在实践中,我可以定制所有这些。问题是关于使用内置微调器小部件。
猜你喜欢
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 2015-07-28
  • 1970-01-01
  • 2015-12-07
  • 2019-04-11
  • 1970-01-01
  • 2021-11-07
相关资源
最近更新 更多