Android custom control-like Youku disc menu, android Disc
Respect the author's Labor achievements. Indicate the author's Labor achievementsHttp://blog.csdn.net/allen315410/article/details/39232535
Recently, I learned how to write custom controls on a document. The above example uses the client of early Youku versions, the menu of this client is a custom component (the current version is not clear, but it has not been downloaded). Well, let's talk about the prototype of Youku first.
This custom component is visually designed for three layers inside and outside. Different menu buttons are arranged on each layer, and the animation for entering and exiting is set for each layer, to enhance the user experience. This kind of design is very good, concise and beautiful. The following is a component that I have customized in the Youku menu. It is written as a reference only.
1. First of all, the three layers inside and outside seem messy. In fact, there is nothing more than the UI layout, which is not very advanced. The layout file is as follows:
<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" > <RelativeLayout android:id="@+id/rl_level1" android:layout_width="100dip" android:layout_height="50dip" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:background="@drawable/level1" > <ImageButton android:id="@+id/ib_home" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:background="@drawable/icon_home" /> </RelativeLayout> <RelativeLayout android:id="@+id/rl_level2" android:layout_width="200dip" android:layout_height="100dip" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:background="@drawable/level2" > <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="5dip" android:layout_marginLeft="10dip" android:background="@drawable/icon_search" /> <ImageButton android:id="@+id/ib_menu" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="10dip" android:background="@drawable/icon_menu" /> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginBottom="5dip" android:layout_marginRight="10dip" android:background="@drawable/icon_myyouku" /> </RelativeLayout> <RelativeLayout android:id="@+id/rl_level3" android:layout_width="320dip" android:layout_height="160dip" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:background="@drawable/level3" > <ImageButton android:id="@+id/ib_channel1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="10dip" android:layout_marginLeft="15dip" android:background="@drawable/channel1" /> <ImageButton android:id="@+id/ib_channel2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/ib_channel1" android:layout_marginBottom="20dip" android:layout_marginLeft="40dip" android:background="@drawable/channel2" /> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/ib_channel2" android:layout_marginBottom="15dip" android:layout_marginLeft="10dip" android:layout_toRightOf="@id/ib_channel2" android:background="@drawable/channel3" /> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="10dip" android:background="@drawable/channel4" /> <ImageButton android:id="@+id/ib_channel7" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginBottom="10dip" android:layout_marginRight="15dip" android:background="@drawable/channel7" /> <ImageButton android:id="@+id/ib_channel6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/ib_channel7" android:layout_alignParentRight="true" android:layout_marginBottom="20dip" android:layout_marginRight="40dip" android:background="@drawable/channel6" /> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/ib_channel6" android:layout_marginBottom="15dip" android:layout_marginRight="10dip" android:layout_toLeftOf="@id/ib_channel6" android:background="@drawable/channel5" /> </RelativeLayout></RelativeLayout>
The UI effect after layout is shown in the following figure:
As shown in, the layout is complete, and then some dynamic effects are added. The effect is described as follows: the menu is called "Level 1 menu" from the inside out ", imageButton at the center of level 1 and level 2 menus is used to control the dynamic effect of the entire menu. When you click Level 1, if Level 2 or level 3 menus are displayed, level 2 and level 3 menus are hidden. If Level 2 or level 3 menus are not displayed, only Level 2 menus are displayed. When you click Level 2, only the Level 3 menu is displayed and hidden.
All the dynamic effects here are implemented by a custom rotation animation. Next we will first complete this custom rotation Animation:
Package com. example. youkumenu; import android. view. animation. animation; import android. view. animation. rotateAnimation; import android. widget. relativeLayout; public class AnimationUtils {public static boolean isRunningAnimation = false; // record whether the animation is being executed/*** the animation to be rotated ** @ param layout * the animation object to be executed * @ param startOffset * Delay Time */public static void outRotateAnimation (RelativeLayout layout, long startOffset) {// prevents the child control in the parent control from gaining focus, and sets the child control to unavailable for (int I = 0; I <layout. getChildCount (); I ++) {layout. getChildAt (I ). setEnabled (false);} RotateAnimation ra = new RotateAnimation (// 0.0f, // rotation start angle-180.0f, // rotation end angle RotateAnimation. RELATIVE_TO_SELF, // rotate the x-axis of the reference object 0.5f, // The percentage of RotateAnimation relative to the x-axis of the reference object. RELATIVE_TO_SELF, // The percentage of the reference object 1.0f to the Y axis of the rotating coordinate Y axis); ra. setDuration (1, 500); ra. setStartOffset (startOffset); // The animation Delay Time ra. setFillAfter (true); ra. setAnimationListener (new MyAnimationListener (); layout. startAnimation (ra);}/*** the rotated animation ** @ param layout * the animation execution object */public static void inRotateAnimation (RelativeLayout layout) {// set all child controls to available for (int I = 0; I <layout. getChildCount (); I ++) {layout. getChildAt (I ). setEnabled (false);} RotateAnimation ra = new RotateAnimation (//-180.0f, // rotation start angle 0.0f, // rotation end angle RotateAnimation. RELATIVE_TO_SELF, // rotate the x-axis of the reference object 0.5f, // The percentage of RotateAnimation relative to the x-axis of the reference object. RELATIVE_TO_SELF, // The percentage of the reference object 1.0f to the Y axis of the rotating coordinate Y axis); ra. setDuration (1, 500); ra. setFillAfter (true); layout. startAnimation (ra);} static class MyAnimationListener implements Animation. animationListener {/*** run the Animation */@ Overridepublic void onAnimationStart (animation Animation) {// TODO Auto-generated method stubisRunningAnimation = true ;} /*** run the Animation at the end of the animation */@ Overridepublic void onAnimationEnd (Animation animation) {// TODO Auto-generated method stubisRunningAnimation = false ;} /*** when the Animation is repeatedly executed */@ Overridepublic void onAnimationRepeat (animation Animation) {// TODO Auto-generated method stub }}}
The main code of MainActivity is as follows:
Package com. example. youkumenu; import android. OS. bundle; import android. app. activity; import android. view. keyEvent; import android. view. view; import android. view. view. onClickListener; import android. widget. relativeLayout; public class MainActivity extends Activity implements OnClickListener {private RelativeLayout rlLevel1; private RelativeLayout rlLevel2; private RelativeLayout rlLevel3;/** record level 3 menu display */pr Ivate boolean isDisplayLevel3 = true;/** record level 2 menu display */private boolean isDisplayLevel2 = true;/** record level 1 menu display */private boolean isDisplayLevel1 = true; @ Overrideprotected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); rlLevel1 = (RelativeLayout) findViewById (R. id. rl_level1); rlLevel2 = (RelativeLayout) findViewById (R. id. rl_level2); rl Level3 = (RelativeLayout) findViewById (R. id. rl_level3); findViewById (R. id. ib_home ). setOnClickListener (this); findViewById (R. id. ib_menu ). setOnClickListener (this) ;}@ Overridepublic void onClick (View v) {switch (v. getId () {case R. id. ib_home: if (AnimationUtils. isRunningAnimation) // when the current animation is being executed, do not execute the animation return; if (isDisplayLevel2) {// Level 2 menu is displaying long startOffset = 0; // if (isDisplayLevel3) {// the Level 3 menu is also displayed, Rotate Level 3 menus first, and then Level 2 menu AnimationUtils. outRotateAnimation (rlLevel3, startOffset); startOffset + = 200; isDisplayLevel3 =! IsDisplayLevel3;} AnimationUtils. Equals (rlLevel2, startOffset);} The else {// Level 2 menu is not displayed and needs to be rotated in the AnimationUtils. Equals (rlLevel2);} isDisplayLevel2 =! IsDisplayLevel2; break; case R. id. ib_menu: if (AnimationUtils. isRunningAnimation) return; if (isDisplayLevel3) {// Level 3 menu is being displayed, which needs to be rotated out of AnimationUtils. outRotateAnimation (rlLevel3, 0);} The else {// Level 3 menu is not displayed and needs to be rotated in AnimationUtils. inRotateAnimation (rlLevel3);} isDisplayLevel3 =! IsDisplayLevel3; break; default: break;}/*** menu button Processing */@ Overridepublic boolean onKeyDown (int keyCode, KeyEvent event) {// TODO Auto-generated method stubif (keyCode = KeyEvent. KEYCODE_MENU) {if (AnimationUtils. isRunningAnimation) return super. onKeyDown (keyCode, event); if (isDisplayLevel1) {// Level 1 menu rotates out long startOffset = 0; // record the delay time if (isDisplayLevel2) {// Level 2 menu rotated out if (isDisplayLevel3) {// Level 3 menu first rotated out ImationUtils. outRotateAnimation (rlLevel3, startOffset); startOffset + = 200; isDisplayLevel3 =! IsDisplayLevel3;} AnimationUtils. outRotateAnimation (rlLevel2, startOffset); startOffset + = 200; // latency: msisdisplaylevel2 =! IsDisplayLevel2;} AnimationUtils. outRotateAnimation (rlLevel1, startOffset);} The else {// Level 1 menu is rotated in AnimationUtils. Equals (rlLevel1);} isDisplayLevel1 =! IsDisplayLevel1;} return super. onKeyDown (keyCode, event );}}
The above is the full source code of the entire custom component. It is worth noting that:
1. Focus on controls.
In the layout, we can see that all the controls I use in the relative layout of RelativeLayout are ImageButton. ImageButton has a notable characteristic that it has a particularly strong focus-grabbing capability. Therefore, when you click the ImageButton button during animation execution for menus of different levels, an event response occurs. This kind of user experience is extremely undesirable. How can this problem be solved.
It is true that RelativeLayout inherits from the ViewGroup class through API Search.
ViewGroup provides the getChildAt (int index) method for Traversing sub-elements. Therefore, we traverse all sub-controls on the menu in the Animation rotation method of Animation, and set it to unavailable. In the method of animation rotation, traverse all the sub-controls on the menu and set its availability. This contradiction is solved.
2. When you click the menu twice, that is, when you click the menu twice for the first time, the menu is not completely rotated or rotated in, with the second click menu, the opposite animation effect is executed here, that is, when the menu is not completely out or in, the operation is forced to stop halfway, and the opposite action is taken, that is, rotate in or out. So how can this problem be solved?
Through the animation API, we can find an interface Android provides to us:
public static interface AnimationListener { /** * <p>Notifies the start of the animation.</p> * * @param animation The started animation. */ void onAnimationStart(Animation animation); /** * <p>Notifies the end of the animation. This callback is not invoked * for animations with repeat count set to INFINITE.</p> * * @param animation The animation which reached its end. */ void onAnimationEnd(Animation animation); /** * <p>Notifies the repetition of the animation.</p> * * @param animation The animation which was repeated. */ void onAnimationRepeat(Animation animation); }
Through this interface, you can see how to use these methods. As long as you define an implementation class and implement the three methods of the interface, you can set a static variable to record whether the current animation is running, set this listener for the animation.
In MainActivity, you only need to determine the animation running status before calling this method to control the menu animation. Then, the problem can be solved.
The above are my hobbies and the source code has been uploaded. You are welcome to exchange and learn.
Download the source code here
Android desktop disc menu
Is the landlord looking for a full-screen assistant?
Android custom control error
A class is not found. Is it the jar package you added? There is no such reason. Open the build path and check if there is any.