Android自定义控件之仿优酷菜单

时间:2021-08-05 20:38:55

优酷菜单 下拉菜单

  1. 组合式的控件
  2. 补间动画:
    1. 是通过父容器来不停的绘制孩子的形状
  3. 步骤:
    1. 创建布局
    2. 创建一个类,继承布局的根View
    3. 将业务逻辑封装到类中
  4. 特点:
    1. 将原生的view进行组装
    2. 加入业务逻辑
<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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" >


<!-- 外侧 -->

<RelativeLayout
android:id="@+id/level3"
android:layout_width="280dp"
android:layout_height="140dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:background="@drawable/level3" >


<!-- channel1 -->

<ImageView
android:id="@+id/level3_channel1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="8dp"
android:src="@drawable/channel1" />


<!-- channel2 -->

<ImageView
android:id="@+id/level3_channel2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/level3_channel1"
android:layout_marginBottom="12dp"
android:layout_marginLeft="30dp"
android:src="@drawable/channel2" />


<!-- channel3 -->

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/level3_channel2"
android:layout_marginBottom="12dp"
android:layout_marginLeft="60dp"
android:src="@drawable/channel3" />


<!-- channel4 -->

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="5dp"
android:src="@drawable/channel4" />


<!-- channel7 -->

<ImageView
android:id="@+id/level3_channel7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="8dp"
android:src="@drawable/channel7" />


<!-- channel6 -->

<ImageView
android:id="@+id/level3_channel6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/level3_channel7"
android:layout_alignParentRight="true"
android:layout_marginBottom="12dp"
android:layout_marginRight="30dp"
android:src="@drawable/channel6" />


<!-- channel5 -->

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/level3_channel6"
android:layout_alignParentRight="true"
android:layout_marginBottom="12dp"
android:layout_marginRight="60dp"
android:src="@drawable/channel5" />

</RelativeLayout>

<!-- 中间 -->

<RelativeLayout
android:id="@+id/level2"
android:layout_width="180dp"
android:layout_height="90dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:background="@drawable/level2" >


<!-- 搜索图标 -->

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="8dp"
android:src="@drawable/icon_search" />


<!-- 菜单图标 -->

<ImageView
android:id="@+id/level2_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="5dp"
android:src="@drawable/icon_menu" />


<!-- youku -->

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="8dp"
android:src="@drawable/icon_myyouku" />

</RelativeLayout>

<!-- 内侧 -->

<RelativeLayout
android:id="@+id/level1"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:background="@drawable/level1" >


<!-- home图标 -->

<ImageView
android:id="@+id/level1_home"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/icon_home" />

</RelativeLayout>
</RelativeLayout>

</RelativeLayout>
public class MainActivity extends Activity implements OnClickListener {

private ImageView mIvHome;
private ImageView mIvMenu;

private boolean isLevel1Display = true;// 用来标记内侧的容器是否显示
private boolean isLevel2Display = true;// 用来标记中间的容器是否显示
private boolean isLevel3Display = true;// 用来标记外侧的容器是否显示

private RelativeLayout mLevel1;// 内侧的容器
private RelativeLayout mLevel2;// 中间的容器
private RelativeLayout mLevel3;// 外侧的容器

private int mCurrentAnimationCount = 0;// 用来记录当前动画的个数

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

initView();
}

private void initView() {
mIvHome = (ImageView) findViewById(R.id.level1_home);
mIvMenu = (ImageView) findViewById(R.id.level2_menu);

mLevel1 = (RelativeLayout) findViewById(R.id.level1);
mLevel2 = (RelativeLayout) findViewById(R.id.level2);
mLevel3 = (RelativeLayout) findViewById(R.id.level3);

// 设置点击事件
mIvHome.setOnClickListener(this);
mIvMenu.setOnClickListener(this);
}

@Override
public void onClick(View v) {
if (v == mIvHome) {
clickLevel1Home();
} else if (v == mIvMenu) {
clickLevel2Menu();
}
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// keyCode点击的按钮
if (keyCode == KeyEvent.KEYCODE_MENU) {
System.out.println("点击了实体menu");

clickHardwareMenu();

// 如果点击的是menu按钮,就去消费掉这个点击事件,不向下传递
return true;
}

return super.onKeyUp(keyCode, event);
}

/**
* 点击实体的menu按钮
*/

