六边形按钮及其触摸区域

5

我需要创建与下面图片中相同的按钮。按钮必须带有内部文本。

  • enter image description here

当我制作XML布局时,遇到了一个按钮触摸区域的问题。每个后续按钮都会用矩形按钮区域覆盖前一个按钮。

  • enter image description here

我是否正确地将六边形放置在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>
1个回答

1

好的,这比我想象的要复杂得多。基本问题是最高的Z轴按钮始终会获得触摸事件,但即使它没有处理该事件,也不会将其传递给较低的Z轴。为了使两个按钮都能看到事件,需要一个解决方法。以下是基本方法:

1:在当前包含六角形按钮的Fragment / Activity中创建一个容器

2:创建一个包含按钮和另一个在所有按钮之上且 alpha = 0的按钮的Fragment

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

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

5:在主Activity / Fragment中设置侦听器以处理覆盖按钮的触摸事件

我制作了(并测试了)一个应用程序来演示这个概念。我将把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";
        }
    }
}

祝你好运。

预计时间:示例 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() 方法将检查按钮区域矩形的最大宽度和高度坐标。 - AskQuestion
第二个按钮为什么有0.4的透明度? - AskQuestion
只是为了让我能够看到我通过更大的按钮点击了button1区域。您的按钮可以使用任何alpha。 - h0x0
hitTest()需要测试运动事件发生在哪个六边形中。这并不是一件简单的事情,因为六边形并没有填满视图边界,你可能实际上想要一个不同的六边形而不是你正在考虑的那一个。我会快速写一个示例来给你一个想法。 - h0x0
看看我的第二张图片。如果我按下底部按钮,但在它覆盖第一个按钮或第二个按钮的区域内,我认为会触发第一个按钮或第二个按钮,而不是第三个按钮(底部)。或者不是这样吗? - AskQuestion
这里的想法是你不直接调用Button.onClick(View)方法(因为重叠的按钮永远不会调用它)。但是通过判断事件是否在其中一个六边形的范围内,你可以得到相同的结果。 - h0x0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接