【问题标题】:Use of setVisibility(View.INVISIBLE) with pre Lollipop Circular Reveal library将 setVisibility(View.INVISIBLE) 与 pre Lollipop Circular Reveal 库一起使用
【发布时间】:2015-09-28 11:04:10
【问题描述】:

我正在做一个项目,我想根据 Material Design 使用 Circular Reveal 效果。项目的 minSDK = 11,所以为了与棒棒糖之前的设备兼容,我使用这个库https://github.com/ozodrukh/CircularReveal

我有一个带有 FloatingActionButton 的片段,当点击它时,它会在 CardView 中变换自身,就像这里描述的 FAB transformations

一旦卡片显示出来,它就会有一个按钮来恢复动画,将卡片重新转换为 FAB。现在我的问题是这样的:假设用户点击 FAB 并显示 CardView。现在用户旋转他的设备,因此活动重置片段。我想要实现的是卡片保持可见和显示,而 FAB 应该被禁用和不可见。问题是,如果我只是在我的 FAB 上使用setVisibility(View.INVISIBLE),它就不起作用(请注意,如果我在将其设置为不可见之后在其上使用getVisibility(),它会正确返回值 4 == View.INVISIBLE,但是晶圆厂仍然可见)。我必须将 setVisibility(...) 调用封装在 postDelayed() 中,延迟至少 50-100 毫秒,以使工厂不可见。

所以我的问题是:我做的事情是正确的还是有更好的方法来完成我想要的(因为它对我来说看起来很丑陋)?

这里有一些代码。这是我的片段的 XML 布局:

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/my_appbar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/toolbar_expanded_height"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:expandedTitleMarginEnd="64dp"
            app:expandedTitleMarginStart="70dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <android.support.v7.widget.Toolbar
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:popupTheme="@style/ToolbarPopupTheme"
                app:layout_collapseMode="pin"/>
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>
    ...
    <io.codetail.widget.RevealFrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <include layout="@layout/my_fragment" />
    </io.codetail.widget.RevealFrameLayout>
    <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        app:fabSize="mini"
        app:layout_anchor="@+id/my_appbar"
        app:layout_anchorGravity="bottom|left|start" />
</android.support.design.widget.CoordinatorLayout>

这是包含的 CardView 的 XML 布局:

<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="16dp"
    android:layout_gravity="center"
    card_view:cardCornerRadius="2dp"
    android:visibility="invisible"
    android:focusableInTouchMode="true">

    <RelativeLayout android:layout_width="match_parent"
        android:layout_height="match_parent">

        ...
        <LinearLayout android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="end">
            <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:text="Ok" />
            <Button android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Cancel" />
        </LinearLayout>
    </RelativeLayout>
</android.support.v7.widget.CardView>

这是我的 Fragment 的代码:

public class MyFragment extends Fragment {

    private static final String CARD_OPEN_TAG = "CARD_OPEN_TAG";

    public static MyFragment newInstance(){
        return new MyFragment();
    }

    private int cardOpen;
    private FloatingActionButton fabAddPublication;
    private CardView card;
    private Button cardCancel;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
        final View rootView = inflater.inflate(R.layout.fragment_magazines, container, false);
        toolbar = (Toolbar) rootView.findViewById(R.id.layout);

        ...

        // Initialize view status
        if (savedInstanceState != null){
            cardOpen = savedInstanceState.getInt(CARD_OPEN_TAG);
        } else {
            cardOpen = -1;
        }

        ...