private void clickHardwareMenu() {
// 如果现在正在有动画在执行的情况下,就不去执行点击的逻辑
if (mCurrentAnimationCount != 0) {
return;
}

// 如果全部可见,就全部收起来
if (isLevel1Display && isLevel2Display && isLevel3Display) {
hiddenLevel(mLevel1, 200);
hiddenLevel(mLevel2, 100);
hiddenLevel(mLevel3, 0);// 不需要延时

// 状态
isLevel1Display = false;
isLevel2Display = false;
isLevel3Display = false;
return;
}

// 如果全部都不显示,全部显示
if (!isLevel1Display && !isLevel2Display && !isLevel3Display) {
showLevel(mLevel1, 0);
showLevel(mLevel2, 100);
showLevel(mLevel3, 200);

// 状态
isLevel1Display = true;
isLevel2Display = true;
isLevel3Display = true;
return;
}
// 如果外侧不可见,内侧和中间可见,全部不见
if (!isLevel3Display && isLevel1Display && isLevel2Display) {

hiddenLevel(mLevel1, 100);
hiddenLevel(mLevel2, 0);

isLevel1Display = false;
isLevel2Display = false;

return;
}

if (!isLevel3Display && !isLevel2Display && isLevel1Display) {

// 隐藏level1
hiddenLevel(mLevel1, 0);

isLevel1Display = false;
return;
}
}

// 当点击中间的menu按钮
private void clickLevel2Menu() {
// 如果现在正在有动画在执行的情况下,就不去执行点击的逻辑
if (mCurrentAnimationCount != 0) {
return;
}

// 如果外侧可见,隐藏外侧
if (isLevel3Display) {
// 隐藏外侧
hiddenLevel(mLevel3, 0);

// 设置状态
isLevel3Display = false;
} else {
showLevel(mLevel3, 0);

isLevel3Display = true;
}

}

// 点击home按钮的逻辑操作
private void clickLevel1Home() {
// 点击内侧的home

// 如果现在正在有动画在执行的情况下,就不去执行点击的逻辑
if (mCurrentAnimationCount != 0) {
return;
}

// 如果外侧和中间都显示的情况下, 收起外侧和中间
if (isLevel2Display && isLevel3Display) {
// 收起外侧和中间

// 动画的去关闭view
hiddenLevel(mLevel2, 100);
hiddenLevel(mLevel3, 0);

// 改变状态记录
isLevel2Display = false;
isLevel3Display = false;
return;
}

// 如果外侧和中间都不显示的情况下,只显示中间
if (!isLevel2Display && !isLevel3Display) {

// 去显示中间的

showLevel(mLevel2, 0);

// 状态的改变
isLevel2Display = true;
return;
}

// 如果只显示中间,不显示外侧的情况下,收起中间
if (isLevel2Display && !isLevel3Display) {
// 收起中间
hiddenLevel(mLevel2, 0);

// 状态的改变
isLevel2Display = false;
return;
}
}

/**
* 动画的隐藏level
*
* @param level
* @param startOffset
*/

private void hiddenLevel(ViewGroup level, long startOffset) {
// 动画的去阴影

// 让viewGroup和他的孩子不可用
level.setEnabled(false);
int count = level.getChildCount();
for (int i = 0; i < count; i++) {
level.getChildAt(i).setEnabled(false);
}

// 做旋转动画
RotateAnimation rotate = new RotateAnimation(0,// 起始的角度
-180, // 旋转的角度
RotateAnimation.RELATIVE_TO_SELF,// 旋转的中轴的x是相对谁的
0.5f, RotateAnimation.RELATIVE_TO_SELF, 1f);
rotate.setDuration(400);
rotate.setFillAfter(true);// 保持为旋转后的位置
rotate.setStartOffset(startOffset);// 设置动画的延时
rotate.setAnimationListener(new AnimationListener() {

@Override
public void onAnimationStart(Animation animation) {
// 动画开启
mCurrentAnimationCount++;
}

@Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub

}

@Override
public void onAnimationEnd(Animation animation) {
// 动画结束
mCurrentAnimationCount--;
}
});
level.startAnimation(rotate);

}

private void showLevel(ViewGroup level, long startOffset) {
// 动画的去阴影

level.setEnabled(true);
int count = level.getChildCount();
for (int i = 0; i < count; i++) {
level.getChildAt(i).setEnabled(true);
}

// 做旋转动画
RotateAnimation rotate = new RotateAnimation(-180,// 起始的角度
0, // 旋转的角度
RotateAnimation.RELATIVE_TO_SELF,// 旋转的中轴的x是相对谁的
0.5f, RotateAnimation.RELATIVE_TO_SELF, 1f);
rotate.setDuration(400);
rotate.setFillAfter(true);// 保持为旋转后的位置
rotate.setStartOffset(startOffset);// 设置动画的延时

rotate.setAnimationListener(new AnimationListener() {

@Override
public void onAnimationStart(Animation animation) {
// 动画开启
mCurrentAnimationCount++;
}

@Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub

}

@Override
public void onAnimationEnd(Animation animation) {
// 动画结束
mCurrentAnimationCount--;
}
});
level.startAnimation(rotate);
}

}

