本自定义需求来自一个朋友的需求。它的效果在触手TV上的视频播放页面的聊天界面可以看到。

本自定义视图我给它命名为线性菜单(LinearMenu)。

LinearMenu 是继承与ViewGroup的一个自定义视图,新增 orientation,position,division等三个属性,orientation表示是垂直(vertical)或水平(horizontal)来显示;position表示在左上(left_top),左下(left_bottom),右上(right_top),右下(right_bottom)等方位上显示;division表示每个菜单之间距离(分割线)。

LinearMenu视图的难点在主菜单的定位以及子菜单的布局和动画的实现。对于菜单视图的布局坐标计算我绘制了一个草图如下:

我表达的不是很有条理,但是希望三个字会给大家带来帮助。

视图的属性如下:

<?xml version="1.0" encoding="utf-8"?>
<resources><attr name="orientation"><enum name="vertical" value="0" /><enum name="horizontal" value="1" /></attr><attr name="position"><enum name="left_top" value="0" /><enum name="left_bottom" value="1" /><enum name="right_top" value="2" /><enum name="right_bottom" value="3" /></attr><declare-styleable name="LinearMenu"><attr name="division" format="dimension" /><attr name="position" /><attr name="orientation" /></declare-styleable></resources>

Demo其中的一个子布局horizontal_right_bottom代码如下:

<?xml version="1.0" encoding="utf-8"?>
<com.yehu.linearmenu.LinearMenu xmlns:android="http://schemas.android.com/apk/res/android"xmlns:yehu="http://schemas.android.com/apk/res-auto"android:id="@+id/myAnimation"android:layout_width="match_parent"android:layout_height="match_parent"yehu:division="5dp"yehu:orientation="horizontal"yehu:position="left_top" ><ImageButtonandroid:layout_width="40dp"android:layout_height="40dp"android:background="#ffffff"android:src="@drawable/striction" /><ImageButtonandroid:layout_width="40dp"android:layout_height="40dp"android:background="#ffffff"android:src="@drawable/gift1"android:tag="ib_gift" /><ImageButtonandroid:layout_width="40dp"android:layout_height="40dp"android:background="#ffffff"android:src="@drawable/clock2"android:tag="ib_clock" /><ImageButtonandroid:layout_width="40dp"android:layout_height="40dp"android:background="#ffffff"android:src="@drawable/gift1"android:tag="ib_gift" /><ImageButtonandroid:layout_width="40dp"android:layout_height="40dp"android:background="#ffffff"android:src="@drawable/clock2"android:tag="ib_clock" /></com.yehu.linearmenu.LinearMenu>

主布局activity_main.xml代码如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"xmlns:yehu="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#ffffff"android:padding="5dp" ><include layout="@layout/horizontal_right_bottom" /><include layout="@layout/vertical_left_bottom" /><include layout="@layout/horizontal_left_top" /><include layout="@layout/vertical_right_top" /></RelativeLayout>

对于上图没有看懂的可以看看本核心类来理解,代码的注释比较详细。

核心类LinearMenu代码如下:

