标签:
1.创建3个“菜单”视图,并且将“菜单”添加到父视图。
1 private Context context; 2 private RelativeLayout leftMenu; 3 private RelativeLayout middleMenu; 4 private RelativeLayout rightMenu; 5 6 public MainUI(Context context, AttributeSet attrs) { 7 super(context, attrs); 8 initView(context); 9 } 10 11 public MainUI(Context context) { 12 super(context); 13 initView(context); 14 } 15 16 private void initView(Context context) { 17 leftMenu=new RelativeLayout(context); 18 middleMenu=new RelativeLayout(context); 19 rightMenu=new RelativeLayout(context); 20 21 leftMenu.setBackgroundColor(Color.BLACK); 22 middleMenu.setBackgroundColor(Color.GREEN); 23 rightMenu.setBackgroundColor(Color.BLACK); 24 25 addView(leftMenu); 26 addView(middleMenu); 27 addView(rightMenu); 28 }
MainUI.java
2.将左右菜单的宽度设置为当前屏幕宽度的百分之八十,高度设为当前屏幕高度的值;中间菜单的宽度和高度设置为当前屏幕的宽高值。
1) 重载onMeasure()方法,并且测量菜单视图的宽高值。
3)设置为当前屏幕的百分之八十,需要用到以下方法
MeasureSpec.getSize(int measureSpec):根据提供的测量值(格式)提取大小值。
MeasureSpec.makeMeasureSpec(int size,int mode);根据提供的大小值和模式创建一个测量值(格式)。
size:这里设置为当前长度的百分之八十。
mode:以怎样的方式测量。MeasureSpec.EXACTLY精准的
1 //测量屏幕的宽度和高度 2 @Override 3 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 4 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 5 middleMenu.measure(widthMeasureSpec, heightMeasureSpec);
int realwidth=MeasureSpec.getSize(widthMeasureSpec);//取得当前屏幕整体的宽度) 8 int tempWidthMeasure=MeasureSpec.makeMeasureSpec( 9 (int)(realwidth*0.8f), MeasureSpec.EXACTLY);//左菜单和右菜单都为百分之八十 10 int temp=(int) (widthMeasureSpec*0.8f); 11 leftMenu.measure(tempWidthMeasure, heightMeasureSpec); 12 rightMenu.measure(tempWidthMeasure, heightMeasureSpec); 13 }
MainUI.java
3.将“菜单布局”填充到屏幕中。
1)重载OnLayout()方法.
2)设置布局的位置:middleMask.layout(l, t, r, b);
// 填充到位置的哪里 @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); middleMenu.layout(l, t, r, b); leftMenu.layout(l - leftMenu.getMeasuredWidth(), t, r, b); rightMenu.layout(l + middleMenu.getMeasuredWidth(),t, l + middleMenu.getMeasuredWidth() + rightMenu.getMeasuredWidth(), b); }
MainUI.java
4.当手指左右滑动的时候,移动“菜单”。
1)对事件进行处理,判断是否是左右滑动。
i. 当手指按下的时候,获取初始点。
ii. 当手指移动的时候获取x轴和y轴的值,取整数。
iii. 如果x轴移动的值大于20,且x轴移动的值大于y轴移动的值时,则判定是左右滑动。
1 private boolean isTestCompete;//判断是怎样的一个事件 2 private boolean isLeftRightEvent;//是否是左右滑动 3 4 //事件分发 5 @Override 6 public boolean dispatchTouchEvent(MotionEvent ev) { 7 if(!isTestCompete){ 8 getEventType(ev); 9 return true; 10 } 11 return super.dispatchTouchEvent(ev); 12 } 13 14 private Point point=new Point(); 15 private static final int TEST_DIS=20; 16 17 //对事件进行处理 18 private void getEventType(MotionEvent ev) { 19 switch (ev.getActionMasked()) { 20 case MotionEvent.ACTION_DOWN://当手指按下的时候,获取初始点 21 point.x=(int) ev.getX(); 22 point.y=(int) ev.getY(); 23 break; 24 case MotionEvent.ACTION_MOVE://当手指移动的时候 25 int dx=(int) Math.abs(ev.getX()-point.x);//获取x轴移动的值,取正数 26 int dy=(int) Math.abs(ev.getY()-point.y);//获取y轴移动的值,取正数 27 if(dx>=TEST_DIS&&dx>dy){ 28 //左右滑动 29 isLeftRightEvent=true; 30 isTestCompete=true; 31 point.x=(int) ev.getX(); 32 point.y=(int) ev.getY(); 33 }else if(dy>=TEST_DIS&&dy>dx){ 34 //上下滑动 35 isLeftRightEvent=false; 36 isTestCompete=true; 37 point.x=(int) ev.getX(); 38 point.y=(int) ev.getY(); 39 } 40 break; 41 case MotionEvent.ACTION_UP://当手指离开的时候 42 case MotionEvent.ACTION_CANCEL: 43 break; 44 } 45 46 }
MainUI.java
2)如果是左右滑动,则移动“菜单布局”.
i. 获取要移动的x轴距离(expectX):要移动的距离=移动的距离-滚动的距离。
ii. 如果expectX小于0,则在expectX和左菜单的长度的负数之间取最大值。
否则,在expectX和右菜单的长度的正数之间取最小值。
iii. 移动“菜单布局”到当前的位置。scrollTo(finalX,0);
iv 重新给初始点赋值。
1 @Override 2 public boolean dispatchTouchEvent(MotionEvent ev) { 3 if(!isTestCompete){ 4 getEventType(ev); 5 return true; 6 } 7 if(isLeftRightEvent){//当用户左右滑动的时候 8 switch (ev.getActionMasked()) { 9 case MotionEvent.ACTION_MOVE: 10 int curScrollX=getScrollX();//滚动的距离 11 int dis_x=(int) (ev.getX()-point.x);//手指放下到滑动的距离,也就是移动的距离 12 int expectX=-dis_x+curScrollX;//它两的差值肯定在20之间。 13 int finalX=0; 14 if(expectX<0){ 15 finalX=Math.max(expectX, -leftMenu.getMeasuredWidth()); 16 }else{ 17 finalX=Math.min(expectX, rightMenu.getMeasuredWidth()); 18 } 19 scrollTo(finalX,0);//移动到当前这个位置 20 point.x=(int) ev.getX(); 21 break; 22 case MotionEvent.ACTION_UP: 23 case MotionEvent.ACTION_CANCEL: 24 isLeftRightEvent=false; 25 isTestCompete=false; 26 break; 27 default: 28 break; 29 } 30 }else{ 31 switch (ev.getActionMasked()) { 32 case MotionEvent.ACTION_UP: 33 isLeftRightEvent=false; 34 isTestCompete=false; 35 break; 36 default: 37 break; 38 } 39 } 40 return super.dispatchTouchEvent(ev); 41 }
MainUI.java
5.在MainActivity上显示左右菜单的自定义控件。
1 public class MainActivity extends FragmentActivity { 2 3 private MainUI mainUI; 4 5 @Override 6 protected void onCreate(Bundle savedInstanceState) { 7 super.onCreate(savedInstanceState); 8 mainUI = new MainUI(this); 9 setContentView(mainUI); 10 } 11 12 }
做完上面那一步,菜单就可以左右滑动了。不过我们发现它的手指移到一半多的时候,它还是停留在那不动,不会展示出左菜单,用户体验不是很好,我们应该加入左右移动的动画。
6.当手指离开屏幕且手指移动超过左右菜单的一半时,就让它自动滑动,出现左菜单或是右菜单。
当手指离开屏幕且手指移动不超过左右菜单的一半时,就让它自动滑动回去,显示中间的菜单。
1)定义Scroller:Scroller mScoller=new Scroller(context, new DecelerateInterpolator());
private Scroller mScoller; private void initView(Context context){ ...... mScoller=new Scroller(context, new DecelerateInterpolator()); }
MainUI.java
2)手指离开屏幕的时候使用动画控制偏移过程 。
在duration秒内开始一个动画控制Scoller.startScroll(startX, startY, dx, dy, duration);
1 //事件分发 2 @Override 3 public boolean dispatchTouchEvent(MotionEvent ev) { 4 if(!isTestCompete){ 5 getEventType(ev); 6 return true; 7 } 8 if(isLeftRightEvent){//当用户左右滑动的时候 9 switch (ev.getActionMasked()) { 10 case MotionEvent.ACTION_MOVE:
int curScorllX=getScrollX(); 11 ......
break; 23 case MotionEvent.ACTION_UP: 24 case MotionEvent.ACTION_CANCEL: 25 curScrollX=getScrollX(); 26 //当移动超过左右菜单一半的时候,让它自动滑动,出现左右菜单 27 //leftMenu.getMeasuredHeight()>>1等于leftMenu.getMeasuredHeight()/2 28 if(Math.abs(curScrollX)>leftMenu.getMeasuredHeight()>>1){ 29 if(curScrollX<0){//向左滑动 30 //使用动画控制偏移过程 31 mScoller.startScroll(curScrollX, 0, 32 -leftMenu.getMeasuredWidth()-curScrollX, 0,200);//从手指那一块开始执行 33 }else{ 34 mScoller.startScroll(curScrollX, 0, 35 leftMenu.getMeasuredWidth()-curScrollX, 0,200); 36 } 37 }else{//如果当移动没有超过左右菜单一半,则让它自动滑动回去 38 mScoller.startScroll(curScrollX, 0, -curScrollX, 0,200); 39 } 40 //我们需要刷新,所以我们需要调用invalidate()方法,手动重新绘制。 41 invalidate(); 42 isLeftRightEvent=false; 43 isTestCompete=false; 44 break;
47 } 48 }else{ 49 ......
} 58 return super.dispatchTouchEvent(ev); 59 }
MainUI.java
3)重写computeScroll()方法:如果滚动尚未完成,则调用scrollTo(mScoller.getCurrX(), 0)方法平滑移动到该坐标处。
/** * 为了实现偏移 控制,一般自定义View/ViewGroup都需要重载该方法 * 该方法由父视图调用用来请求子视图根据偏移值mScrollX,mScrollY重新绘制。 */ @Override public void computeScroll() { super.computeScroll(); if(!mScoller.computeScrollOffset()){//判断滚动是否完成 return; } int tempmX=mScoller.getCurrX(); scrollTo(tempmX, 0); }
MainUI.java
7.处理点击事件:
1)定义一个LeftMenu类,继承Fragment。
1 public class LeftMenu extends Fragment { 2 3 @Override 4 public View onCreateView(LayoutInflater inflater, ViewGroup container, 5 Bundle savedInstanceState) { 6 View v=inflater.inflate(R.layout.left, container,false); 7 v.findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() { 8 9 @Override 10 public void onClick(View v) { 11 System.out.println("com.morag.mymenu"); 12 13 } 14 }); 15 return v; 16 } 17 18 }
left.xml
<?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" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" /> </LinearLayout>
2)在MainUi.java中给leftMenu添加一个id。
public static final int LEFT_ID=0xaabbcc; public static final int RIGHT_ID=0xaaccbb; public static final int MIDDLE_ID=0xbbaacc; private void initView(Context context){ ...... leftMenu.setId(LEFT_ID); rightMenu.setId(RIGHT_ID); middleMenu.setId(MIDDLE_ID); }
MainUI.java
3)让MainActivity继承FragmentAcitivy,并且添加LeftMneu碎片,提交业务处理
public class MainActivity extends FragmentActivity { private MainUI mainUI; private LeftMenu leftMenu; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mainUI = new MainUI(this); setContentView(mainUI); leftMenu = new LeftMenu(); getSupportFragmentManager().beginTransaction() .add(mainUI.LEFT_ID, leftMenu).commit(); } }
4)到这一步,发现那个左菜单的Button还是不能点击。怎么会事?
在手指按下和手指离开时,
super.dispatchTouchEvent(ev);无效的手势
private void getEventType(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN://当手指按下的时候,获取初始点 point.x=(int) ev.getX(); point.y=(int) ev.getY(); super.dispatchTouchEvent(ev);//无效的手势 break; case MotionEvent.ACTION_MOVE://当手指移动的时候 ...... case MotionEvent.ACTION_UP://当手指离开的时候 case MotionEvent.ACTION_CANCEL: super.dispatchTouchEvent(ev);//分发TouchEvent,无效的手势 isLeftRightEvent=false; isTestCompete=false; break; } }
MainUI.java
8.添加蒙版效果
1)在上面盖一层LinearLaout。
2)设置背景颜色、大小、位置。
4)让蒙版添加渐变效果
i.重载scrollTo(int x,int y)方法,它会根据手指的变化而变化。
ii.蒙版设置不透明度:不透明度=X轴滚动距离的正值/左右菜单的宽。
这个最后一步了,直接上MainUI.java的全部代码吧。
1 package com.morag.mymenu; 2 3 import android.content.Context; 4 import android.graphics.Color; 5 import android.graphics.Point; 6 import android.util.AttributeSet; 7 import android.view.MotionEvent; 8 import android.view.animation.DecelerateInterpolator; 9 import android.widget.FrameLayout; 10 import android.widget.LinearLayout; 11 import android.widget.RelativeLayout; 12 import android.widget.Scroller; 13 14 /** 15 * 实现左右菜单的自定义布局 16 * 17 */ 18 public class MainUI extends RelativeLayout{ 19 20 private Context context; 21 private FrameLayout leftMenu; 22 private FrameLayout middleMenu; 23 private FrameLayout rightMenu; 24 private LinearLayout middleMask; 25 26 private Point point=new Point(); 27 private static final int TEST_DIS=20; 28 29 30 public static final int LEFT_ID=0xaabbcc; 31 public static final int RIGHT_ID=0xaaccbb; 32 public static final int MIDDLE_ID=0xbbaacc; 33 public MainUI(Context context) { 34 super(context); 35 initView(context); 36 } 37 38 39 //注意因为是使用自定义控件,会在layout xml里 注册使用,所以必须创建该构造函数 40 public MainUI(Context context, AttributeSet attrs) { 41 super(context, attrs); 42 initView(context); 43 } 44 45 private Scroller mScoller; 46 47 private void initView(Context context){ 48 this.context=context; 49 mScoller=new Scroller(context, new DecelerateInterpolator()); 50 leftMenu=new FrameLayout(context); 51 middleMenu=new FrameLayout(context); 52 rightMenu=new FrameLayout(context); 53 middleMask=new LinearLayout(context); 54 55 leftMenu.setBackgroundColor(Color.RED); 56 middleMenu.setBackgroundColor(Color.GREEN); 57 rightMenu.setBackgroundColor(Color.RED); 58 middleMask.setBackgroundColor(0x88000000); 59 60 leftMenu.setId(LEFT_ID); 61 rightMenu.setId(RIGHT_ID); 62 middleMenu.setId(MIDDLE_ID); 63 64 //添加3个菜单 65 addView(leftMenu); 66 addView(middleMenu); 67 addView(rightMenu); 68 addView(middleMask); 69 middleMask.setAlpha(0); 70 } 71 72 public float onMiddleMask(){ 73 return middleMask.getAlpha(); 74 } 75 76 //它会根据手指滑动距离的变化而变化 77 @Override 78 public void scrollTo(int x, int y) { 79 super.scrollTo(x, y); 80 int curX=Math.abs(getScrollX()); 81 float scale=curX/(float)leftMenu.getMeasuredWidth();//变化值 82 middleMask.setAlpha(scale);//设置透明度 83 // System.out.println("透明度"+middleMask.getAlpha()); 84 } 85 86 //测量屏幕的宽度和高度 87 @Override 88 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 89 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 90 middleMenu.measure(widthMeasureSpec, heightMeasureSpec); 91 middleMask.measure(widthMeasureSpec, heightMeasureSpec); 92 int realwidth=MeasureSpec.getSize(widthMeasureSpec);//取得当前屏幕整体的宽度 93 /*MeasureSpec.makeMeasureSpec(size, mode) 94 * size:这里设置为当前长度的百分之八十。 95 * mode:以怎样的方式测量.MeasureSpec.EXACTLY精准的 96 */ 97 int tempWidthMeasure=MeasureSpec.makeMeasureSpec( 98 (int)(realwidth*0.8f), MeasureSpec.EXACTLY);//左菜单和右菜单都为百分之八十
102 leftMenu.measure(tempWidthMeasure, heightMeasureSpec); 103 rightMenu.measure(tempWidthMeasure, heightMeasureSpec); 104 } 105 106 //填充到位置的哪里 107 @Override 108 protected void onLayout(boolean changed, int l, int t, int r, int b) { 109 super.onLayout(changed, l, t, r, b); 110 middleMenu.layout(l, t, r, b); 111 middleMask.layout(l, t, r, b); 112 leftMenu.layout(l-leftMenu.getMeasuredWidth(), t, r, b); 113 rightMenu.layout(l+middleMenu.getMeasuredWidth(), t, 114 l+middleMenu.getMeasuredWidth()+rightMenu.getMeasuredWidth(), b); 115 } 116 117 private boolean isTestCompete;//判断是怎样的一个事件 118 private boolean isLeftRightEvent;//是否是左右滑动 119 120 //事件分发 121 @Override 122 public boolean dispatchTouchEvent(MotionEvent ev) { 123 if(!isTestCompete){ 124 getEventType(ev); 125 return true; 126 } 127 if(isLeftRightEvent){//当用户左右滑动的时候 128 switch (ev.getActionMasked()) { 129 case MotionEvent.ACTION_MOVE: 130 int curScrollX=getScrollX();//滚动的距离 131 int dis_x=(int) (ev.getX()-point.x);//手指放下以及滑动的距离 132 int expectX=-dis_x+curScrollX; 133 int finalX=0; 134 if(expectX<0){ 135 finalX=Math.max(expectX, -leftMenu.getMeasuredWidth()); 136 }else{ 137 finalX=Math.min(expectX, rightMenu.getMeasuredWidth()); 138 } 139 scrollTo(finalX,0);//移动到当前这个位置 140 point.x=(int) ev.getX(); 141 break; 142 case MotionEvent.ACTION_UP: 143 case MotionEvent.ACTION_CANCEL: 144 curScrollX=getScrollX(); 145 //当移动超过左右菜单一半的时候,让它自动滑动,出现左右菜单 146 //leftMenu.getMeasuredHeight()>>1等于leftMenu.getMeasuredHeight()/2 147 if(Math.abs(curScrollX)>leftMenu.getMeasuredHeight()>>1){ 148 if(curScrollX<0){//向左滑动 149 //使用动画控制偏移过程 150 mScoller.startScroll(curScrollX, 0, 151 -leftMenu.getMeasuredWidth()-curScrollX, 0,200); 152 }else{ 153 mScoller.startScroll(curScrollX, 0, 154 leftMenu.getMeasuredWidth()-curScrollX, 0,200); 155 } 156 }else{//如果当移动没有超过左右菜单一半,则让它自动滑动回去 157 mScoller.startScroll(curScrollX, 0, -curScrollX, 0,200); 158 } 159 //我们需要刷新,所以我们需要调用invalidate()方法,手动重新绘制。 160 invalidate(); 161 isLeftRightEvent=false; 162 isTestCompete=false; 163 break; 164 default: 165 break; 166 } 167 }else{ 168 switch (ev.getActionMasked()) { 169 case MotionEvent.ACTION_UP: 170 isLeftRightEvent=false; 171 isTestCompete=false; 172 break; 173 default: 174 break; 175 } 176 } 177 return super.dispatchTouchEvent(ev); 178 } 179 180 /** 181 * 为了实现偏移 控制,一般自定义View/ViewGroup都需要重载该方法 182 * 该方法由父视图调用用来请求子视图根据偏移值mScrollX,mScrollY重新绘制。 183 */ 184 @Override 185 public void computeScroll() { 186 super.computeScroll(); 187 if(!mScoller.computeScrollOffset()){//判断滚动是否完成 188 return; 189 } 190 int tempmX=mScoller.getCurrX(); 191 scrollTo(tempmX, 0); 192 } 193 194 195 //对事件进行处理 196 private void getEventType(MotionEvent ev) { 197 switch (ev.getActionMasked()) { 198 case MotionEvent.ACTION_DOWN://当手指按下的时候,获取初始点 199 point.x=(int) ev.getX(); 200 point.y=(int) ev.getY(); 201 super.dispatchTouchEvent(ev);//无效的手势 202 break; 203 case MotionEvent.ACTION_MOVE://当手指移动的时候 204 int dx=(int) Math.abs(ev.getX()-point.x);//获取x轴移动的值,取正数 205 int dy=(int) Math.abs(ev.getY()-point.y);//获取y轴移动的值,取正数 206 if(dx>=TEST_DIS&&dx>dy){ 207 //左右滑动 208 isLeftRightEvent=true; 209 isTestCompete=true; 210 point.x=(int) ev.getX(); 211 point.y=(int) ev.getY(); 212 }else if(dy>=TEST_DIS&&dy>dx){ 213 //上下滑动 214 isLeftRightEvent=false; 215 isTestCompete=true; 216 point.x=(int) ev.getX(); 217 point.y=(int) ev.getY(); 218 } 219 break; 220 case MotionEvent.ACTION_UP://当手指离开的时候 221 case MotionEvent.ACTION_CANCEL: 222 super.dispatchTouchEvent(ev);//分发TouchEvent,无效的手势 223 isLeftRightEvent=false; 224 isTestCompete=false; 225 break; 226 } 227 228 } 229 230 }
标签:
原文地址:http://www.cnblogs.com/morag/p/5487384.html