知识点
一、动画
二、自定义ViewGroup
1、自定义属性
a、attrs.xml
b、在布局文件中使用
c、在自定义控件种进行读取
2、onMeasure()
3、onLayout()
4、设置主Button旋转动画
5、为menuItem添加平移动画和旋转动画
效果
ArcMenu
package com.example.day0228;
import android.R.animator;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationSet;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
public class ArcMenu extends ViewGroup implements OnClickListener{
private final int POS_LEFT_TOP = 0;
private final int POS_LEFT_BOTTOM = 1;
private final int POS_RIGHT_TOP = 2;
private final int POS_RIGHT_BOTTOM = 3;
private Position mPosition;
private int mRadius;
private Status mStatus = Status.CLOSE;
private OnMenuItemClickListener itemClickListener;
/**
* 菜单的主按钮
*/
private View mButton;
/**
* 菜单的状态
*/
public enum Status
{
OPEN,CLOSE
}
/**
* 菜单的位置枚举类
*/
public enum Position
{
LEFT_TOP,LEFT_BOTTOM,RIGHT_TOP,RIGHT_BOTTOM
}
public void setOnMenuItemClickListener(OnMenuItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
public ArcMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//默认
mRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics());
mPosition = Position.RIGHT_BOTTOM;
//获取自定义属性
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ArcMenu, defStyleAttr, 0);
int pos = a.getInt(R.styleable.ArcMenu_position, POS_RIGHT_BOTTOM);
switch (pos) {
case POS_LEFT_TOP:
mPosition = Position.LEFT_TOP;
break;
case POS_LEFT_BOTTOM:
mPosition = Position.LEFT_BOTTOM;
break;
case POS_RIGHT_TOP:
mPosition = Position.RIGHT_TOP;
break;
case POS_RIGHT_BOTTOM:
mPosition = Position.RIGHT_BOTTOM;
break;
}
mRadius = (int) a.getDimension(R.styleable.ArcMenu_radius, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics()));
a.recycle();
}
public ArcMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ArcMenu(Context context) {
this(context, null);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int cCount = getChildCount();
for (int i = 0; i < cCount; i++) {
//测量child
measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
layoutButton();
layoutItem();
}
}
/**
* 定位子项
*/
private void layoutItem() {
int cCount = getChildCount();
for (int i = 0; i < cCount - 1; i++) {
View child = getChildAt(i+1);
//隐藏
child.setVisibility(View.GONE);
int cl = (int) (mRadius * Math.sin(Math.PI/2/(cCount-2)*i));
int ct = (int) (mRadius * Math.cos(Math.PI/2/(cCount-2)*i));
int cWidth = child.getMeasuredWidth();
int cHeight = child.getMeasuredHeight();
//如果菜单位置在底部
//左下,右下
if (mPosition == Position.LEFT_BOTTOM || mPosition == Position.RIGHT_BOTTOM) {
ct = getMeasuredHeight() - cHeight -ct;
}
//右上,右下
if (mPosition == Position.RIGHT_TOP || mPosition == Position.RIGHT_BOTTOM) {
cl = getMeasuredWidth() - cWidth - cl;
}
child.layout(cl, ct, cl + cWidth, ct + cHeight);
}
}
/**
* 定位主菜单按钮
*/
private void layoutButton() {
mButton = getChildAt(0);
mButton.setOnClickListener(this);
int l = 0;
int t = 0;
int width = mButton.getMeasuredWidth();
int height = mButton.getMeasuredHeight();
switch (mPosition) {
case LEFT_TOP:
l = 0;
t = 0;
break;
case LEFT_BOTTOM:
l = 0;
t = getMeasuredHeight()- height;
break;
case RIGHT_TOP:
l = getMeasuredWidth() - width;
t = 0;
break;
case RIGHT_BOTTOM:
l = getMeasuredWidth() - width;
t = getMeasuredHeight()- height;
break;
}
mButton.layout(l, t, l + width, t + height);
}
/**
*点击主菜单的回调接口
*/
public interface OnMenuItemClickListener {
void onClick(View view,int position);
}
@Override
public void onClick(View v) {
if (v==getChildAt(0)) {
rotateView(v,0f,360f ,1000);
toggleMenu(400);
}
// switch (v.getId()) {
// case R.id.m_button:
// rotateView(v,0f,360f ,1000);
// toggleMenu(1000);
// break;
// }
}
/**
* 切换菜单
*/
private void toggleMenu(int duration) {
//为menuItem添加平移动画和旋转动画
int cCount = getChildCount();
for (int i = 0;i < cCount - 1; i++) {
final View child = getChildAt(i + 1);
//要显示出来才有动画
child.setVisibility(View.VISIBLE);
// end 0 0;
// start
int cl = (int) (mRadius * Math.sin(Math.PI/2/(cCount-2)*i));
int ct = (int) (mRadius * Math.cos(Math.PI/2/(cCount-2)*i));
int xFlag = 1;
int yFlag = 1;
if (mPosition == Position.LEFT_TOP || mPosition == Position.LEFT_BOTTOM) {
xFlag = -1;
}
if (mPosition == Position.LEFT_TOP || mPosition == Position.RIGHT_TOP) {
yFlag = -1;
}
AnimationSet animationSet = new AnimationSet(true);
animationSet.setDuration(duration);
Animation animation = null;
//to open
if (mStatus == Status.CLOSE) {
animation = new TranslateAnimation(xFlag * cl, 0, yFlag * ct, 0);
child.setClickable(true);
child.setFocusable(true);
} else { //to close
animation = new TranslateAnimation(0, xFlag * cl, 0, yFlag * ct);
child.setClickable(false);
child.setFocusable(false);
}
animation.setStartOffset(200*(i+1)/cCount);
animation.setFillAfter(true);
animation.setDuration(duration);
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
if (mStatus == Status.CLOSE) {
child.setVisibility(View.GONE);
}
}
});
//旋转动画
Animation rotateAnim = new RotateAnimation(0, 720, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F);
animation.setDuration(duration);
animation.setFillAfter(true);
animationSet.addAnimation(rotateAnim);
animationSet.addAnimation(animation);
child.startAnimation(animationSet);
final int pos = i+1;
child.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (itemClickListener != null) {
itemClickListener.onClick(child, pos);
}
menuItemAnim(pos-1);
changeState();
}
});
}
//切换菜单状态
changeState();
}
/**
* 添加menuItem的点击动画
* @param pos
*/
protected void menuItemAnim(int pos) {
for (int i = 0;i < getChildCount()-1 ; i++) {
View child = getChildAt(i+1);
if (i == pos) {
//变大动画
child.startAnimation(scaleBigAnim(300));
} else {
//变小动画
child.startAnimation(scaleSmallAnim(300));
}
child.setClickable(false);
child.setFocusable(false);
}
}
/**
* 变大和变透明动画
* @param duration
* @return
*/
private Animation scaleBigAnim(int duration) {
AnimationSet animationSet = new AnimationSet(true);
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 4.0f, 1.0f, 4.0f, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F);
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f);
animationSet.addAnimation(scaleAnimation);
animationSet.addAnimation(alphaAnimation);
animationSet.setDuration(duration);
animationSet.setFillAfter(true);
return animationSet;
}
/**
* 变小和变透明动画
* @param duration
* @return
*/
private Animation scaleSmallAnim(int duration) {
AnimationSet animationSet = new AnimationSet(true);
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.0f, 1.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F);
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(scaleAnimation);
animationSet.setDuration(duration);
animationSet.setFillAfter(true);
return animationSet;
}
private void changeState() {
mStatus = (mStatus == Status.CLOSE)? Status.OPEN : Status.CLOSE;
}
private void rotateView(View v, float start, float end, int duration) {
Animation animation = new RotateAnimation(start, end, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F);
animation.setDuration(duration);
animation.setFillAfter(true);
v.startAnimation(animation);
}
}
attrs.xml
<resources>
<!-- 位置 -->
<attr name="position">
<enum name="left_top" value="0"></enum>
<enum name="left_bottom" value="1"></enum>
<enum name="right_top" value="2"></enum>
<enum name="right_bottom" value="3"></enum>
</attr>
<!-- 半径 -->
<attr name="radius" format="dimension"></attr>
<declare-styleable name="ArcMenu">
<!-- 声明属性 -->
<attr name="position"></attr>
<attr name="radius"></attr>
</declare-styleable>
</resources>
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"
xmlns:app="http://schemas.android.com/apk/res/com.example.day0228">
<com.example.day0228.ArcMenu
android:layout_width="match_parent"
android:layout_height="match_parent"
app:position="left_top"
app:radius="140dp"
>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/composer_button">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_icn_plus"
android:layout_centerInParent="true" />
</RelativeLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_camera"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_music"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_place"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_sleep"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_with"
/>
</com.example.day0228.ArcMenu>
<com.example.day0228.ArcMenu
android:layout_width="match_parent"
android:layout_height="match_parent"
app:position="left_bottom"
app:radius="140dp"
>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/composer_button">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_icn_plus"
android:layout_centerInParent="true" />
</RelativeLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_camera"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_music"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_place"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_sleep"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_with"
/>
</com.example.day0228.ArcMenu>
<com.example.day0228.ArcMenu
android:layout_width="match_parent"
android:layout_height="match_parent"
app:position="right_bottom"
app:radius="100dp"
>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/composer_button">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_icn_plus"
android:layout_centerInParent="true" />
</RelativeLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_camera"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_music"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_place"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_sleep"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_with"
/>
</com.example.day0228.ArcMenu>
<com.example.day0228.ArcMenu
android:layout_width="match_parent"
android:layout_height="match_parent"
app:position="right_top"
app:radius="100dp"
>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/composer_button">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_icn_plus"
android:layout_centerInParent="true" />
</RelativeLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_camera"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_music"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_place"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_sleep"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_with"
/>
</com.example.day0228.ArcMenu>
</RelativeLayout>