Android开发技巧——使用Dialog实现仿QQ的ActionSheet菜单
2015-05-28 10:40
295 查看
最近看到有人用Dialog来实现QQ的仿ActionSheet的自定义菜单,对于自己没实现过的一些控件,看着也想实现一下。于是动手了一下,发现也不难,和大家分享一下。
本文原创,转载请注明出处:http://blog.csdn.net/maosidiaoxian/article/details/46119197
在这里我也是用Dialog来实现,代码不多,这里说一下实现的过程。
在这里我们先是写一下最基本的布局文件,因为我急着想知道实现上的可行性,所以背景那些暂未修改。
所以我们要先写一个Dialog的Style,继承自系统主题:
接下来我们需要写一个类继承Dialog,来实现自己的弹出菜单。在构造方法中调用
在该类当中,我们需要拦截MENU键,处理按下MENU时菜单消失。
需要注意的是,要显示我们自己的菜单,只重写Activity的
网上传的方法是说在
接着在drawable里编写这四个背景的selector。
menu_iten_top.xml,仅上面是圆角的背景。
menu_item_middle.xml,都不为圆角:
menu_item_bottom.xml,仅下面是圆角:
menu_item_single.xml,均为圆角:
其中取消按扭使用的是均为圆角的背景,所以回到菜单的布局文件中,对其修改。并且把ListView的listSelector设为透明,添加分割线,改完如下:
接着修改ListView的每一项的背景,我们需要重写我们的Adapter,设置背景。在此之前,先定ListView的item的布局文件:
menu_item.xml
定义了文字颜色为蓝色:
同时设置取消按钮的文字也是这个颜色。
重写Adapter,代码如下:
写完之后,给Activity的下面加点文字,看看背景透明度是否如我们的所想。这下子看起来很像了。但还是觉得有所欠缺,没错,我们还缺少动画。
弹出动画:
消失动画:
然后回到ActionSheet类,把我们的rootView重构为成员变量 ,因为我们的动画要加在它身上。同时,需要定义几个成员变量,分别是显示和消失的动画以及表示正在消失的
然后是初始化动画变量,重写show和dismiss方法,加入播放动画的代码。注意,对父类的dismiss调用是在弹出动画结束之后才调用的,所以加入一个isDismissing表示这段过程,并添加一个私有方法dismissMe来调用父类的dismiss方法。代码如下:
这是初始化动画的代码,该方法在initView中调用。然后是显示和隐藏菜单的代码:
加上动画之后,更逼真了些吧。但我们还漏了一个很重要的东西 。事件!
添加
各种事件回调:
这下就基本完成了。
运行,效果如下(由于我是通过Android Studio屏幕录制先录成MP4再在线转换为GIF的,GIF有些大,所以我就不贴图了):http://v.youku.com/v_show/id_XOTY2MTM0ODM2.html
这篇博客由于主要是写实现的过程,所以有点长。实际上代码并不复杂,ActionSheet的全部代码加注释才170行。
项目地址(包括运行效果的录制视频):http://zdz.la/2pz0Ys
下一篇将写一下如何把它写成一个可复用的控件。
参考博客:http://blog.csdn.net/bbld_/article/details/39124097
本文原创,转载请注明出处:http://blog.csdn.net/maosidiaoxian/article/details/46119197
在这里我也是用Dialog来实现,代码不多,这里说一下实现的过程。
菜单的布局文件
首先我们写先一下菜单的布局文件,很简单,一个ListView菜单再加一个取消的Button。<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ListView android:id="@+id/menu_items" android:layout_width="match_parent" android:layout_height="wrap_content" android:listSelector="@android:color/transparent"/> <Button android:id="@+id/menu_cancel" android:layout_width="match_parent" android:layout_height="45dp" android:layout_marginBottom="8dp" android:layout_marginTop="8dp" android:text="取消"/> </LinearLayout>
在这里我们先是写一下最基本的布局文件,因为我急着想知道实现上的可行性,所以背景那些暂未修改。
继承Dialog实现自己的菜单
我们的对话框有几个特点,一是弹出的位置在底部,二是没有对话框的那些windowFrame层也没有标题和contentOverlay层,并且背景透明。所以我们要先写一个Dialog的Style,继承自系统主题:
<style name="ActionSheetDialog" parent="android:Theme.Dialog"> <item name="android:windowContentOverlay">@null</item> <item name="android:windowFrame">@null</item> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowNoTitle">true</item> </style>
接下来我们需要写一个类继承Dialog,来实现自己的弹出菜单。在构造方法中调用
super(Context context, int theme)方法。并且我们尝试设置gravity,来使它显示在底部。
public class ActionSheet extends Dialog { private Button mCancel; private ListView mMenuItems; private ArrayAdapter<String> mAdapter; public ActionSheet(Context context) { super(context, R.style.ActionSheetDialog); getWindow().setGravity(Gravity.BOTTOM); initView(context); } private void initView(Context context) { View rootView = View.inflate(context, R.layout.dialog_action_sheet, null); mCancel = (Button) rootView.findViewById(R.id.menu_cancel); mMenuItems = (ListView) rootView.findViewById(R.id.menu_items); mAdapter = new ArrayAdapter<String>(context, android.R.layout.simple_list_item_1); mMenuItems.setAdapter(mAdapter); this.setContentView(rootView); } public ActionSheet addMenuItem(String items) { mAdapter.add(items); return this; } public void toggle() { if (isShowing()) { dismiss(); } else { show(); } } @Override public void show() { mAdapter.notifyDataSetChanged(); super.show(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_MENU) { dismiss(); return true; } return super.onKeyDown(keyCode, event); } }
在该类当中,我们需要拦截MENU键,处理按下MENU时菜单消失。
写一个Activity来验证可行性
然后写我们的Activity,来显示我们的Dialog,看是否如我们所想。public class MainActivity extends ActionBarActivity { private ActionSheet mActionSheet; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } /** * 创建MENU */ public boolean onCreateOptionsMenu(Menu menu) { menu.add("menu").setVisible(false);// 必须创建一项,设为false之后ActionBar上不会出现菜单按钮。 return super.onCreateOptionsMenu(menu); } /** * 拦截MENU事件,显示自己的菜单 */ @Override public boolean onMenuOpened(int featureId, Menu menu) { if (mActionSheet == null) { mActionSheet = new ActionSheet(this); mActionSheet.addMenuItem("Test1").addMenuItem("Test2"); } mActionSheet.show(); return true; } }
需要注意的是,要显示我们自己的菜单,只重写Activity的
onKeyDown在那里显示是实现不了的。需要继承自
onCreateOptionsMenu方法并添加一项菜单,然后才可以在
onMenuOpened当中显示。
网上传的方法是说在
onCreateOptionsMenu添加一项,然后在
onMenuOpened中弹出我们的菜单并返回
true。但是这样写有一个问题,就是在ActionBar的右边还是会有一个菜单键。在各种尝试中,我发现了一个很简单的解决此问题的方法。就是在
onCreateOptionsMenu中添加了一项菜单之后,设为不可见。接下来在
onMenuOpened弹出菜单之后,返回true和false都可以,都不会显示系统原来的菜单了。
修改我们的菜单
上面的代码跑起来,主要的效果确实如我们所想,所以接下来我们就需要对菜单的外观进行大的修改,来让它更像是QQ的菜单。背景
首先,是菜单背景。菜单的背景共有四种,分别是在四个角中,仅上面圆角,仅下面圆角,都为圆角,都不为圆角。其次,背景都是半透明的。所以我们先在colors.xml中定义背景的颜色,包括正常状态时的颜色及按下去状态的颜色。<color name="menu_item_normal">#c9ffffff</color> <color name="menu_item_pressed">#d5dadada</color>
接着在drawable里编写这四个背景的selector。
menu_iten_top.xml,仅上面是圆角的背景。
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <shape> <corners android:topLeftRadius="@dimen/list_corner" android:topRightRadius="@dimen/list_corner"/> <solid android:color="@color/menu_item_pressed"/> </shape> </item> <item> <shape> <corners android:topLeftRadius="@dimen/list_corner" android:topRightRadius="@dimen/list_corner"/> <solid android:color="@color/menu_item_normal"/> </shape> </item> </selector>
menu_item_middle.xml,都不为圆角:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <shape> <solid android:color="@color/menu_item_pressed"/> </shape> </item> <item> <shape > <solid android:color="@color/menu_item_normal"/> </shape> </item> </selector>
menu_item_bottom.xml,仅下面是圆角:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <shape> <corners android:bottomLeftRadius="@dimen/list_corner" android:bottomRightRadius="@dimen/list_corner"/> <solid android:color="@color/menu_item_pressed"/> </shape> </item> <item> <shape> <corners android:bottomLeftRadius="@dimen/list_corner" android:bottomRightRadius="@dimen/list_corner"/> <solid android:color="@color/menu_item_normal"/> </shape> </item> </selector>
menu_item_single.xml,均为圆角:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <shape> <corners android:radius="@dimen/list_corner"/> <solid android:color="@color/menu_item_pressed"/> </shape> </item> <item> <shape> <corners android:radius="@dimen/list_corner"/> <solid android:color="@color/menu_item_normal"/> </shape> </item> </selector>
其中取消按扭使用的是均为圆角的背景,所以回到菜单的布局文件中,对其修改。并且把ListView的listSelector设为透明,添加分割线,改完如下:
<ListView android:id="@+id/menu_items" android:layout_width="match_parent" android:layout_height="wrap_content" android:divider="#c9dddddd" android:dividerHeight="1px" android:listSelector="@android:color/transparent"/> <Button android:id="@+id/menu_cancel" android:layout_width="match_parent" android:layout_height="45dp" android:layout_marginBottom="8dp" android:layout_marginTop="8dp" android:background="@drawable/menu_item_single" android:text="取消" android:textColor="@color/menu_text"/>
接着修改ListView的每一项的背景,我们需要重写我们的Adapter,设置背景。在此之前,先定ListView的item的布局文件:
menu_item.xml
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="18sp" android:textColor="@color/menu_text" android:gravity="center" android:minHeight="45dp" />
定义了文字颜色为蓝色:
<color name="menu_text">#f12162ff</color>
同时设置取消按钮的文字也是这个颜色。
重写Adapter,代码如下:
mAdapter = new ArrayAdapter<String>(context, R.layout.menu_item) { @Override public View getView(int position, View convertView, ViewGroup parent) { View view = super.getView(position, convertView, parent); setBackground(position, view); return view; } private void setBackground(int position, View view) { int count = getCount(); if (count == 1) { view.setBackgroundResource(R.drawable.menu_item_single); } else if (position == 0) { view.setBackgroundResource(R.drawable.menu_item_top); } else if (position == count - 1) { view.setBackgroundResource(R.drawable.menu_item_bottom); } else { view.setBackgroundResource(R.drawable.menu_item_middle); } } };
写完之后,给Activity的下面加点文字,看看背景透明度是否如我们的所想。这下子看起来很像了。但还是觉得有所欠缺,没错,我们还缺少动画。
动画
编写两个动画,一个是显示时弹出的,一个是消失的。弹出动画:
<?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromYDelta="100%" android:toYDelta="0" android:duration="350"> </translate>
消失动画:
<?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromYDelta="0%" android:toYDelta="100%" android:duration="350"> </translate>
然后回到ActionSheet类,把我们的rootView重构为成员变量 ,因为我们的动画要加在它身上。同时,需要定义几个成员变量,分别是显示和消失的动画以及表示正在消失的
boolean变量。
private View mRootView; private Animation mShowAnim; private Animation mDismissAnim; private boolean isDismissing;
然后是初始化动画变量,重写show和dismiss方法,加入播放动画的代码。注意,对父类的dismiss调用是在弹出动画结束之后才调用的,所以加入一个isDismissing表示这段过程,并添加一个私有方法dismissMe来调用父类的dismiss方法。代码如下:
private void initAnim(Context context) { mShowAnim = AnimationUtils.loadAnimation(context, R.anim.translate_up); mDismissAnim = AnimationUtils.loadAnimation(context, R.anim.translate_down); mDismissAnim.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { dismissMe(); } @Override public void onAnimationRepeat(Animation animation) { } }); }
这是初始化动画的代码,该方法在initView中调用。然后是显示和隐藏菜单的代码:
@Override public void show() { mAdapter.notifyDataSetChanged(); super.show(); mRootView.startAnimation(mShowAnim); } @Override public void dismiss() { if(isDismissing) { return; } isDismissing = true; mRootView.startAnimation(mDismissAnim); } private void dismissMe() { super.dismiss(); isDismissing = false; }
加上动画之后,更逼真了些吧。但我们还漏了一个很重要的东西 。事件!
事件
首先,在ActionSheet里面定义一个接口:interface MenuListener { void onItemSelected(int position, String item); void onCancel(); }
添加
MenuListener变量。
private MenuListener mMenuListener; public MenuListener getMenuListener() { return mMenuListener; } public void setMenuListener(MenuListener menuListener) { mMenuListener = menuListener; }
各种事件回调:
//取消按钮的事件 mCancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { cancel(); } }); // 菜单的事件 mMenuItems.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (mMenuListener != null) { mMenuListener.onItemSelected(position, mAdapter.getItem(position)); dismiss(); } } }); // 对话框取消的回调 setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { if(mMenuListener != null) { mMenuListener.onCancel(); } } });
这下就基本完成了。
运行
然后再对我们的Activity略加修改,加入事件回调。mActionSheet.setMenuListener(new ActionSheet.MenuListener() { @Override public void onItemSelected(int position, String item) { Toast.makeText(MainActivity.this, item, Toast.LENGTH_SHORT).show(); } @Override public void onCancel() { Toast.makeText(MainActivity.this, "onCancel", Toast.LENGTH_SHORT).show(); } });
运行,效果如下(由于我是通过Android Studio屏幕录制先录成MP4再在线转换为GIF的,GIF有些大,所以我就不贴图了):http://v.youku.com/v_show/id_XOTY2MTM0ODM2.html
这篇博客由于主要是写实现的过程,所以有点长。实际上代码并不复杂,ActionSheet的全部代码加注释才170行。
项目地址(包括运行效果的录制视频):http://zdz.la/2pz0Ys
下一篇将写一下如何把它写成一个可复用的控件。
参考博客:http://blog.csdn.net/bbld_/article/details/39124097
相关文章推荐
- Android开发技巧——使用Dialog实现仿QQ的ActionSheet菜单
- Android开发技巧——使用PopupWindow实现弹出菜单
- Android程序开发之使用Design包实现QQ动画侧滑效果和滑动菜单导航
- Android开发技巧——使用PopupWindow实现弹出菜单
- Android开发技巧——使用PopupWindow实现弹出菜单
- android开发(38) 使用 DrawerLayou t实现左侧抽屉式导航菜单
- Android开发实现qqminihd 左右滑动菜单效果
- Android开发之使用SharedPreferences实现QQ登陆的选项框记忆功能(源代码分享)
- 使用Mina框架开发 QQ Android 客户端(3) 登陆功能的实现
- 【Android开发日记】使用 NavigationDrawer + fragment 实现sherlockActionBar+slidingmenu 功能
- android开发(38) 使用 DrawerLayou t实现左侧抽屉式导航菜单
- android开发之ExpandableListView的使用,实现类似QQ好友列表
- android开发之ExpandableListView的使用,实现类似QQ好友列表
- android开发之ExpandableListView的使用,实现类似QQ好友列表
- android开发之ExpandableListView的使用,实现类似QQ好友列表
- Android使用DrawerLayout实现仿QQ双向侧滑菜单
- android 开发技巧(1)--使用 weight 属性实现视图的居中显示
- Android开发技巧——使用RecyclerView实现分组列表
- 使用ionic(选项卡栏tab) icon(图标) ionic上拉菜单(ActionSheet) 实现通讯录界面切换实例代码
- Android开发——使用高级的RecyclerView实现侧滑菜单删除功能(SwipeRecyclerView)