【Android进阶】自定义控件实现底部扇形展开菜单效果
这个项目是优化的其他人的,主要优化了界面菜单的显示,下面开始。
先看效果图
项目的总结构
下面开始贴代码,由于必要的地方都添加了注释,所以不过多讲解
anim_button.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" > <Button
android:id="@+id/btn_sleep"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:visibility="invisible"
android:background="@drawable/composer_sleep" /> <Button
android:id="@+id/btn_thought"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:visibility="invisible"
android:background="@drawable/composer_thought" /> <Button
android:id="@+id/btn_music"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:visibility="invisible"
android:background="@drawable/composer_music" /> <Button
android:id="@+id/btn_place"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:visibility="invisible"
android:background="@drawable/composer_place" /> <Button
android:id="@+id/btn_with"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:visibility="invisible"
android:background="@drawable/composer_with" /> <Button
android:id="@+id/btn_camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:visibility="invisible"
android:background="@drawable/composer_camera" /> <Button
android:id="@+id/btn_menu"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:background="@drawable/friends_delete" /> </RelativeLayout>
主界面的布局main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" > <com.example.anim.AnimButtons
android:id="@+id/animButtons"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
最重要的,自定义控件的实现
AnimButtons.java
package com.example.anim; import android.R.anim;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.RelativeLayout; /**
* 底部展开菜单实现
*
* @author ZhaoKaiQiang
*
* Time:2014年3月11日
*/
public class AnimButtons extends RelativeLayout { private Context context;
private int leftMargin = 0, bottomMargin = 0;
private final int buttonWidth = 58;// 图片宽高
private final int r = 180;// 半径
private final int maxTimeSpent = 200;// 最长动画耗时
private final int minTimeSpent = 80;// 最短动画耗时
private int intervalTimeSpent;// 每相邻2个的时间间隔
private Button[] btns;
private Button btn_menu;
private RelativeLayout.LayoutParams params;
private boolean isOpen = false;// 是否菜单打开状态
private float angle;// 每个按钮之间的夹角 public int bottomMargins = this.getMeasuredHeight() - buttonWidth
- bottomMargin; public AnimButtons(Context context) {
super(context);
this.context = context;
} public AnimButtons(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
} @Override
protected void onFinishInflate() {
super.onFinishInflate();
View view = LayoutInflater.from(context).inflate(R.layout.anim_buttons,
this); initButtons(view); } private void initButtons(View view) {
// 可以根据按钮的个数自己增减
btns = new Button[4];
btns[0] = (Button) view.findViewById(R.id.btn_camera);
btns[1] = (Button) view.findViewById(R.id.btn_with);
btns[2] = (Button) view.findViewById(R.id.btn_place);
btns[3] = (Button) view.findViewById(R.id.btn_music);
// btns[4] = (Button) view.findViewById(R.id.btn_thought);
// btns[5] = (Button) view.findViewById(R.id.btn_sleep);
btn_menu = (Button) view.findViewById(R.id.btn_menu); leftMargin = ((RelativeLayout.LayoutParams) (btn_menu.getLayoutParams())).leftMargin;
bottomMargin = ((RelativeLayout.LayoutParams) (btn_menu
.getLayoutParams())).bottomMargin; for (int i = 0; i < btns.length; i++) {
// 初始化的时候按钮重合
btns[i].setLayoutParams(btn_menu.getLayoutParams());
btns[i].setTag(String.valueOf(i));
btns[i].setOnClickListener(clickListener);
} intervalTimeSpent = (maxTimeSpent - minTimeSpent) / btns.length;
angle = (float) Math.PI / (2 * (btns.length - 1));
} @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
bottomMargins = this.getMeasuredHeight() - buttonWidth - bottomMargin;
btn_menu.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
if (!isOpen) {
openMenu();
} else {
closeMenu();
}
}
}); } public void closeMenu() {
if (isOpen == true) {
isOpen = false;
for (int i = 0; i < btns.length; i++) {
float xLenth = (float) (r * Math.sin(i * angle));
float yLenth = (float) (r * Math.cos(i * angle));
btns[i].startAnimation(animTranslate(-xLenth, yLenth,
leftMargin, bottomMargins, btns[i], maxTimeSpent - i
* intervalTimeSpent));
btns[i].setVisibility(View.INVISIBLE);
}
}
} public void openMenu() {
isOpen = true;
for (int i = 0; i < btns.length; i++) {
float xLenth = (float) (r * Math.sin(i * angle));
float yLenth = (float) (r * Math.cos(i * angle));
btns[i].startAnimation(animTranslate(xLenth, -yLenth, leftMargin
+ (int) xLenth, bottomMargins - (int) yLenth, btns[i],
minTimeSpent + i * intervalTimeSpent));
btns[i].setVisibility(View.VISIBLE);
} } private Animation animScale(float toX, float toY) {
Animation animation = new ScaleAnimation(1.0f, toX, 1.0f, toY,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
animation.setInterpolator(context,
anim.accelerate_decelerate_interpolator);
animation.setDuration(400);
animation.setFillAfter(false);
return animation; } private Animation animTranslate(float toX, float toY, final int lastX,
final int lastY, final Button button, long durationMillis) {
Animation animation = new TranslateAnimation(0, toX, 0, toY);
animation.setAnimationListener(new AnimationListener() { @Override
public void onAnimationStart(Animation animation) { } @Override
public void onAnimationRepeat(Animation animation) { } @Override
public void onAnimationEnd(Animation animation) {
params = new RelativeLayout.LayoutParams(0, 0);
params.height = buttonWidth;
params.width = buttonWidth;
params.setMargins(lastX, lastY, 0, 0);
button.setLayoutParams(params);
button.clearAnimation(); }
});
animation.setDuration(durationMillis);
return animation;
} View.OnClickListener clickListener = new View.OnClickListener() { @Override
public void onClick(View v) {
int selectedItem = Integer.parseInt((String) v.getTag());
for (int i = 0; i < btns.length; i++) {
if (i == selectedItem) {
btns[i].startAnimation(animScale(2.0f, 2.0f));
} else {
btns[i].startAnimation(animScale(0.0f, 0.0f));
}
}
if (onButtonClickListener != null) {
onButtonClickListener.onButtonClick(v, selectedItem);
}
} }; public boolean isOpen() {
return isOpen;
} private OnButtonClickListener onButtonClickListener; public interface OnButtonClickListener {
void onButtonClick(View v, int id);
} public void setOnButtonClickListener(
OnButtonClickListener onButtonClickListener) {
this.onButtonClickListener = onButtonClickListener;
} }
AnimButtonsActivity.java
package com.example.anim; import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
/**
* 主界面
* @author ZhaoKaiQiang
*
* Time:2014年3月11日
*/
public class AnimButtonsActivity extends Activity { private AnimButtons animButtons; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
animButtons = (AnimButtons) findViewById(R.id.animButtons);
animButtons
.setOnButtonClickListener(new AnimButtons.OnButtonClickListener() {
@Override
public void onButtonClick(View v, int id) {
Toast.makeText(AnimButtonsActivity.this, "id-->" + id,
0).show();
animButtons.closeMenu();
}
});
}
}
如有问题,请留言
【Android进阶】自定义控件实现底部扇形展开菜单效果的更多相关文章
- 商城项目实战 | 1.1 Android 仿京东商城底部布局的选择效果 —— Selector 选择器的实现
前言 本文为菜鸟窝作者刘婷的连载."商城项目实战"系列来聊聊仿"京东淘宝的购物商城"如何实现. 京东商城的底部布局的选择效果看上去很复杂,其实很简单,这主要是要 ...
- Android进阶(二十六)MenuInflater实现菜单添加
MenuInflater实现菜单添加 前言 之前实现的Android项目中可以实现菜单的显示.但是再次调试项目时发现此功能已无法实现,很是令人费解.难道是因为自己手机Android系统的问题?尝试通过 ...
- Android开发实战之底部Dialog弹出效果
在Android开发中,有很多情况下我们需要使用到对话框,遗憾的是,安卓自带的对话框样式不能满足我们实际的需要,所以往往需要我们自定义对话框,具体做法:写一个对话框继承自Dialog实现他的一个构造方 ...
- Android进阶(二十八)上下文菜单ContextMenu使用案例
上下文菜单ContextMenu使用案例 前言 回顾之前的应用程序,发现之前创建的选项菜单无法显示了.按照正常逻辑来说,左图中在"商品信息"一栏中应该存在选项菜单,用户可进行分享等 ...
- Android实现下拉导航选择菜单效果
本文介绍在Android中如何实现下拉导航选择菜单效果. 关于下拉导航选择菜单效果在新闻客户端中用的比较多,当然也可以用在其他的项目中,这样可以很方便的选择更多的菜单.我们可以让我们的应用顶部有左 ...
- Android 实现形态各异的双向側滑菜单 自己定义控件来袭
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39670935.本文出自:[张鸿洋的博客] 1.概述 关于自己定义控件側滑已经写了 ...
- Android底部菜单栏+顶部菜单
底部菜单栏+顶部菜单(wechat)demo http://blog.csdn.net/evankaka/article/details/44121457 底部菜单demo http://blog.c ...
- 《Android进阶之光》--Material Design
接上篇<Android进阶之光>--Android新特性 No1: 组件: 1)底部工作条-Bottom Sheets 2)卡片-Cards 3)提示框-Dialogs 4)菜单-Menu ...
- Android笔记--自定义控件仿遥控器的圆形上下左右OK圆盘按钮
原文:Android笔记--自定义控件仿遥控器的圆形上下左右OK圆盘按钮 上面就是几张预览图!代码在最底下 主要就两个步骤,画图.监听点击 1.整个控件基本上是一步步画出来的,重写onDraw方法开始 ...
随机推荐
- Android----------WindowManager
我们Android平台是一个又一个的Activity组成的,每个Activity有一个或者多个View构成.所以说.当我们想显示一个界面的时候,我们首先想到的是建立一个Activity,然后全部的操作 ...
- POJ1789 Truck History 【最小生成树Prim】
Truck History Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 18981 Accepted: 7321 De ...
- random_shuffle (stl算法)打乱顺序 - 飞不会的日志 - 网易博客
random_shuffle (stl算法)打乱顺序 - 飞不会的日志 - 网易博客 random_shuffle (stl算法)打乱顺序 2012-03-31 10:39:11| 分类: 算法 | ...
- hbase总结(二)-hbase安装
本篇介绍两种HBase的安装方式:本地安装方式和伪分布式安装方式. 安装的前提条件是已经安装成功了hadoop,并且hadoop的版本号要和hbase的版本号相匹配. 我将要安装的hbase是hbas ...
- hdu 1561 The more, The Better (依赖背包 树形dp)
题目: 链接:点击打开链接 题意: 非常明显的依赖背包. 思路: dp[i][j]表示以i为根结点时攻击j个城堡得到的最大值.(以i为根的子树选择j个点所能达到的最优值) dp[root][j] = ...
- JAVA card 应用开发(三) 把APPLET(CAP文件)装载到卡片
依据前面两篇博文.我们能够在Eclipse上建立好Applet.而且能够有多个AID.能够选择不同的应用. 那么,以上我们都是基于模拟环境的逻辑,实际上有些函数接口是须要实际的环境.就是说我们须要把A ...
- WPF界面设计技巧(8)—自制山寨版CheckListBox
原文:WPF界面设计技巧(8)-自制山寨版CheckListBox 近年来IT市场山寨横行啊,我们今天也来发扬一下山寨精神,搞个自制的CheckListBox出来. 喏,CheckListBox 就是 ...
- poj Budget
Budget 建图好题.不知道为什么提交一直TLE. 然后.该了几次,看了别人的普通网络流都过了. 我觉得可能是卡DINIC的某些部分吧.这题就是一道普通的上下界最小流. 建图麻烦,所以说一下建图吧. ...
- 本科非cs菜鸟计算机面试实录
两年制小硕,本硕期间差不多都打酱油的.本科非cs专业,硕士cs,编程基础一般,专业基础尚可.研究生期间分析分析了pgsql数据库的源码:同时实验室一些杂项目:自己业余为了应试读了些计算机书.自己当时q ...
- 你真的了解try{ return }finally{}中的return?(转)
今天去逛论坛 时发现了一个很有趣的问题: 谁能给我我解释一下这段程序的结果为什么是:2.而不是:3 代码如下: class Test { public int aaa() { int x = 1; t ...