实现思路继承自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;
}
}