封装:

public class YoukuMenu extends RelativeLayout implements OnClickListener {
private ImageView mIvHome;
private ImageView mIvMenu;

private boolean isLevel1Display = true;// 用来标记内侧的容器是否显示
private boolean isLevel2Display = true;// 用来标记中间的容器是否显示
private boolean isLevel3Display = true;// 用来标记外侧的容器是否显示

private RelativeLayout mLevel1;// 内侧的容器
private RelativeLayout mLevel2;// 中间的容器
private RelativeLayout mLevel3;// 外侧的容器

private int mCurrentAnimationCount = 0;// 用来记录当前动画的个数

// new
public YoukuMenu(Context context) {
this(context, null);
}

// xml
public YoukuMenu(Context context, AttributeSet attrs) {
super(context, attrs);

// 将xml挂载到class上
View.inflate(context, R.layout.youku_menu, this);

initView();
}

private void initView() {

// 设置focus
setFocusableInTouchMode(true);

mIvHome = (ImageView) findViewById(R.id.level1_home);
mIvMenu = (ImageView) findViewById(R.id.level2_menu);

mLevel1 = (RelativeLayout) findViewById(R.id.level1);
mLevel2 = (RelativeLayout) findViewById(R.id.level2);
mLevel3 = (RelativeLayout) findViewById(R.id.level3);

// 设置点击事件
mIvHome.setOnClickListener(this);
mIvMenu.setOnClickListener(this);
}

@Override
public void onClick(View v) {
if (v == mIvHome) {
clickLevel1Home();
} else if (v == mIvMenu) {
clickLevel2Menu();
}
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// keyCode点击的按钮
if (keyCode == KeyEvent.KEYCODE_MENU) {
System.out.println("点击了实体menu");

clickHardwareMenu();

// 如果点击的是menu按钮,就去消费掉这个点击事件,不向下传递
return true;
}

return super.onKeyUp(keyCode, event);
}

/**
* 点击实体的menu按钮
*/

private void clickHardwareMenu() {
// 如果现在正在有动画在执行的情况下,就不去执行点击的逻辑
if (mCurrentAnimationCount != 0) {
return;
}

// 如果全部可见,就全部收起来
if (isLevel1Display && isLevel2Display && isLevel3Display) {
hiddenLevel(mLevel1, 200);
hiddenLevel(mLevel2, 100);
hiddenLevel(mLevel3, 0);// 不需要延时

// 状态
isLevel1Display = false;
isLevel2Display = false;
isLevel3Display = false;
return;
}

// 如果全部都不显示,全部显示
if (!isLevel1Display && !isLevel2Display && !isLevel3Display) {
showLevel(mLevel1, 0);
showLevel(mLevel2, 100);
showLevel(mLevel3, 200);

// 状态
isLevel1Display = true;
isLevel2Display = true;
isLevel3Display = true;
return;
}
// 如果外侧不可见,内侧和中间可见,全部不见
if (!isLevel3Display && isLevel1Display && isLevel2Display) {

hiddenLevel(mLevel1, 100);
hiddenLevel(mLevel2, 0);

isLevel1Display = false;
isLevel2Display = false;

return;
}

if (!isLevel3Display && !isLevel2Display && isLevel1Display) {

// 隐藏level1
hiddenLevel(mLevel1, 0);

isLevel1Display = false;
return;
}
}

// 当点击中间的menu按钮
private void clickLevel2Menu() {
// 如果现在正在有动画在执行的情况下,就不去执行点击的逻辑
if (mCurrentAnimationCount != 0) {
return;
}

// 如果外侧可见,隐藏外侧
if (isLevel3Display) {
// 隐藏外侧
hiddenLevel(mLevel3, 0);

// 设置状态
isLevel3Display = false;
} else {
showLevel(mLevel3, 0);

isLevel3Display = true;
}

}

// 点击home按钮的逻辑操作
private void clickLevel1Home() {
// 点击内侧的home

// 如果现在正在有动画在执行的情况下,就不去执行点击的逻辑
if (mCurrentAnimationCount != 0) {
return;
}

// 如果外侧和中间都显示的情况下, 收起外侧和中间
if (isLevel2Display && isLevel3Display) {
// 收起外侧和中间

// 动画的去关闭view
hiddenLevel(mLevel2, 100);
hiddenLevel(mLevel3, 0);

// 改变状态记录
isLevel2Display = false;
isLevel3Display = false;
return;
}

// 如果外侧和中间都不显示的情况下,只显示中间
if (!isLevel2Display && !isLevel3Display) {

// 去显示中间的

showLevel(mLevel2, 0);

// 状态的改变
isLevel2Display = true;
return;
}

// 如果只显示中间,不显示外侧的情况下,收起中间
if (isLevel2Display && !isLevel3Display) {
// 收起中间
hiddenLevel(mLevel2, 0);

// 状态的改变
isLevel2Display = false;
return;
}
}

/**
* 动画的隐藏level
*
* @param level
* @param startOffset
*/

private void hiddenLevel(ViewGroup level, long startOffset) {
// 动画的去阴影

// 让viewGroup和他的孩子不可用
level.setEnabled(false);
int count = level.getChildCount();
for (int i = 0; i < count; i++) {
level.getChildAt(i).setEnabled(false);
}

// 做旋转动画
RotateAnimation rotate = new RotateAnimation(0,// 起始的角度
-180, // 旋转的角度
RotateAnimation.RELATIVE_TO_SELF,// 旋转的中轴的x是相对谁的
0.5f, RotateAnimation.RELATIVE_TO_SELF, 1f);
rotate.setDuration(400);
rotate.setFillAfter(true);// 保持为旋转后的位置
rotate.setStartOffset(startOffset);// 设置动画的延时
rotate.setAnimationListener(new AnimationListener() {

@Override
public void onAnimationStart(Animation animation) {
// 动画开启
mCurrentAnimationCount++;
}

@Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub

}

@Override
public void onAnimationEnd(Animation animation) {
// 动画结束
mCurrentAnimationCount--;
}
});
level.startAnimation(rotate);

}

