Android - 旋转菜单,其中一部分在屏幕外

时间:2014-03-20 21:00:15

标签: android android-layout layout rotation

我想在我的应用中开发this rotation。我已经实现了“向下”菜单(“můjdeporter”,“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>

1 个答案:

答案 0 :(得分:4)

我已经整理了一个复制行为的自定义视图,你仍然需要像你的例子那样设置样式,但它适用于任意数量的子视图,目前看起来像这样:

enter image description here

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

enter image description here

当然,您可以做很多事情来改善这种观点。我只是在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吗?我不这么认为。