Android 自定义侧滑ViewGroup
2014-03-27 23:42
267 查看
实现思路继承自ViewGroup,通过调用三个主要的ChildView的layout(int l, int t, int r, int b)方法来改变各个子View在ViewGroup中的位置。主要注意的是对触屏事件的拦截处理,不多说,上代码。Demo下载地址http://download.csdn.net/detail/ilioili/7701321
import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Color; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.Scroller; public class SlidingFrame extends ViewGroup{ private int leftWidthPercentage = 60; private int rightWidthPercentage = 60; private int animationDuration = 400; private View centreView; private float firstX,firstY; /** * 是否设置了左侧的View */ private boolean hasLeftView; /** * 是否设置了右侧的View */ private boolean hasRightView; private boolean interceptFlag; private int lastPosition; private View leftShadow; private int leftShadowWidth; private View leftView; private FrameLayout leftContiner; private FrameLayout centreContiner; private FrameLayout rightContiner; /** * 左边View的宽度 */ private int leftWidth; /** * 中间View左上角的X坐标 */ private int position = 0; private View rightShadow; private int rightShadowWidth; private View rightView; /** * 右边View的宽度 */ private int rightWidth; private Scroller scroller; final private int STATE_CENTRE_IDLE = 3; final private int STATE_CENTRE_TO_LEFT = 4; final private int STATE_CENTRE_TO_RIGHT = 6; final private int STATE_LEFT_IDLE = 1; final private int STATE_LEFT_TO_CENTRE = 5; final private int STATE_RIGHT_IDLE = 2; final private int STATE_RIGHT_TO_CENTRE = 7; private int slidingState = STATE_CENTRE_IDLE; public SlidingFrame(Context context) { super(context); init(context); } public SlidingFrame(Context context, AttributeSet attrs) { super(context, attrs); init(context); } private void addAllViews() { addView(leftContiner); addView(rightContiner); addView(leftShadow); addView(rightShadow); addView(centreContiner); } @Override public void computeScroll() { if(scroller.computeScrollOffset()){ position = scroller.getCurrX(); if(position>=leftWidth){ position = leftWidth; scroller.forceFinished(true); slidingState = STATE_RIGHT_IDLE; if(null!=listener) listener.toTheRight(); }else if(position<=-rightWidth){ scroller.forceFinished(true); position = -rightWidth; slidingState = STATE_LEFT_IDLE; if(null!=listener) listener.toTheLeft(); }else if(position==0){ scroller.forceFinished(true); slidingState = STATE_CENTRE_IDLE; blockTouchEvent = false; if(null!=listener) listener.toTheCentre(); } layoutChildren(); postInvalidate();//不调用可能看不到滚动效果,不要换成invalidate() }else{ if(position==0){ blockTouchEvent = false; slidingState = STATE_CENTRE_IDLE; } } } @SuppressLint("NewApi") private void layoutChildren(){ boolean canAlpha = android.os.Build.VERSION.SDK_INT>10; int width = getMeasuredWidth(); int height = getMeasuredHeight(); if(position<0){ leftContiner.setVisibility(View.INVISIBLE); leftShadow.setVisibility(View.INVISIBLE); rightContiner.setVisibility(View.VISIBLE); rightShadow.setVisibility(View.VISIBLE); int left = width+position/2-rightWidth/2; int top = height/4+height*position/rightWidth/4; rightContiner.layout(left, top, left+rightWidth, top+height); if(rightShadowWidth!=0){ rightShadow.layout(position+width, 0, position+rightShadowWidth+width, height); } if(canAlpha) rightContiner.setAlpha((-position*0.9f)/rightWidth+0.1f); }else if(position>0){ rightContiner.setVisibility(View.INVISIBLE); rightShadow.setVisibility(View.INVISIBLE); leftContiner.setVisibility(View.VISIBLE); leftShadow.setVisibility(View.VISIBLE); int left = -leftWidth/2+position/2; int top = height/4-height*position/leftWidth/4; leftContiner.layout(left, top, left+leftWidth, top+height); if(leftShadowWidth!=0){ leftShadow.layout(position-leftShadowWidth, 0, position, height); } if(canAlpha) leftContiner.setAlpha(position*0.9f/leftWidth+0.1f); }else{ leftContiner.setVisibility(View.INVISIBLE); leftShadow.setVisibility(View.INVISIBLE); rightContiner.setVisibility(View.INVISIBLE); rightShadow.setVisibility(View.INVISIBLE); } centreContiner.layout(position, 0, width+position, height); } private boolean blockTouchEvent; private void init(Context c){ leftShadow = new View(c); rightShadow = new View(c); leftContiner = new FrameLayout(c); rightContiner = new FrameLayout(c); centreContiner = new FrameLayout(c){ public boolean onInterceptTouchEvent(MotionEvent ev) { if(blockTouchEvent){ return true; } return super.onInterceptTouchEvent(ev); }; }; centreContiner.setBackgroundColor(Color.WHITE); // leftContiner.setId(View.generateViewId()); // rightContiner.setId(View.generateViewId()); // centreContiner.setId(View.generateViewId()); setBackgroundColor(Color.BLACK); addAllViews(); scroller = new Scroller(c); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if(slidingState==STATE_CENTRE_TO_LEFT || slidingState==STATE_LEFT_IDLE || slidingState==STATE_LEFT_TO_CENTRE){ if(ev.getX()<position+getMeasuredWidth()){ blockTouchEvent = true; } }else if(slidingState==STATE_CENTRE_TO_RIGHT || slidingState==STATE_RIGHT_TO_CENTRE || slidingState==STATE_RIGHT_IDLE){ if(ev.getX()>position){ blockTouchEvent = true; } } switch(ev.getAction()){ case MotionEvent.ACTION_DOWN: firstX = ev.getX(); firstY = ev.getY(); lastPosition = position; interceptFlag=false; break; case MotionEvent.ACTION_MOVE: if(!interceptFlag){ float dx = ev.getX()-firstX; float dy = ev.getY()-firstY; if(Math.abs(dx)+Math.abs(dy)>10 && Math.abs(dx)>Math.abs(dy)){ interceptFlag=true; } } } return interceptFlag; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { layoutChildren(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); centreContiner.measure(widthMeasureSpec, heightMeasureSpec); if(hasLeftView){ measureMyChild(leftContiner, width, height, leftWidthPercentage); leftWidth = leftContiner.getMeasuredWidth(); } if(hasRightView){ measureMyChild(rightContiner, width, height, rightWidthPercentage); rightWidth = rightContiner.getMeasuredWidth(); } setMeasuredDimension(width, height); } private void measureMyChild(FrameLayout childView, int width, int height, int percentage){ int childWidthMeasureSpec = 0; LayoutParams params = childView.getChildAt(0).getLayoutParams(); if(params.width == LayoutParams.WRAP_CONTENT){ childWidthMeasureSpec = getChildMeasureSpec(MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED), 0, LayoutParams.WRAP_CONTENT); }else if(params.width==LayoutParams.MATCH_PARENT) { childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width*percentage/100, MeasureSpec.EXACTLY); }else{ childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(params.width, MeasureSpec.EXACTLY); } int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); childView.measure(childWidthMeasureSpec, childHeightMeasureSpec); // childView.getLayoutParams().width = params.width; // childView.getLayoutParams().height = params.height; // measureChild(childView, childWidthMeasureSpec, childHeightMeasureSpec); } @Override public boolean onTouchEvent(MotionEvent ev) { // System.out.println("SlidingFrame onTouchEvent()"); switch(ev.getAction()){ case MotionEvent.ACTION_UP: smoothScroll(); return false; case MotionEvent.ACTION_DOWN://onInterceptTouchEvent 返回true跳到这里 if(!scroller.isFinished()){ scroller.forceFinished(true); } firstX = ev.getX(); return true; case MotionEvent.ACTION_MOVE: if(!scroller.isFinished()){ scroller.forceFinished(true); } float distanceX = ev.getX()-firstX; float dx = position-lastPosition-distanceX; if(dx<1&dx>-1) return false; // System.out.println("position="+position); if(slidingState==STATE_LEFT_IDLE && dx>0){ return true; }else if(slidingState==STATE_RIGHT_IDLE && dx<0){ return true; } position = (int) (lastPosition+distanceX);//dx>0 往左滑动 if(position>leftWidth){ position = leftWidth; }else if(position<-rightWidth){ position = -rightWidth; } if(position>0){ if(dx>0){ slidingState = STATE_RIGHT_TO_CENTRE; }else{ slidingState = STATE_CENTRE_TO_RIGHT; } }else if(position<0){ if(dx>0){ slidingState = STATE_CENTRE_TO_LEFT; }else{ slidingState = STATE_LEFT_TO_CENTRE; } } layoutChildren(); postInvalidate(); return true; } return false; } /** * 设置动画持续时间,单位毫秒 */ public void setAnimationDuration(int duration){ animationDuration = duration; } /** * 设置中间的View */ public void setCentreView(View v){ centreView = v; centreContiner.addView(centreView); } /** * 设置左侧阴影 * @param resid 背景资源ID * @param width 阴影宽度,单位像素 */ public void setLeftShadow(int resid, int width){ leftShadow.setBackgroundResource(resid); leftShadowWidth = width; } /** * 当left的LayoutParams的宽度为MathParent时设置此参数有效 * @param percentage 1~100,不调用此方法则根据View的LayoutParams来设置,如果没有设置LayoutParams则高度MatchParent,宽度WrapContent */ public void setLeftWidth(int percentage){ leftWidthPercentage = percentage; if(leftWidthPercentage>100 || leftWidthPercentage<0){ throw new IllegalAccessError("percentage 范围 1~100"); } } /** * 当left的LayoutParams的宽度为MathParent时设置此参数有效 * @param percentage 1~100,不调用此方法则根据View的LayoutParams来设置,如果没有设置LayoutParams则高度MatchParent,宽度WrapContent */ public void setRightWidth(int percentage){ rightWidthPercentage = percentage; if(rightWidthPercentage>100 || rightWidthPercentage<1){ throw new IllegalAccessError("percentage 范围 1~100"); } } /** * 设置左侧的View */ public void setLeftView(View v){ hasLeftView = true; leftView = v; leftContiner.addView(leftView); } /** * 设置右侧阴影 * @param resid 背景资源ID * @param width 阴影宽度,单位像素 */ public void setRightShadow(int resid, int width){ rightShadow.setBackgroundResource(resid); rightShadowWidth = width; } /** * 设置右侧的View */ public void setRightView(View v){ hasRightView = true; rightView = v; rightContiner.addView(rightView); } /** * 如果不在中间则滑动到中间 */ public void slideToCentre(){ if(position>0){ scroller.forceFinished(true); slidingState = STATE_RIGHT_TO_CENTRE; smoothScroll(); }else if(position<0){ scroller.forceFinished(true); slidingState = STATE_LEFT_TO_CENTRE; smoothScroll(); } } /** * 在中间状态时,调用此函数会滑动到左侧。 */ public void slideToLeft(){ if(hasRightView&&slidingState==STATE_CENTRE_IDLE){ scroller.forceFinished(true); slidingState = STATE_CENTRE_TO_LEFT; smoothScroll(); } } /** * 在中间状态时,调用此函数会滑动到右侧。 */ public void slideToRight(){ if(hasLeftView&&slidingState==STATE_CENTRE_IDLE){ scroller.forceFinished(true); slidingState = STATE_CENTRE_TO_RIGHT; smoothScroll(); } } private void smoothScroll(){ switch(slidingState){ case STATE_CENTRE_TO_RIGHT: scroller.startScroll(position, 0, leftWidth-position, 0, animationDuration); break; case STATE_RIGHT_TO_CENTRE: scroller.startScroll(position, 0, -position, 0, animationDuration); break; case STATE_LEFT_TO_CENTRE: scroller.startScroll(position, 0, -position, 0, animationDuration); break; case STATE_CENTRE_TO_LEFT: scroller.startScroll(position, 0, -rightWidth-position, 0, animationDuration); break; } postInvalidate();//不调用看不到滚动效果 } /** *当滑动停止时的回调,左侧停下,右侧停下,中间停下 */ public interface SlidingStateListener{ /** * 滑动到左侧停下 */ void toTheLeft(); /** * 滑动到右侧停下 */ void toTheRight(); /** * 滑动到中间停下 */ void toTheCentre(); } private SlidingStateListener listener; /** * @param listener 当滑动停止时的回调,左侧停下,右侧停下,中间停下 */ public void setSlidingStateListener(SlidingStateListener listener){ this.listener = listener; } }
相关文章推荐
- Android自定义ViewGroup(侧滑菜单)详解及简单实例
- Android自定义组件系列【3】——自定义ViewGroup实现侧滑
- android自定义ViewGroup(侧滑菜单)
- Android自定义控件6----继承ViewGroup自定义侧滑菜单
- Android自定义组件系列【3】自定义ViewGroup实现侧滑
- Android自定义View,ViewGroup(一)的一些原理与细节,RecyclerView版侧滑删除
- Android自定义组件系列【3】——自定义ViewGroup实现侧滑
- Android自定义组件系列【3】——自定义ViewGroup实现侧滑
- Android 手把手教您自定义ViewGroup(一)
- Android 自定义ViewGroup 实战篇 -> 实现FlowLayout
- androidviewgroup实现侧滑面板
- android之自定义ViewGroup和自动换行的布局的实现
- android自定义ViewGroup之瀑布流FlowLayout 简洁明了 支持padding和margin 100行代码搞定
- Android ViewDragHelper完全解析 自定义ViewGroup神器
- android自定义viewgroup实现等分格子布局
- Android自定义ViewGroup之浪漫求婚
- android自定义viewgroup实现等分格子布局
- android 自定义 view 和 ViewGroup
- Android自定义ViewGroup ,动态添加数目不确定的ImageView
- Android学习自定义View(五)——自定义ViewGroup及其onMeasure()的理解