【问题标题】:Hexagon button with hexagon touch area六边形按键,六边形触摸区
【发布时间】:2015-03-22 13:05:59
【问题描述】:

我需要创建与下图相同的按钮。 Button's 必须与里面的文字。

我在制作XML 布局时遇到了Button 的触摸区域问题。每个下一个按钮都是用矩形Button 的区域覆盖上一个按钮。

像我做的那样在 XML 标记中放置六边形来实现图片中的六边形是否正确?请帮我解决触摸区域的问题,如果可能的话,请告诉我如何正确创建布局,因为我不确定,因为我做的是否正确。

这是我的测试布局的一部分:

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:layout_marginTop="?attr/actionBarSize">


            <Button
                android:id="@+id/button1"
                android:layout_width="130dp"
                android:layout_height="134dp"
                android:background="@drawable/hexagon_shape_img"
                android:text="Home page"
                android:textSize="@dimen/small_text" />

            <Button
                android:id="@+id/button2"
                android:layout_width="130dp"
                android:layout_height="134dp"
                android:layout_marginLeft="65dp"
                android:layout_marginTop="-20dp"
                android:background="@drawable/hexagon_shape_img"
                android:text="Tavern"
                android:textSize="@dimen/small_text" />

        </LinearLayout>

【问题讨论】:

    标签: android xml button


    【解决方案1】:

    好吧,事实证明这比我想象的要复杂。基本问题是最高 Z 顺序的按钮总是获得触摸事件,但即使它没有处理事件,也不会在较低的 Z 顺序上传递它。要让两个按钮都能看到事件,需要一种变通方法。这是基本方法:

    1:在当前保存十六进制按钮的 Fragment/Activity 中创建一个容器

    2:创建一个片段,其中包含按钮和在所有按钮之上的另一个按钮,alpha=0

    3:添加一个 getOverlay():View 方法,返回那个 alpha=0 按钮

    4:在片段中实现一个hitTest():Button方法

    5:在主Activity/Fragment中,设置监听器处理overlay按钮的触摸事件

    我制作(并测试了)一个应用程序来演示这个概念。我将把 hitTest 留给你,因为它相当乏味

    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" 
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" 
    tools:context=".MainActivity">
    
        <FrameLayout android:id="@+id/main_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
    </RelativeLayout>
    

    fragment_buttons.xml:

    <FrameLayout
    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="com.example.me.testapplication.Buttons">
    
        <Button android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/lorem"/>
        <Button android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/lorem_ipsum"
            android:alpha="0.4"/>
        <Button android:id="@+id/overlay"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:alpha="0"/>
    
    </FrameLayout>
    

    MainActivity.java:

    public class MainActivity extends ActionBarActivity {
        public static final String DEBUG_TAG = "TEST";
    
        private Buttons mButtons;
        private GestureDetector mGestureDetector;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mButtons = Buttons.newInstance();
            getFragmentManager().beginTransaction()
                    .replace(R.id.main_container, mButtons)
                    .commit();
    
            mGestureDetector = new GestureDetector(this, mGestureListener);
        }
    
        @Override
        protected void onStart() {
            super.onStart();
    
            View overlay = mButtons.getOverlay();
            if (overlay != null) {
                overlay.setOnTouchListener(mTouchListener);
            }
        }
        ...
        private View.OnTouchListener mTouchListener = new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                 return mGestureDetector.onTouchEvent(event);
            }
        };
    
        private GestureDetector.OnGestureListener mGestureListener
                =new GestureDetector.OnGestureListener()
        {
                @Override
                public boolean onDown(MotionEvent e) {
                String buttonHit = mButtons.hitTest(e);
                Log.i(DEBUG_TAG, buttonHit);
                return true;
            }
            ...
        };
    }
    

    Buttons.java:

    public class Buttons extends Fragment {
    
        Button mButton1, mButton2;
        View mOverlay;
    
        public static Buttons newInstance() {
            return new Buttons();
        }
    
        public Buttons() {}
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View root = inflater.inflate(R.layout.fragment_buttons, container, false);
            mButton1 = (Button) root.findViewById(R.id.button1);
            mButton2 = (Button) root.findViewById(R.id.button2);
            mOverlay = root.findViewById(R.id.overlay);
            return root;
        }
    
        @Nullable
        public View getOverlay() {
            return mOverlay;
        }
    
        public String hitTest(MotionEvent event) {
            float x = event.getX();
            float y = event.getY();
    
            if (x > mButton1.getLeft() && mButton1.getRight() > x &&
                    y > mButton1.getTop() && mButton1.getBottom() > y)
            {
                return "Button 1";
            } else if (x > mButton2.getLeft() && mButton2.getRight() > x &&
                    y > mButton2.getTop() && mButton2.getBottom() > y)
            {
                return "Button 2";
            } else {
                return "None";
            }
        }
    }
    

    祝你好运。

    ETA:示例 hitTest

    /**
     * UNTESTED
     * I'm going to assume square buttons (equilateral hexagons)
     * this just calculates if the distance from the center of the button is less than its width. It may be good enough for government work.
     */
    public View hitTest(MotionEvent e) {
        for (Button hex : SomeIterableThingHoldingYourButtons) { //ArrayList<Button> maybe
            float x = e.getX();
            float y = e.getY();
    
            if (isInHex(hex, x, y)) return hex;
         }
         return null;
    }
    
    private boolean isInHex(Button hex, float x, float y) {
        float radius = hex.getRight() - hex.getLeft() / 2;
        float centerX = hex.getLeft() + radius;
        float centerY = hex.getTop() + radius;
    
        float dist = FloatMath.sqrt(...) //euclidean distance
    
        return dist < radius;
    }
    

    【讨论】:

    • 它将使用十六进制按钮?据我了解,hitTest() 方法将检查按钮区域矩形的最大宽度和高度坐标。
    • 为什么第二个 btn 有 0.4 alpha?
    • 只是为了让我可以看到我正在通过较大的按钮单击 button1 区域。您的按钮可以使用您喜欢的任何 alpha。
    • hitTest() 需要测试运动事件发生在哪个六边形中。这很重要,因为六边形不会填充视图边界,并且您实际上可能想要一个与您不同的十六进制考虑。我会写一个快速的镜头,只是为了给你一个想法
    • 看我的第二张照片。如果我按下底部按钮,但在它覆盖第一个 btn 或第二个的区域,我认为将首先触发 btn 或第二个而不是第三个(底部)。还是没有?
    猜你喜欢
    • 2015-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多