        // Get FAB reference
        fab = (FloatingActionButton) rootView.findViewById(R.id.fab_id);
        // Get card reference
        card = (CardView) rootView.findViewById(R.id.card_id);
        editorPublication.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                // Using this event because I need my card to be measured to move correctly fab at his center
                if (cardOpen != -1){
                    // Move FAB to center of card
                    fab.setTranslationX(coordX); // WORKS
                    fab.setTranslationY(coordY); // WORKS
                    // fab.setVisibility(View.INVISIBLE) -> DOESN'T WORK, fab remain visible on top and at center of my card
                    // Ugly workaround
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            // Hide FAB
                            fab.setVisibility(View.INVISIBLE);
                        }
                    }, 50); // Sometimes fails: if device/emulator use too much time to "rotate" screen, fab stay visible
                    // Remove listener
                    ViewTreeObserver obs = card.getViewTreeObserver();
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
                        obs.removeOnGlobalLayoutListener(this);
                    else obs.removeGlobalOnLayoutListener(this);
                }
            }
        });
        if (editorOpen != -1){
            fab.setEnabled(false); // WORKS
            card.setVisibility(View.VISIBLE); // WORKS
        }
        // Get editors buttons reference
        cardCancel = (Button) card.findViewById(R.id.card_cancel_id);
        // Set FAB listener
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Explode FAB
                explodeFab(fab, card); // This method trigger the reveal animation
                cardOpen = card.getId();
            }
        });
        // Set editors button listeners
        cardCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Implode FAB
                implodeFAB(fab, card); // This card reverts the reveal animation
        cardOpen = -1;
            }
        });

        ...

        return rootView;
    }

    @Override
    public void onSaveInstanceState(Bundle outState){
        super.onSaveInstanceState(outState);
        ...
        outState.putInt(CARD_OPEN_TAG, cardOpen);
    }

}

【问题讨论】:

    标签: android android-fragments material-design circularreveal


    【解决方案1】:

    我不认为你的做法是正确的,原因如下:

    1.

    您使用的循环显示库与 Android 11 到 17(3.0 到 4.2)上的硬件加速不兼容。它使用 Canvas.clipPath() - 一种仅在 Android 4.3 之前的软件中实现的方法。这意味着您必须关闭硬件加速,否则您的应用会因不支持的 OpenGL 调用而崩溃。

    剪辑布局的最佳方法是使用 Canvas.saveLayer()/restoreLayer()。这是支持所有设备、获得抗锯齿图像并正确支持无效/布局/绘制流程的唯一方法。

    2.

    依靠计时器和布局侦听器来更改布局意味着某些东西并不适合您。每次更改都可以直接执行,无需等待。你只需要为那段代码找到合适的位置。

    计时器也可以在您的应用处于后台时触发。这意味着它会在尝试从 Timer 线程访问 UI 时崩溃。

    也许您在代码中的某处调用了 setVisibility(true),这就是您的 FAB 可见的原因?设置可见性是可以的,应该可以在没有任何延迟呼叫的情况下工作。只需将可见性保存为 FAB 的状态并在方向更改后恢复它。

    如果您希望将 FAB 放置在工具栏的中心,请将它们都包装在 FrameLayout (wrap_content) 中并使用 layout_gravity="center" 放置 FAB。这应该允许您删除布局侦听器。

    3.

    support.CardView 已损坏,根本不应该使用。它在 Lollipop 和旧系统上的外观和工作方式略有不同。阴影不同,填充不同,内容剪辑在棒棒糖之前的设备上不起作用等等。这就是为什么很难在所有平台上获得一致的良好结果。

    您应该考虑为此目的使用普通布局。

    4.

    有些动画看起来很酷,但很难实现,没有任何价值,很快就会变得烦人,因为用户每次点击按钮都必须等待动画完成。

    我并不是说不适合您的情况,但您可以考虑移除转换并为此使用下拉菜单、底部工作表、静态工具栏或对话框。

    【讨论】:

    • 您好 Zielony,感谢您抽出宝贵时间。我开始认为您删除此动画是正确的,尤其是因为我不是图形方面的专家。我正在使用那个库,因为它声称在 Lollipop 之前的 Android 版本中启用了这种效果,而且我还没有读过任何关于硬件加速问题的文章(或者我又错了吗?)。我正在使用 CardView 显示一个小表单来在我的数据库中创建一个新条目(只有 2 个编辑文本)。如果我决定更换 CardView,您对此有何建议? DialogFragment(我使用片段)?
    • 我所说的硬件加速问题在这里被报告为一个问题:github.com/ozodrukh/CircularReveal/issues/27。我会在那里使用某种对话,但我不知道你的情况,这只是我的意见。选择最适合您的。
    • 谢谢 Zielony,我想我会去 DialogFragment。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-14
    • 2017-08-13
    • 1970-01-01
    • 1970-01-01
    • 2014-10-09
    • 1970-01-01
    相关资源
    最近更新 更多