package com.yehu.linearmenu;import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
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.TranslateAnimation;
import android.widget.ImageButton;/*** 自定义线性菜单* * @author yehu* @time 2016年1月10日上午9:46:14*/
public class LinearMenu extends ViewGroup implements OnClickListener {private int mOrientation = ORIENTATION_VERTICAL;// 默认显示模式private Position mPosition = Position.RIGHT_BOTTOM;// 默认显示位置private int mDivision;// 默认高度private Status mCurrentStatus = Status.CLOSE;// 菜单的状态private View mCButton;// 菜单的主按钮private OnMenuItemClickListener mMenuItemClickListener;// 回调private int count;// 菜单显示位置状态private static final int POS_LEFT_TOP = 0;private static final int POS_LEFT_BOTTON = 1;private static final int POS_RIGHT_TOP = 2;private static final int POS_RIGHT_BOTTON = 3;// 菜单显示模式private static final int ORIENTATION_VERTICAL = 0;private static final int ORIENTATION_HORIZONTAL = 1;// 根据菜单显示模式 来计算子布局的变量private int ml = 0;// X轴private int mt = 1;// Y轴public static enum Orientation {VERTICAL, HORIZONTAL}public LinearMenu(Context context) {this(context, null);}public LinearMenu(Context context, AttributeSet attrs) {this(context, attrs, 0);}public LinearMenu(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);// 获取自定义属性的值TypedArray a = context.getTheme().obtainStyledAttributes(attrs,R.styleable.LinearMenu, defStyle, 0);mDivision = a.getDimensionPixelSize(R.styleable.LinearMenu_division,100);mOrientation = a.getInt(R.styleable.LinearMenu_orientation,ORIENTATION_VERTICAL);int pos = a.getInt(R.styleable.LinearMenu_position, POS_RIGHT_BOTTON);switch (pos) {case POS_LEFT_TOP:mPosition = Position.LEFT_TOP;break;case POS_LEFT_BOTTON:mPosition = Position.LEFT_BOTTOM;break;case POS_RIGHT_TOP:mPosition = Position.RIGHT_TOP;break;case POS_RIGHT_BOTTON:mPosition = Position.RIGHT_BOTTOM;break;}Log.i("TAG", "Position=" + mPosition + ", Division=" + mDivision);a.recycle();}/*** 菜单状态的枚举类*/public enum Status {CLOSE, OPEN}/*** 菜单的位置枚举类*/public enum Position {LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM}/*** 点击子菜单的回调接口*/public static interface OnMenuItemClickListener {void onClick(View v, int pos);}public void setOnMenuItemClickListener(OnMenuItemClickListener mMenuItemClickListener) {this.mMenuItemClickListener = mMenuItemClickListener;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {count = getChildCount();for (int i = 0; i < count; i++) {// 测量childmeasureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {if (changed) {// 定位主菜单按钮layoutCRutton();// 判断显示模式if (mOrientation == ORIENTATION_VERTICAL) {ml = 0;mt = 1;} else if (mOrientation == ORIENTATION_HORIZONTAL) {ml = 1;mt = 0;} // 根据显示模式来布局子菜单的布局layoutMenu(mOrientation, ml, mt);}}private void layoutMenu(int mOrientation, int ml, int mt) {// 累积X , Y方向以前的距离坐标值用来确定下一个菜单的坐标int mDivisions = getChildAt(0).getMeasuredHeight();for (int i = 1; i < count; i++) {View child = getChildAt(i);child.setVisibility(View.GONE);int cl = ml * (mDivisions + (int) (mDivision * i));int ct = mt * (mDivisions + (int) (mDivision * i));int cWidth = child.getMeasuredWidth();int cHeight = child.getMeasuredHeight();mDivisions += cHeight;// 如果菜单位置在底部 左下,右下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);Log.i("TAG", "cl=" + cl + " ,ct=" + ct + " ,cr=" + cl + cWidth+ " ,cb=" + ct + cHeight);}}/*** 定位主菜单按钮*/private void layoutCRutton() {mCButton = getChildAt(0);mCButton.setOnClickListener(this);int l = 0;int t = 0;int width = mCButton.getMeasuredWidth();int height = mCButton.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;}mCButton.layout(l, t, l + width, t + height);}@Overridepublic void onClick(View v) {// 切换菜单toggleMenu(300);}/*** 切换菜单*/private void toggleMenu(int duration) {int mDivisions = getChildAt(0).getMeasuredHeight();for (int i = 1; i < count; i++) {final View childView = getChildAt(i);childView.setVisibility(View.VISIBLE);// end 0 , 0// startint cl = ml * (mDivisions + (int) (mDivision * i));int ct = mt * (mDivisions + (int) (mDivision * 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 animset = new AnimationSet(true);Animation tranAnim = null;// to openImageButton ib = (ImageButton) mCButton;if (mCurrentStatus == Status.CLOSE) {tranAnim = new TranslateAnimation(xflag * cl, 0, yflag * ct, 0);childView.setClickable(true);childView.setFocusable(true);ib.setImageResource(R.drawable.replace);} else {// to closetranAnim = new TranslateAnimation(0, xflag * cl, 0, yflag * ct);childView.setClickable(false);childView.setFocusable(false);ib.setImageResource(R.drawable.striction);}tranAnim.setFillAfter(true);tranAnim.setDuration(duration);// 使各菜单动画启动不一致tranAnim.setStartOffset(i * 20);tranAnim.setAnimationListener(new AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {}@Overridepublic void onAnimationRepeat(Animation animation) {}@Overridepublic void onAnimationEnd(Animation animation) {if (mCurrentStatus == Status.CLOSE) {childView.setVisibility(View.GONE);}}});// 旋转动画RotateAnimation rotateAnim = new RotateAnimation(0, 720,Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f);rotateAnim.setDuration(duration);rotateAnim.setFillAfter(true);animset.addAnimation(rotateAnim);animset.addAnimation(tranAnim);childView.startAnimation(animset);final int pos = i;childView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {if (mMenuItemClickListener != null)mMenuItemClickListener.onClick(childView, pos);changeStatus();}});}// 切换菜单状态changeStatus();}/*** 切换菜单状态*/private void changeStatus() {mCurrentStatus = (mCurrentStatus == Status.CLOSE ? Status.OPEN: Status.CLOSE);}}

主MainActivity代码如下:

package com.yehu.linearmenu;import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
/*** @author yehu* @time 2016年1月12日下午9:05:57*/
public class MainActivity extends Activity {private LinearMenu myAnimation;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);myAnimation = (LinearMenu) findViewById(R.id.myAnimation);myAnimation.setOnMenuItemClickListener(new LinearMenu.OnMenuItemClickListener() {@Overridepublic void onClick(View v, int pos) {String str=null;// 根据View的tag来处理事件    tag 在XML布局中设置if ("ib_gift".equals(v.getTag().toString())) {str = "ib_gift";}else if ("ib_clock".equals(v.getTag().toString())){str = "ib_clock";}Toast.makeText(MainActivity.this, "tag="+str+" ,position="+pos, 0).show();}});}
}

本Demo的效果图如下:

自定义线性菜单 LinearMenu 仿触手tv菜单效果相关推荐

  1. android view设置按钮颜色_Android 酷炫自定义 View:高仿 QQ 窗帘菜单

    作者:大公爵 链接:https://www.jianshu.com/p/cdb3d373fe37 介绍 不知道大家是否有印象,QQ 曾经有个版本用到了一种双向侧拉菜单,就像窗帘一样可以两边开合,并且伴 ...

  2. Android自定义View之仿QQ侧滑菜单实现

    最近,由于正在做的一个应用中要用到侧滑菜单,所以通过查资料看视频,学习了一下自定义View,实现一个类似于QQ的侧滑菜单,顺便还将其封装为自定义组件,可以实现类似QQ的侧滑菜单和抽屉式侧滑菜单两种菜单 ...

  3. Android仿QQ侧滑菜单

    先上效果图: GIF图有点模糊,源码已上传Github:Android仿QQ侧滑菜单 ####整体思路: 自定义ItemView的根布局(SwipeMenuLayout extends LinearL ...

  4. Android高仿QQ侧滑菜单

    文章目录 效果图 整体思路 实现过程 先分析SwipeMenuLayout 再分析下SwipeRecycleView 踩过的坑 后记 效果图 GIF图有点模糊,源码已上传Github:Android仿 ...

  5. 仿美团外卖菜单界面的实现

    仿美团外卖菜单界面的实现 布局文件 总布局 <?xml version="1.0" encoding="utf-8"?> <LinearLay ...

  6. 仿QQ侧滑菜单(二)

    在(一)https://blog.csdn.net/qq_36551426/article/details/80427352中讲了一下DrawerLayout的简单概念,但是这并不足以让我们去做一个完 ...

  7. python choice添加下拉框_自定义Django Form中choicefield下拉菜单选取数据库内容实例...

    工作中遇到的问题,自定义了一个forms.form表单,某项需要作出下拉菜单,下拉菜单中的选项需要从数据库(objectForm models)中提取. form.py为: class objectF ...

  8. Android仿ios二级菜单侧滑,仿IOS的列表项滑动菜单——ListItemMenu

    一个简单的仿IOS的列表项滑动菜单(也不知道怎么描述比较好). 顺手做出来的小东西,就分享给大家了. 仿iOS列表项滑动菜单: 1.滑动出现菜单,越界阻尼效果: 2.删除列表项效果. GitHub地址 ...

  9. python下拉菜单_自定义Django Form中choicefield下拉菜单选取数据库内容实例

    工作中遇到的问题,自定义了一个forms.form表单,某项需要作出下拉菜单,下拉菜单中的选项需要从数据库(objectForm models)中提取. form.py为: class objectF ...

最新文章

  1. 使用reveal.js制作PPT,并部署至GitHub
  2. 中国大学MOOC 编译原理 第6讲测验
  3. canvas做的图片查看器1
  4. 收藏100个网络基础知识
  5. STM32F0使用LL库实现MS5536C通讯
  6. cmd查询Oracle中的表 成表格显示,oracle如何通过cmd导出某个用户下的所有表
  7. JavaEE实战班第十五天
  8. 如何进行筛选数组(源码解析)
  9. python软件下载中文版-PyCharm中文版
  10. 计算机网络TCPP是一组什么,WWW的全称是什么?WWW中文名称是啥?
  11. HDU 6185 2017广西邀请赛:Covering(矩阵快速幂)
  12. RETINA 屏幕1px 边框实现
  13. nio中的Files类常用方法
  14. 【奇葩瑞萨-002】调教Renesas RX130独立看门狗
  15. linux系统玩ps3模拟器下载地址,【RPCS3模拟器】RPCS3模拟器下载(PS3模拟器) 电脑版-开心电玩...
  16. 计算机DCS三级体系结构组成,DCS系统原理和结构.ppt
  17. 以transformer为基础的Bert和GPT
  18. - 在c语言中是什么意思?
  19. python批量创建文件夹
  20. 被误删的手机短信息如何恢复?

热门文章

  1. 腮腺炎,淋巴肿大,翳风穴疼,脸麻
  2. LCD1602液晶第一行静止不动,第二行向右滚动程序
  3. Html5新增标签总结
  4. 不需要下载电脑软件,就能解决U盘里不显示文件的两种方法
  5. 信息安全等级保护措施之数据安全技术
  6. [小技巧] 网易邮箱收到的邮件乱码怎么办?
  7. 说反话 stringstream
  8. Oracle删除表中的重复数据
  9. 福州php前景,重磅!福州市未来三年棚改计划出炉!看看都拆哪?
  10. win10录完指纹要求验证pin,输完pin闪退