大家好,先介绍下这个控件,是来之于umano的SlidingUpPanelLayout Open Source,效果非常赞,也很流畅。
开始前,先向大家展示下控件的效果,如下:
接下来我们分析这个控件的实现,主要是SlidingUpPanelLayout.java
public class SlidingUpPanelLayout extends ViewGroup {
private static final String TAG = SlidingUpPanelLayout.class.getSimpleName();
/**
* 默认panel高度
*/
private static final int DEFAULT_PANEL_HEIGHT = 68; // dp;
/**
* 默认阴影的高度
*/
private static final int DEFAULT_SHADOW_HEIGHT = 4; // dp;
/**
* 默认蒙层颜色
*/
private static final int DEFAULT_FADE_COLOR = 0x99000000;
/**
* 默认最低快速滑动的阀值
*/
private static final int DEFAULT_MIN_FLING_VELOCITY = 400; // dips per second
/**
* 默认是否在mMainview上加一层蒙层
*/
private static final boolean DEFAULT_OVERLAY_FLAG = false;
/**
* 默认定义要解析的属性
*/
private static final int[] DEFAULT_ATTRS = new int[] {
android.R.attr.gravity
};
/**
* fling最低速度阀值
*/
private int mMinFlingVelocity = DEFAULT_MIN_FLING_VELOCITY;
/**
* 蒙层颜色
*/
private int mCoveredFadeColor = DEFAULT_FADE_COLOR;
/**
* 默认定义在滑动时,mMainView的偏移值
*/
private static final int DEFAULT_PARALAX_OFFSET = 0;
/**
* 画蒙层的paint
*/
private final Paint mCoveredFadePaint = new Paint();
/**
* 画阴影的drawable
*/
private final Drawable mShadowDrawable;
/**
* slideable view折叠时的高度 单位像素
*/
private int mPanelHeight = -1;
/**
* 阴影的高度
*/
private int mShadowHeight = -1;
/**
* 定义mMainView的最大偏移值
*/
private int mParalaxOffset = -1;
/**
* 若为true,定义slideable view向上滑动为展开
*/
private boolean mIsSlidingUp;
/**
* 若为true,表示panel可以滑动
*/
private boolean mCanSlide;
/**
* 若为false 表示会在mMainview上加上一层蒙层
*/
private boolean mOverlayContent = DEFAULT_OVERLAY_FLAG;
/**
* 可用来拖动的view
*/
private View mDragView;
/**
* 对应mDragView
*/
private int mDragViewResId = -1;
/**
* 可被滑动的view
*/
private View mSlideableView;
/**
* main view 一般是第一个索引的child view
*/
private View mMainView;
/**
* 定义可滑动slideable view的状态
*/
private enum SlideState {
EXPANDED,
COLLAPSED,
ANCHORED,//类似锚点的功能
}
//记录当前slideable view的状态
private SlideState mSlideState = SlideState.COLLAPSED;
/**
* 当前slideable view的滑动位置 是个比值 range[0,1] 0 = 展开, 1 = 收起
*/
private float mSlideOffset;
/**
* slideable view能滑动的最大距离 单位像素
*/
private int mSlideRange;
/**
* 若为true 表示不能够继续拖动
*/
private boolean mIsUnableToDrag;
/**
* 一个flag 来标示是否激活滑动功能
*/
private boolean mIsSlidingEnabled;
/**
* 若为true,此flag表示drag view想自己处理内部触摸事件,drag view可以水平滚动和处理点击事件
* 默认这个值是false
*/
private boolean mIsUsingDragViewTouchEvents;
/**
* 定义最低可滑动的距离 单位像素
*/
private final int mScrollTouchSlop;
//触摸事件down时,会记录point的x、y值
private float mInitialMotionX;
private float mInitialMotionY;
/**
* 锚点 有效值范围[0,1]
*/
private float mAnchorPoint = 0.f;
/**
* Panel滑动动作监听
*/
private PanelSlideListener mPanelSlideListener;
/**
* 辅助类 用于处理滑动的细节
*/
private final ViewDragHelper mDragHelper;
/**
* 标示是否需要重新初始化
*/
private boolean mFirstLayout = true;
/**
* 画main view和蒙层的区域大小
*/
private final Rect mTmpRect = new Rect();
/**
* Panel滑动动作监听
*/
public interface PanelSlideListener {
/**
* 正在drag时,若有有效的滑动距离,会回调此函数
* @param panel
* @param slideOffset
*/
public void onPanelSlide(View panel, float slideOffset);
/**
* Panel收起时回调
* @param panel
*/
public void onPanelCollapsed(View panel);
/**
* Panel展开时回调
* @param panel
*/
public void onPanelExpanded(View panel);
/**
* Panel滑到锚点时,会回调
* @param panel
*/
public void onPanelAnchored(View panel);
}
/**
* 如果你不想实现PanelSlideListener的全部函数,可使用此
*/
public static class SimplePanelSlideListener implements PanelSlideListener {
@Override
public void onPanelSlide(View panel, float slideOffset) {
}
@Override
public void onPanelCollapsed(View panel) {
}
@Override
public void onPanelExpanded(View panel) {
}
@Override
public void onPanelAnchored(View panel) {
}
}
//构造函数
public SlidingUpPanelLayout(Context context) {
this(context, null);
}
//构造函数
public SlidingUpPanelLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
//构造函数
public SlidingUpPanelLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
//兼容一些android提供的可视化工具做的处理
if(isInEditMode()) {
mShadowDrawable = null;
mScrollTouchSlop = 0;
mDragHelper = null;
return;
}
//解析系统属性
if (attrs != null) {
TypedArray defAttrs = context.obtainStyledAttributes(attrs, DEFAULT_ATTRS);
if (defAttrs != null) {
int gravity = defAttrs.getInt(0, Gravity.NO_GRAVITY);
if (gravity != Gravity.TOP && gravity != Gravity.BOTTOM) {
throw new IllegalArgumentException("gravity must be set to either top or bottom");
}
mIsSlidingUp = gravity == Gravity.BOTTOM;
}
defAttrs.recycle();
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlidingUpPanelLayout);
//解析自定义的属性
if (ta != null) {
mPanelHeight = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_panelHeight, -1);
mShadowHeight = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_shadowHeight, -1);
mParalaxOffset = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_paralaxOffset, -1);
mMinFlingVelocity = ta.getInt(R.styleable.SlidingUpPanelLayout_flingVelocity, DEFAULT_MIN_FLING_VELOCITY);
mCoveredFadeColor = ta.getColor(R.styleable.SlidingUpPanelLayout_fade