Android - 旋转菜单的一部分超出屏幕

标签 android android-layout layout rotation

我要开发this rotation在我的应用程序中。我已经实现了“向下”菜单(“můj Dealer”、“moje Volvo”、“kontakty”),并且我需要实现“上部”​​旋转菜单。

我该怎么做?你有什么建议吗?希望你能理解我。

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

enter image description here enter image description here

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>

最佳答案

我已经组合了一个复制行为的自定义 View ,您仍然需要像示例中那样设置它的样式,但它可以与任意数量的 subview 一起使用,目前看起来像这样:

enter image description here

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

enter image description here

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

1) 来源

旋转菜单.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)用法:

自定义 View 利用适配器和 View 回收,因此您使用它的方式与使用 ListView 的方式相同。由于 View 回收,它的内存消耗不是很大,而且速度实际上相当快。在底部创建菜单项的逻辑也在适配器中,并且它也使用 View 回收。因此,请务必像实现 getView() 一样小心地实现 getMenuItemView()。首先你必须编写一个适配器:

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() 中添加逻辑来启用/禁用菜单项。

然后将适配器应用到 View :

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 吗?我不这么认为。

关于Android - 旋转菜单的一部分超出屏幕,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22544972/

相关文章:

android - fragment 最佳实践

android - 从后台服务获取位置,这是正确的吗?

android - 将线性布局添加到 GridView 会导致每个元素作为一个完整的行

java - 如何使两个具有不同字体大小的 TextView 居中

android - 巨大的形态: how can I do it in Android?

java - android 覆盖流高度

Java - 如何编码非拉丁字符的 URL 路径

android - 如何用特定行更新 sqlite 表而不重复存在的行?

java - 如何设置动态创建的表格布局单元格高度和宽度固定

Java : divide the screen