Android: Quick control menu
Everyone who wants to use the menu from the browser and the new camera,
welcome to cat.
While working on one of the applications, the question arose of how to make a menu for the user. Having played enough with the Browser application for ICS, it was decided to use its optional component PieMenu. In another project, there was a need for a menu that not only looks pretty, but also knows how to do it in an arbitrary place.
It remains a big mystery to me why these menus were not added to the API: quite a lot of people like these menus, and developers would not need to reinvent the wheel.
So, my path lay on AOSP (Android Open Source Project). The search for the source data for these applications was successful. Both versions of the menu were taken from the master branch.
Then all the bindings to the application were removed: fields, methods, listeners, etc. (and the code is really very connected with application components, i.e. it’s corny to take and declare an object). For the convenience of using the side menu, I added the ability to install separate Listener'y on different menu items, adding
This menu supports the display of submenus. On the code side, this looks like adding
An important point : set the number of submenus for each item matching the same ring. For example, you have 3 item in the first ring and 2 in the second. Then, on the item’s submenu of the 1st ring, you can put either 2 elements (since when deploying, only 2 positions will remain) or 2 elements of the 2nd ring (because they can be completely replaced). Initially, the code in violation of this condition went to the apostle Peter and complained that he was forced to do terrible things. However, now you can experiment with how the code will behave without fear of Exception.
This class allows you to implement a side menu.
We declare the heir of this class and redefine the methods
This class allows you to implement a circular menu from the Camera application. Using it, in my opinion, is much simpler than PieControl. Conceptually, this menu differs from the side menu: you can not bind it to any heirs
Create an object
Sources of the resulting library can be pulled from here .
Runs on Android 2.2.1 and higher (theoretically works on Android 1.0 and higher).
Thank you for your attention and have a nice day!
Update:
Thanks to Prototik for the tip about the ported Animation classes. Now the menu also works on older APIs: JakeWharton indicates a minimum API of 1.0, but I managed to check for 2.2.1 and higher.
welcome to cat.
Background
While working on one of the applications, the question arose of how to make a menu for the user. Having played enough with the Browser application for ICS, it was decided to use its optional component PieMenu. In another project, there was a need for a menu that not only looks pretty, but also knows how to do it in an arbitrary place.
It remains a big mystery to me why these menus were not added to the API: quite a lot of people like these menus, and developers would not need to reinvent the wheel.
Source code
So, my path lay on AOSP (Android Open Source Project). The search for the source data for these applications was successful. Both versions of the menu were taken from the master branch.
Sidebar Modification
Then all the bindings to the application were removed: fields, methods, listeners, etc. (and the code is really very connected with application components, i.e. it’s corny to take and declare an object). For the convenience of using the side menu, I added the ability to install separate Listener'y on different menu items, adding
Item
's from List
' a. In the original implementation, the level parameter in the constructor did not work at all. He is responsible for the number of the ring in the menu, where the first ring with a minimum radius, and with increasing level, the radius grows. After studying the code, it turned out that the Path
one that draws the background for the icon was reassigned for each new ring in the rendering method, which made it impossible to use the multi-level menu. This flaw has been eliminated.This menu supports the display of submenus. On the code side, this looks like adding
Item
's to another Item
' y. An important point : set the number of submenus for each item matching the same ring. For example, you have 3 item in the first ring and 2 in the second. Then, on the item’s submenu of the 1st ring, you can put either 2 elements (since when deploying, only 2 positions will remain) or 2 elements of the 2nd ring (because they can be completely replaced). Initially, the code in violation of this condition went to the apostle Peter and complained that he was forced to do terrible things. However, now you can experiment with how the code will behave without fear of Exception.
Piecontrol
This class allows you to implement a side menu.
We declare the heir of this class and redefine the methods
populateMenu()
. I also recommend adding setListeners()
clicks for convenient installation of Listener's. We PieItem
create an element using makeItem()
. As parameters we indicate the resource of the icon and level (ring number). Then we create an object of our new class, bind it to frame through attachToContainer()
and set Listener's by calling the method setListeners()
.Heir example:
private class TestMenu extends PieControl {
List plus_sub;
List minus_sub;
public TestMenu(Activity activity) {
super(activity);
}
protected void populateMenu() {
PieItem plus = makeItem(android.R.drawable.ic_input_add,1);
{
plus_sub = new ArrayList(2);
plus_sub.add(makeItem(android.R.drawable.ic_input_add,2));
plus_sub.add(makeItem(android.R.drawable.ic_input_add, 2));
plus.addItems(plus_sub);
}
PieItem minus = makeItem(android.R.drawable.ic_menu_preferences,1);
{
minus_sub = new ArrayList(2);
minus_sub.add(makeItem(android.R.drawable.ic_menu_preferences,1));
minus_sub.add(makeItem(android.R.drawable.ic_menu_preferences, 1));
minus.addItems(minus_sub);
}
PieItem close = makeItem(android.R.drawable.ic_menu_close_clear_cancel,1);
mPie.addItem(plus);
mPie.addItem(minus);
mPie.addItem(close);
PieItem level2_0 = makeItem(android.R.drawable.ic_menu_report_image, 2);
mPie.addItem(level2_0);
PieItem level2_1 = makeItem(android.R.drawable.ic_media_next, 2);
mPie.addItem(level2_1);
}
public void setListeners() {
this.setClickListener(plus_sub.get(0),new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(),"Clicked plus 1", Toast.LENGTH_SHORT).show();
}
});
this.setClickListener(plus_sub.get(1), new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(),"Clicked plus 2", Toast.LENGTH_SHORT).show();
}
});
this.setClickListener(minus_sub.get(0), new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(),"Clicked minus 1", Toast.LENGTH_SHORT).show();
}
});
this.setClickListener(minus_sub.get(1), new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(),"Clicked minus 2", Toast.LENGTH_SHORT).show();
}
});
this.setClickListener(close, new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
}
}
Example menu declaration:
TestMenu testMenu = new TestMenu(this);
testMenu.attachToContainer(some_container);
testMenu.setListeners();
Pierenderer
This class allows you to implement a circular menu from the Camera application. Using it, in my opinion, is much simpler than PieControl. Conceptually, this menu differs from the side menu: you can not bind it to any heirs
ViewGroup
, but only to a special one RenderOverlay
(which is actually normal FrameLayout
with a small set of additional methods, but this is inner workings). You RenderOverlay
can bind to objects of the type Renderer
whose descendant is PieRenderer
. We then need it to render the menu. We also need a class PieController
to add items to the menu. So here we go : Create an object
PieRenderer
, an object PieController
, menu items using makeItem's method PieController
. Add menu items in PieRenderer
via addItem. Then create an objectRenderOverlay
(or find through findViewById
if you like to define everything in xml). Add PieRendere
to RenderOverlay
via addRenderer
. Now the final touch: in onTouchEvent
send the event to the handlerPieRenderer
Example activity code:
public class MainActivity extends Activity {
private static float FLOAT_PI_DIVIDED_BY_TWO = (float) Math.PI / 2;
private final static float sweep = FLOAT_PI_DIVIDED_BY_TWO / 2;
private PieRenderer pieRenderer;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
pieRenderer = new PieRenderer(getApplicationContext());
PieController pieController = new PieController(this, pieRenderer);
RenderOverlay renderOverlay = (RenderOverlay) findViewById(R.id.render_overlay);
PieItem item0 = pieController.makeItem(android.R.drawable.arrow_up_float);
item0.setFixedSlice(FLOAT_PI_DIVIDED_BY_TWO, sweep);
item0.setOnClickListener(new PieItem.OnClickListener() {
@Override
public void onClick(PieItem item) {
Toast.makeText(getApplicationContext(), "some cmd", Toast.LENGTH_SHORT).show();
}
});
PieItem item1 = pieController.makeItem(android.R.drawable.arrow_up_float);
item1.setFixedSlice(FLOAT_PI_DIVIDED_BY_TWO + sweep, sweep);
item1.setOnClickListener(new PieItem.OnClickListener() {
@Override
public void onClick(PieItem item) {
Toast.makeText(getApplicationContext(), "some cmd 2", Toast.LENGTH_SHORT).show();
}
});
PieItem item7 = pieController.makeItem(android.R.drawable.arrow_up_float);
item7.setFixedSlice(FLOAT_PI_DIVIDED_BY_TWO - sweep, sweep);
item7.setOnClickListener(new PieItem.OnClickListener() {
@Override
public void onClick(PieItem item) {
Toast.makeText(getApplicationContext(), "some cmd 7", Toast.LENGTH_SHORT).show();
}
});
pieRenderer.addItem(item0);
pieRenderer.addItem(item1);
pieRenderer.addItem(item7);
PieItem item0_0 = pieController.makeItem(android.R.drawable.ic_menu_add);
item0_0.setFixedSlice(FLOAT_PI_DIVIDED_BY_TWO, sweep);
item0_0.setOnClickListener(new PieItem.OnClickListener() {
@Override
public void onClick(PieItem item) {
Toast.makeText(getApplicationContext(), "some cmd", Toast.LENGTH_SHORT).show();
}
});
PieItem item0_6 = pieController.makeItem(android.R.drawable.ic_menu_add);
item0_6.setFixedSlice(FLOAT_PI_DIVIDED_BY_TWO + sweep, sweep);
item0_6.setOnClickListener(new PieItem.OnClickListener() {
@Override
public void onClick(PieItem item) {
Toast.makeText(getApplicationContext(), "some cmd 2", Toast.LENGTH_SHORT).show();
}
});
PieItem item0_7 = pieController.makeItem(android.R.drawable.ic_menu_add);
item0_7.setFixedSlice(FLOAT_PI_DIVIDED_BY_TWO - sweep, sweep);
item0_7.setOnClickListener(new PieItem.OnClickListener() {
@Override
public void onClick(PieItem item) {
Toast.makeText(getApplicationContext(), "some cmd 7", Toast.LENGTH_SHORT).show();
}
});
item0.addItem(item0_0);
item0.addItem(item0_6);
item0.addItem(item0_7);
renderOverlay.addRenderer(pieRenderer);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
pieRenderer.onTouchEvent(event);
return super.onTouchEvent(event);
}
}
Afterword
Sources of the resulting library can be pulled from here .
Runs on Android 2.2.1 and higher (theoretically works on Android 1.0 and higher).
Thank you for your attention and have a nice day!
Update:
Thanks to Prototik for the tip about the ported Animation classes. Now the menu also works on older APIs: JakeWharton indicates a minimum API of 1.0, but I managed to check for 2.2.1 and higher.