I need to create the same buttons like in the picture below. Button
's must be with the text inside.
When I was making XML
layout I encountered with a problem of Button
's touch area. Each next button is cover previous button with a rectangular Button
's area.
Is it right to place hexagons in the XML markup like I done, to implement hexagons like in picture? Please, help me to solve problem with touch area and if possible tell me how to create a layout right, because I'm not sure that since I'm doing is correctly.
<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>
Ok, this turned out to be more complicated than I thought. The basic problem is that the highest Z-ordered button always gets the touch event, but doesn't pass it on the the lower Z-orders even if it doesn't dispose of the event. To get both buttons to see the event, a work-around is needed. Here's the basic approach:
1: Create a container in the Fragment/Activity that currently holds your hex buttons
2: Create a Fragment that contains the buttons and another button on top of all of them with alpha=0
3: Add a getOverlay():View method that returns that alpha=0 button
4: implement a hitTest():Button method in the fragment
5: In the main Activity/Fragment, set up listeners to handle the touch events of the overlay button
I made (and tested) an Application to demonstrate the concept. I'm going to leave the hitTest to you as it's rather tedious
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";
}
}
}
Good luck.
ETA: Sample 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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With