Android自定义控件之仿优酷菜单
简介
本文将介绍如何通过自定义ViewGroup实现仿优酷菜单的效果,主要涉及以下几个方面:
- 自定义ViewGroup的基本概念
- 仿优酷菜单的实现过程
- 示例展示说明
自定义ViewGroup
ViewGroup是View的子类,可以包含多个子View,是Android App中布局最常用的容器之一。自定义ViewGroup可以让我们更好的控制App的布局效果,并实现一些自定义的交互效果。
自定义ViewGroup需要重写ViewGroup的三个方法:
- onMeasure()
- onLayout()
-
onDraw()
-
onMeasure():是ViewGroup中最重要的方法,用于测量ViewGroup的大小。
- onLayout():用于确定ViewGroup中子View的位置。
- onDraw():用于绘制ViewGroup的背景。
仿优酷菜单的实现
准备工作
在布局文件中引入自定义的ViewGroup:
<com.example.menu.MyMenu
android:id="@+id/my_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
自定义ViewGroup
首先,我们要继承ViewGroup类,并且实现其构造方法和测量子View的方法onMeasure()。
public class MyMenu extends ViewGroup {
public MyMenu(Context context) {
super(context);
init();
}
public MyMenu(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
...
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
...
}
}
在init()方法中,我们可以配置ViewGroup的默认属性,比如菜单的宽度和高度,以及子View的颜色。
private void init() {
setBackgroundColor(Color.WHITE);
setClickable(true);
setWillNotDraw(false);
setOrientation(VERTICAL);
mMenuWidth = dip2px(getContext(), 200);
mMenuHeight = dip2px(getContext(), 50);
mMenuItemWidth = dip2px(getContext(), 50);
mMenuItemHeight = dip2px(getContext(), 50);
mMenuItemCount = 4;
mMenuItemColor = new int[] {Color.RED, Color.BLUE, Color.YELLOW, Color.GREEN};
}
接下来,我们重写onMeasure()方法,通过测量子View的宽度和高度,计算出ViewGroup的宽度和高度。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取到ViewGroup的宽度和高度
//分别获取其宽度测量模式,以及宽度数值
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
//先测量菜单项的宽高,用于确定菜单的大小
int itemWidthSpec = MeasureSpec.makeMeasureSpec(mMenuItemWidth, MeasureSpec.EXACTLY);
int itemHeightSpec = MeasureSpec.makeMeasureSpec(mMenuItemHeight, MeasureSpec.EXACTLY);
for (int i = 0; i < mMenuItemCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(itemWidthSpec, itemHeightSpec);
}
//菜单的宽度设置为所有菜单项的宽度之和
int menuWidth = mMenuWidth;
int menuHeight = mMenuHeight * mMenuItemCount;
//根据ViewGroup的宽度和高度,设置宽度和高度的测量模式
//宽度和高度的模式必须为MeasureSpec、AT_MOST
setMeasuredDimension(width, Math.min(menuHeight, height));
}
接下来,我们在onLayout()方法中,确定菜单的位置和每个菜单项的位置。
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int childTop = getHeight() - mMenuHeight;
//动画结束时,子菜单项显示的行为,即未展开时,其他子菜单项收起
if (!mExpanded) {
for (int i = 1; i < mMenuItemCount; i++) {
View child = getChildAt(i);
child.setVisibility(GONE);
}
}
for (int i = 0; i < mMenuItemCount; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
int childLeft = getWidth() - mMenuItemWidth - getPaddingRight();
childTop -= mMenuItemHeight;
child.layout(childLeft, childTop, childLeft + mMenuItemWidth, childTop + mMenuItemHeight);
}
}
在onDraw() 方法中,我们可以绘制ViewGroup的背景,这里可以通过Paint和Canvas绘制一些特定效果,比如渐变色等。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Rect rect = new Rect(0, getHeight() - mMenuHeight * mMenuItemCount, getWidth(), getHeight());
canvas.drawBitmap(getBackgroundBitmap(getWidth(), getHeight()), 0, 0, null);
canvas.drawRect(rect, mShadowPaint);
}
实现菜单缩放动画
在自定义ViewGroup的基础上,我们还需要实现菜单的展开和收起动画。
首先,我们可以通过OnClickListener监听菜单的点击事件,并在事件处理中添加动画效果。
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (mAnimating) {
return;
}
if (!mExpanded) {
expandMenu();
} else {
collapseMenu();
}
}
});
接下来,我们分别实现菜单的展开和收起动画。
private void expandMenu() {
//展开MenuItem时为了制造一些阻尼效果,先移动一段距离,然后再移动过去
int distance = ((getHeight() - mMenuHeight) / (mMenuItemCount - 1)) + mMenuItemHeight / 2;
for (int i = 0; i < mMenuItemCount - 1; i++) {
final View child = getChildAt(i + 1);
final int x = i + 1;
final int y = mMenuItemCount - x;
child.setVisibility(VISIBLE);
AnimationSet animationSet = new AnimationSet(true);
animationSet.setInterpolator(new OvershootInterpolator());
Animation animation = null;
animation = new TranslateAnimation(getPaddingLeft(), getPaddingLeft(), distance * y, 0);
animation.setDuration(mAnimationDuration);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
mAnimating = true;
}
@Override
public void onAnimationEnd(Animation animation) {
child.clearAnimation();
mAnimating = false;
mExpanded = true;
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
animationSet.addAnimation(animation);
child.startAnimation(animationSet);
}
}
private void collapseMenu() {
//收缩右侧的MenuItem,只留中心点
int distance = ((getHeight() - mMenuHeight) / (mMenuItemCount - 1)) + mMenuItemHeight / 2;
for (int i = 0; i < mMenuItemCount - 1; i++) {
final View child = getChildAt(i + 1);
final int x = i + 1;
final int y = mMenuItemCount - x;
AnimationSet animationSet = new AnimationSet(true);
animationSet.setInterpolator(new OvershootInterpolator());
Animation animation = null;
animation = new TranslateAnimation(getPaddingLeft(), getPaddingLeft(), 0, distance * y);
animation.setDuration(mAnimationDuration);
animation.setFillAfter(false);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
mAnimating = true;
}
@Override
public void onAnimationEnd(Animation animation) {
int childCount = getChildCount();
for (int i = 1; i < childCount; i++) {
View child = getChildAt(i);
child.clearAnimation();
child.setVisibility(GONE);
}
mAnimating = false;
mExpanded = false;
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
animationSet.addAnimation(animation);
child.startAnimation(animationSet);
}
}
示例展示说明
我们现在来演示一下,如何在自定义控件中实现菜单缩放动画。
示例1 仿优酷菜单
首先,我们在菜单中添加一个菜单项,用于展示菜单展开和收起动画。
<com.example.menu.MyMenu
android:id="@+id/my_menu"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_menu"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_menu" />
</com.example.menu.MyMenu>
接着,我们通过代码实现菜单的展开和收起动画。
final MyMenu menu = findViewById(R.id.my_menu);
final ImageView ivMenu = findViewById(R.id.iv_menu);
ivMenu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (menu.isAnimating()) {
return;
}
if (!menu.isExpanded()) {
ivMenu.animate().rotation(135).setDuration(300).start();
menu.expandMenu();
} else {
ivMenu.animate().rotation(0).setDuration(300).start();
menu.collapseMenu();
}
}
});
示例2 仿开心网播放器
接下来,我们来演示一下仿开心网播放器的例子。
<com.example.menu.MyMenu
android:id="@+id/my_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:menu_type="player">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="100dp"
android:gravity="center"
android:orientation="horizontal"
app:layout_my_menu_item_color="@color/colorPrimary"
app:layout_my_menu_item_count="3">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:src="@drawable/ic_player_album_list"
app:layout_my_menu_item_width="50dp"
app:layout_my_menu_item_height="50dp"/>
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:src="@drawable/ic_player_equalizer"
app:layout_my_menu_item_width="50dp"
app:layout_my_menu_item_height="50dp"/>
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:src="@drawable/ic_player_sleep_off"
app:layout_my_menu_item_width="50dp"
app:layout_my_menu_item_height="50dp"/>
</LinearLayout>
</com.example.menu.MyMenu>
我们可以在menu_type中设置菜单的显示类型,比如"player"表示播放器中的菜单。
menu.setType(TypedArrayUtils.getString(attr, R.styleable.MyMenu_layout_my_menu_type, ""));
结语
自定义ViewGroup能够让我们更加灵活地控制Android App中的布局效果,提升App的用户体验。在这里,我们通过自定义ViewGroup的方式实现了一个仿优酷菜单的效果,并实现了菜单的缩放动画。通过不断学习和实践,我们可以更加熟练地掌握Android自定义控件的技巧和方法。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Android自定义控件之仿优酷菜单 - Python技术站