自定义线性菜单 LinearMenu 仿触手tv菜单效果

来源:互联网 发布:深圳精锐纵横网络 编辑:程序博客网 时间:2024/04/26 03:14

本自定义需求来自一个朋友的需求。它的效果在触手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" >    <ImageButton        android:layout_width="40dp"        android:layout_height="40dp"        android:background="#ffffff"        android:src="@drawable/striction" />    <ImageButton        android:layout_width="40dp"        android:layout_height="40dp"        android:background="#ffffff"        android:src="@drawable/gift1"        android:tag="ib_gift" />    <ImageButton        android:layout_width="40dp"        android:layout_height="40dp"        android:background="#ffffff"        android:src="@drawable/clock2"        android:tag="ib_clock" />    <ImageButton        android:layout_width="40dp"        android:layout_height="40dp"        android:background="#ffffff"        android:src="@drawable/gift1"        android:tag="ib_gift" />    <ImageButton        android: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的效果图如下:








0 0
原创粉丝点击