【问题标题】:Android - Rotation menu with one part out of the screenAndroid - 一部分在屏幕外的旋转菜单
【发布时间】:2023-03-21 18:53:02
【问题描述】:

我想在我的 App 中开发 this rotation。我已经实现了“向下”菜单(“můj 经销商”、“moje Volvo”、“kontakty”),我需要实现“上”旋转菜单。

我该怎么做?你有提示吗?希望你能理解我。

图片(请在链接中观看上面的视频):

Menu_item_test.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent" >

<ImageView
    android:id="@+id/menuItemImg"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="fitXY"
    android:src="@drawable/tab_1_1" />

</RelativeLayout>

Activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >

<FrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_above="@+id/rotationMenu" >
</FrameLayout>

<ImageView
    android:id="@+id/imageViewShadow"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_marginBottom="60dp"
    android:contentDescription="shadow"
    android:scaleType="fitXY"
    android:src="@drawable/half_round_back" />

<cz.mixedapps.volvista.RotationMenu
    android:id="@+id/rotationMenu"
    android:layout_width="match_parent"
    android:layout_height="265dp"
    android:layout_alignParentBottom="true" >
</cz.mixedapps.volvista.RotationMenu>

</RelativeLayout>

【问题讨论】:

    标签: android android-layout layout rotation


    【解决方案1】:

    我已经组合了一个复制行为的自定义视图,您仍然必须像示例中那样设置它的样式,但它适用于任意数量的子视图,目前看起来像这样:

    编辑:我添加了通过showRotationMenu()hideRotationMenu()两种方法显示和隐藏上部旋转菜单的功能:

    当然,您可以做很多事情来改善这种观点。我只是在 15 分钟内写了这篇文章,因此它的边缘有点粗糙。但这应该足以让你走上正轨。按钮的外观和旋转的视图都是完全可定制的。

    1) 来源

    RotationMenu.java:

    import android.content.Context;
    import android.content.res.Resources;
    import android.database.DataSetObserver;
    import android.util.AttributeSet;
    import android.util.DisplayMetrics;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.animation.*;
    import android.widget.FrameLayout;
    import android.widget.LinearLayout;
    
    /**
     * Created by Xaver Kapeller on 26/03/14.
     */
    public class RotationMenu extends LinearLayout {
    
        private final DataSetObserver dataSetObserver = new DataSetObserver() {
            @Override
            public void onChanged() {
                super.onChanged();
                reloadAdapter();
            }
        };
    
        private RotationMenuAdapter adapter;
        private FrameLayout flViewContainer;
        private LinearLayout llMenu;
        private View currentView;
        private View previousView;
    
        private int animationPivotX;
        private int animationPivotY;
    
        private int selectedPosition = 0;
    
        public RotationMenu(Context context) {
            super(context);
    
            setupMenu();
        }
    
        public RotationMenu(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            setupMenu();
        }
    
        public RotationMenu(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
    
            setupMenu();
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
    
            this.animationPivotX = w / 2;
            this.animationPivotY = h;
        }
    
        private void setupMenu() {
            this.setOrientation(VERTICAL);
            this.flViewContainer = new FrameLayout(getContext());
            this.addView(this.flViewContainer, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dpToPixel(150)));
    
            this.llMenu = new LinearLayout(getContext());
            this.llMenu.setOrientation(LinearLayout.HORIZONTAL);
            this.addView(this.llMenu, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
        }
    
        private int dpToPixel(int dp) {
            float scale = getDisplayDensityFactor();
            return (int) (dp * scale + 0.5f);
        }
    
        private float getDisplayDensityFactor() {
            Resources res = getResources();
            if (res != null) {
                DisplayMetrics metrics = res.getDisplayMetrics();
                if(metrics != null) {
                    return metrics.density;
                }
            }
            return 1.0f;
        }
    
        public RotationMenuAdapter getAdapter() {
            return this.adapter;
        }
    
        public void setAdapter(RotationMenuAdapter adapter) {
            if (adapter != null) {
                if (this.adapter != null) {
                    this.adapter.unregisterDataSetObserver(this.dataSetObserver);
                }
                adapter.registerDataSetObserver(this.dataSetObserver);
                this.adapter = adapter;
                reloadAdapter();
            }
        }
    
        public boolean isRotationMenuVisible() {
            return this.flViewContainer.getVisibility() == View.VISIBLE;
        }
    
        public void showRotationMenu() {
                if(this.flViewContainer.getVisibility() != View.VISIBLE) {
                    this.flViewContainer.setVisibility(View.VISIBLE);
                    AnimationSet set = new AnimationSet(false);
    
                    TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 0);
                    translateAnimation.setDuration(1000);
                    set.addAnimation(translateAnimation);
    
                    AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
                    alphaAnimation.setDuration(1000);
                    set.addAnimation(alphaAnimation);
    
                    this.flViewContainer.startAnimation(set);
                }
        }
    
        public void hideRotationMenu() {
            if(this.flViewContainer.getVisibility() == View.VISIBLE) {
                AnimationSet set = new AnimationSet(false);
    
                TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1);
                translateAnimation.setDuration(1000);
                set.addAnimation(translateAnimation);
    
                AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
                alphaAnimation.setDuration(1000);
                set.addAnimation(alphaAnimation);
    
                set.setAnimationListener(new ViewAnimationEndListener(this.flViewContainer) {
                    @Override
                    protected void onAnimationEnd(Animation animation, View view) {
                        view.setVisibility(View.GONE);
                    }
                });
    
                this.flViewContainer.startAnimation(set);
            }
        }
    
        private void reloadAdapter() {
            Context context = getContext();
            if (this.adapter != null && context != null) {
                int viewCount = this.adapter.getCount();
                int oldViewCount = this.llMenu.getChildCount();
                for (int i = 0; i < Math.max(oldViewCount, viewCount); i++) {
                    if (i < viewCount) {
                        LayoutParams layoutParams = new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1);
                        View menuItem;
                        if (i < this.llMenu.getChildCount()) {
                            menuItem = this.adapter.getMenuItemView(i, this.llMenu.getChildAt(i), this.llMenu);
                            if(menuItem.getParent() == null) {
                                this.llMenu.removeViewAt(i);
                                this.llMenu.addView(menuItem, i, layoutParams);
                            }
                        } else {
                            menuItem = this.adapter.getMenuItemView(i, null, this.llMenu);
                            this.llMenu.addView(menuItem, layoutParams);
                        }
                        menuItem.setOnClickListener(new MenuItemClickListener(i));
                    } else {
                        this.llMenu.removeViewAt(i);
                    }
                }
    
                this.flViewContainer.removeAllViews();
                this.previousView = this.currentView;
                if (this.selectedPosition >= viewCount) {
                    this.selectedPosition = viewCount - 1;
                }
                this.currentView = this.adapter.getView(this.selectedPosition, this.previousView, this);
                addViewWithAnimation(this.currentView, false);
            }
        }
    
        public void switchToItem(int position, boolean animate) {
            if (this.adapter != null) {
                int viewCount = this.adapter.getCount();
                position = valueInRange(position, 0, viewCount - 1);
    
                if (position != this.selectedPosition) {
                    View oldView = this.currentView;
                    this.currentView = this.adapter.getView(position, this.previousView, this);
                    this.previousView = oldView;
    
                    addViewWithAnimation(this.currentView, animate, position < this.selectedPosition);
                    removeViewWithAnimation(this.previousView, animate, position < this.selectedPosition);
    
                    this.selectedPosition = position;
                }
            }
        }
    
        private int valueInRange(int value, int min, int max) {
            if (value > max) {
                value = max;
            } else if (value < min) {
                value = min;
            }
            return value;
        }
    
        private void addViewWithAnimation(View view, boolean animate, boolean leftToRight) {
            if (view != null) {
                if(view.getParent() == null) {
                    this.flViewContainer.addView(view, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                }
                if (animate) {
    
                    int start = leftToRight ? -90 : 90;
    
                    Animation rotateIn = new RotateAnimation(start, 0, this.animationPivotX, this.animationPivotY);
                    rotateIn.setDuration(1000);
    
                    view.startAnimation(rotateIn);
                }
            }
        }
    
        private void addViewWithAnimation(View view, boolean animate) {
            addViewWithAnimation(view, animate, true);
        }
    
        private void removeViewWithAnimation(View view, boolean animate, boolean leftToRight) {
            if (view != null) {
                if (animate) {
    
                    int target = leftToRight ? 90 : -90;
    
                    Animation rotateOut = new RotateAnimation(0, target, this.animationPivotX, this.animationPivotY);
                    rotateOut.setDuration(1000);
                    rotateOut.setAnimationListener(new ViewAnimationEndListener(view) {
                        @Override
                        protected void onAnimationEnd(Animation animation, View view) {
                            flViewContainer.removeView(view);
                        }
                    });
                    view.startAnimation(rotateOut);
                } else {
                    this.flViewContainer.removeView(view);
                }
            }
        }
    
        private void removeViewWithAnimation(View view, boolean animate) {
            removeViewWithAnimation(view, animate, true);
        }
    
        private abstract class ViewAnimationEndListener implements Animation.AnimationListener {
    
            private final View view;
    
            private ViewAnimationEndListener(View view) {
                this.view = view;
            }
    
            @Override
            public void onAnimationStart(Animation animation) {
    
            }
    
            @Override
            public void onAnimationEnd(Animation animation) {
                onAnimationEnd(animation, this.view);
            }
    
            @Override
            public void onAnimationRepeat(Animation animation) {
    
            }
    
            protected abstract void onAnimationEnd(Animation animation, View view);
        }
    
        private class MenuItemClickListener implements OnClickListener {
    
            private final int position;
    
            MenuItemClickListener(int position) {
                this.position = position;
            }
    
            @Override
            public void onClick(View v) {
                if(adapter != null && adapter.isMenuItemEnabled(this.position) && isRotationMenuVisible()) {
                    switchToItem(this.position, true);
                }
            }
        }
    }
    

    RotationMenuAdapter.java:

    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    
    /**
     * Created by Xaver Kapeller on 26/03/14.
     */
    public abstract class RotationMenuAdapter extends BaseAdapter {
    
        public abstract View getMenuItemView(int position, View convertView, ViewGroup parent);
        public abstract long getMenuItemId(int position);
        public abstract boolean isMenuItemEnabled(int position);
    }
    

    2) 用法:

    自定义视图正在使用适配器和视图回收,因此您可以像使用 ListView 一样使用它。由于视图回收,它不是非常占用内存并且实际上非常快。底部菜单项的创建逻辑也在适配器中,它也使用视图回收。因此,请确保您在实现getMenuItemView() 时要像实现getView() 时一样小心。首先你要写一个适配器:

    import android.content.Context;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import at.test.app.R;
    import at.test.app.RotationMenu.RotationMenuAdapter;
    
    import java.util.List;
    
    /**
     * Created by Xaver Kapeller on 26/03/14.
     */
    public class TestAdapter extends RotationMenuAdapter {
    
        private static final long TEST_VIEW_ID = 0;
        private static final long DEFAULT_VIEW_ID = TEST_VIEW_ID;
    
        private static final long TEST_MENU_ID = 0;
        private static final long DEFAULT_MENU_ID = TEST_MENU_ID;
    
        private final LayoutInflater inflater;
        private final List<TestViewModel> viewModels;
    
        public TestAdapter(Context context, List<TestViewModel> viewModels) {
            this.inflater = LayoutInflater.from(context);
            this.viewModels = viewModels;
        }
    
        @Override
        public int getCount() {
            return this.viewModels.size();
        }
    
        @Override
        public Object getItem(int position) {
            return this.viewModels.get(position);
        }
    
        @Override
        public long getItemId(int position) {
    
            Object viewModel = getItem(position);
    
            if(viewModel instanceof TestViewModel) {
                return TEST_VIEW_ID;
            }
    
            return DEFAULT_VIEW_ID;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
    
            if(getItemId(position) == TEST_VIEW_ID) {
                TestViewModel viewModel = (TestViewModel) getItem(position);
    
                TestRow row;
                if(convertView == null) {
                    convertView = this.inflater.inflate(TestRow.LAYOUT, parent, false);
                    row = new TestRow(convertView);
                    convertView.setTag(row);
                }
    
                row = (TestRow) convertView.getTag();
                row.bind(viewModel);
            }
    
            return convertView;
        }
    
        @Override
        public View getMenuItemView(int position, View convertView, ViewGroup parent) {
    
            if(getMenuItemId(position) == TEST_MENU_ID) {
                TestViewModel viewModel = (TestViewModel)getItem(position);
    
                MenuRow row;
                if(convertView == null) {
                    convertView = this.inflater.inflate(MenuRow.LAYOUT, parent, false);
                    row = new MenuRow(convertView);
                    convertView.setTag(row);
                }
    
                row = (MenuRow)convertView.getTag();
                row.bind(viewModel);
            }
    
            return convertView;
        }
    
        @Override
        public long getMenuItemId(int position) {
            Object item = getItem(position);
    
            if(item instanceof TestViewModel) {
                return TEST_MENU_ID;
            }
    
            return DEFAULT_MENU_ID;
        }
    
        @Override
        public boolean isMenuItemEnabled(int position) {
            return true;
        }
    }
    

    没什么特别的,除了用于创建菜单项的额外方法。在isMenuItemEnabled() 中,您可以根据需要添加逻辑来启用/禁用菜单项。

    然后你将你的适配器应用到视图上:

    List<TestViewModel> viewModels = new ArrayList<TestViewModel>();
    
    TestViewModel menuItemOne = new TestViewModel();
    menuItemOne.setMenuItemIconResId(R.drawable.icon);
    viewModels.add(menuItemOne);
    
    TestViewModel menuItemTwo = new TestViewModel();
    menuItemTwo.setMenuItemIconResId(R.drawable.icon);
    viewModels.add(menuItemTwo);
    
    TestAdapter adapter = new TestAdapter(getActivity(), viewModels);
    this.rotationMenu.setAdapter(adapter);
    

    编辑:

    wrap_content 代替match_parent 试试这个布局:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" >
    
        <ImageView
                android:id="@+id/menuItemImg"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:scaleType="fitXY"
                android:src="@drawable/tab_1_1" />
    
    </RelativeLayout>
    

    我也不太确定你的layout_width,你确定你需要match_parent吗?我不这么认为。

    【讨论】:

    • 我还有一个问题:) 如何隐藏“上部”旋转菜单?例如,在某些动画中,“上”菜单会在“下”菜单后面向下滚动,或者向上滚动,然后又可以看到。
    • 我不确定我是否理解您的要求。您想完全隐藏上层菜单吗?如果需要,我可以将其添加到自定义视图中。是否应该有上层菜单滑到下层菜单后面的动画?
    • 是的,我想以编程方式完全隐藏/显示上部菜单,并且应该有一个动画(就像你写的那样)。如果您编辑代码,我将不胜感激。我希望它会在未来对其他人有所帮助。
    • 这应该不难添加。我将编辑我的代码,但请给我一些时间来完成它。我现在有很多事情要做,但我想我会在接下来的几个小时内找到时间。完成后我会在这里评论。
    • 这将是片段的经典用例。您可以使用所需的布局和功能创建不同的片段,并使用FragmentTransaction 交换它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多