private void showLevel(ViewGroup level, long startOffset) {
// 动画的去阴影

level.setEnabled(true);
int count = level.getChildCount();
for (int i = 0; i < count; i++) {
level.getChildAt(i).setEnabled(true);
}

// 做旋转动画
RotateAnimation rotate = new RotateAnimation(-180,// 起始的角度
0, // 旋转的角度
RotateAnimation.RELATIVE_TO_SELF,// 旋转的中轴的x是相对谁的
0.5f, RotateAnimation.RELATIVE_TO_SELF, 1f);
rotate.setDuration(400);
rotate.setFillAfter(true);// 保持为旋转后的位置
rotate.setStartOffset(startOffset);// 设置动画的延时

rotate.setAnimationListener(new AnimationListener() {

@Override
public void onAnimationStart(Animation animation) {
// 动画开启
mCurrentAnimationCount++;
}

@Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub

}

@Override
public void onAnimationEnd(Animation animation) {
// 动画结束
mCurrentAnimationCount--;
}
});
level.startAnimation(rotate);
}

}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >


<!-- 外侧 -->

<RelativeLayout
android:id="@+id/level3"
android:layout_width="280dp"
android:layout_height="140dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:background="@drawable/level3" >


<!-- channel1 -->

<ImageView
android:id="@+id/level3_channel1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="8dp"
android:src="@drawable/channel1" />


<!-- channel2 -->

<ImageView
android:id="@+id/level3_channel2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/level3_channel1"
android:layout_marginBottom="12dp"
android:layout_marginLeft="30dp"
android:src="@drawable/channel2" />


<!-- channel3 -->

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/level3_channel2"
android:layout_marginBottom="12dp"
android:layout_marginLeft="60dp"
android:src="@drawable/channel3" />


<!-- channel4 -->

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="5dp"
android:src="@drawable/channel4" />


<!-- channel7 -->

<ImageView
android:id="@+id/level3_channel7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="8dp"
android:src="@drawable/channel7" />


<!-- channel6 -->

<ImageView
android:id="@+id/level3_channel6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/level3_channel7"
android:layout_alignParentRight="true"
android:layout_marginBottom="12dp"
android:layout_marginRight="30dp"
android:src="@drawable/channel6" />


<!-- channel5 -->

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/level3_channel6"
android:layout_alignParentRight="true"
android:layout_marginBottom="12dp"
android:layout_marginRight="60dp"
android:src="@drawable/channel5" />

</RelativeLayout>

<!-- 中间 -->

<RelativeLayout
android:id="@+id/level2"
android:layout_width="180dp"
android:layout_height="90dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:background="@drawable/level2" >


<!-- 搜索图标 -->

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="8dp"
android:src="@drawable/icon_search" />


<!-- 菜单图标 -->

<ImageView
android:id="@+id/level2_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="5dp"
android:src="@drawable/icon_menu" />


<!-- youku -->

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="8dp"
android:src="@drawable/icon_myyouku" />

</RelativeLayout>

<!-- 内侧 -->

<RelativeLayout
android:id="@+id/level1"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:background="@drawable/level1" >


<!-- home图标 -->

<ImageView
android:id="@+id/level1_home"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/icon_home" />

</RelativeLayout>

</RelativeLayout>
public class DemoActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_demo);
}
}

参考:Android自定义控件