一个可以在界面顶部展现的自定义 View

作者: 夏洛克的喵 | 来源:发表于2017-02-14 17:43 被阅读172次

    这个源码十分简单,没啥可说的.只有一点提一下,看到有些控件达到背景色的效果,是动态填加一个带背景色父布局实现的.

    我这里是在画布上动态绘制的.

    GitHub 地址: TranslationView

    源码如下:

    public class TranslationView extends FrameLayout {
    
        private static final String TAG = "TranslationView";
        private static final int DEFAULT_COLOR = 0x50000000;
        private int mShadowColor = DEFAULT_COLOR;
        private boolean mIsShow = false;
    
        private View mTranslationView;
        private ObjectAnimator mShowAni;
        private ObjectAnimator mHideAni;
    
    
        public TranslationView(Context context) {
            super(context, null);
        }
    
        public TranslationView(Context context, AttributeSet attrs) {
            super(context, attrs, 0);
        }
    
        public TranslationView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
    
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            if (getChildCount() != 2) {
                throw new IllegalStateException("only and should contain two child view");
            }
            mTranslationView = getChildAt(1);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            Log.d(TAG, "onMeasure");
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);
            mTranslationView.layout(0, -mTranslationView.getHeight(), mTranslationView.getWidth(), 0);
        }
    
    
        @Override
        protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
            if (mIsShow && child == mTranslationView) {
                canvas.drawColor(mShadowColor);
            }
            return super.drawChild(canvas, child, drawingTime);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            final int action = MotionEventCompat.getActionMasked(ev);
            switch (action) {
                case MotionEvent.ACTION_DOWN: {
                    if (mIsShow && inShadow(ev)) {
                        hide();
                        return true;
                    }
                }
            }
            return super.onInterceptTouchEvent(ev);
        }
    
        private boolean inShadow(MotionEvent ev) {
            float x = ev.getX();
            float y = ev.getY();
            final float leftEdge = mTranslationView.getX();
            final float rightEdge = leftEdge + mTranslationView.getWidth();
            final float topEdge = mTranslationView.getHeight();
            final float bottomEdge = getHeight() + topEdge;
            return x > leftEdge && x < rightEdge && y > topEdge && y < bottomEdge;
        }
    
    
        public void show() {
            if (!mIsShow) {
                mIsShow = true;
                if (mShowAni == null) {
                    mShowAni = ObjectAnimator.ofFloat(mTranslationView, "translationY", mTranslationView.getTranslationY(), mTranslationView.getHeight());
                    mShowAni.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationStart(Animator animation) {
                            super.onAnimationStart(animation);
                            invalidate();
                        }
                    });
                }
                mShowAni.start();
            }
        }
    
        public void hide() {
            if (mIsShow) {
                mIsShow = false;
                if (mHideAni == null) {
                    mHideAni = ObjectAnimator.ofFloat(mTranslationView, "translationY", mTranslationView.getTranslationY(), -mTranslationView.getHeight());
                    mHideAni.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            super.onAnimationEnd(animation);
                            invalidate();
                        }
                    });
                }
                mHideAni.start();
            }
        }
    
        public void setShadowColor(@ColorInt int color) {
            mShadowColor = color;
        }
    }
    

    勘误:之前写这个小控件的时候,代码是有些问题的,虽然有点歪打正着。当初写这个控件的时候控件的坐标计算都是按照绝对坐标去思考的,但其实 translationY相对原坐标移动了多少的一个相对概念,虽然之前也已经了解了相关概念,没想到写的时候还是犯了错误。这次因为别的需要,扩展功能的时候才发现错误。

    2017/6/1更新:新增其他方向的滑动效果。

    public class TranslationView extends FrameLayout {
        public static final String START = "start";
        public static final String END = "end";
        public static final String TOP = "top";
        public static final String BOTTOM = "bottom";
        private static final String TAG = "TranslationView";
        private static final int DEFAULT_COLOR = 0x50000000;
        private String mDirection = TOP;
        private int mShadowColor = DEFAULT_COLOR;
        private int mW;
        private int mH;
        private boolean mIsShow = false;
    
    
        private View mTranslationView;
        private ObjectAnimator mShowAni;
        private ObjectAnimator mHideAni;
    
    
        public TranslationView(Context context) {
            this(context, null);
        }
    
        public TranslationView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public TranslationView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            if (attrs == null) {
                return;
            }
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TranslationView);
            mDirection = a.getString(R.styleable.TranslationView_direction);
            if (TextUtils.isEmpty(mDirection)) {
                mDirection = TOP;
            }
            a.recycle();
        }
    
    
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            if (getChildCount() != 2) {
                throw new IllegalStateException("only and should contain two child view");
            }
            mTranslationView = getChildAt(1);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            mW = w;
            mH = h;
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);
            switch (mDirection) {
                case TOP:
                    mTranslationView.layout(0, -mTranslationView.getHeight(), mTranslationView.getWidth(), 0);
                    break;
                case BOTTOM:
                    mTranslationView.layout(0, mH, mTranslationView.getWidth(), mH + mTranslationView.getHeight());
                    break;
                case START:
                    mTranslationView.layout(-mTranslationView.getWidth(), 0, 0, mTranslationView.getHeight());
                    break;
                case END:
                    mTranslationView.layout(mW, 0, mW + mTranslationView.getWidth(), mTranslationView.getHeight());
                    break;
                default:
                    break;
            }
        }
    
    
        @Override
        protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
            if (mIsShow && child == mTranslationView) {
                canvas.drawColor(mShadowColor);
            }
            return super.drawChild(canvas, child, drawingTime);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            final int action = MotionEventCompat.getActionMasked(ev);
            switch (action) {
                case MotionEvent.ACTION_DOWN: {
                    if (mIsShow && !inTranslationView(ev)) {
                        hide();
                        return true;
                    }
                }
            }
            return super.onInterceptTouchEvent(ev);
        }
    
        /**
         * 在内容区域中
         *
         * @param ev
         * @return
         */
        private boolean inTranslationView(MotionEvent ev) {
            float x = ev.getX();
            float y = ev.getY();
            final float leftEdge = mTranslationView.getX();
            final float rightEdge = leftEdge + mTranslationView.getWidth();
            final float topEdge = mTranslationView.getY();
            final float bottomEdge = mTranslationView.getHeight() + topEdge;
            return x > leftEdge && x < rightEdge && y > topEdge && y < bottomEdge;
        }
    
    
        public void show() {
            if (!mIsShow) {
                mIsShow = true;
                if (mShowAni == null) {
                    switch (mDirection) {
                        case TOP:
                            mShowAni = ObjectAnimator.ofFloat(mTranslationView, "translationY", 0, mTranslationView.getHeight());
                            break;
                        case BOTTOM:
                            mShowAni = ObjectAnimator.ofFloat(mTranslationView, "translationY", 0, -mTranslationView.getHeight());
                            break;
                        case START:
                            mShowAni = ObjectAnimator.ofFloat(mTranslationView, "translationX", 0, mTranslationView.getWidth());
                            break;
                        case END:
                            mShowAni = ObjectAnimator.ofFloat(mTranslationView, "translationX", 0, -mTranslationView.getWidth());
                            break;
                        default:
                            break;
                    }
                    mShowAni.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationStart(Animator animation) {
                            super.onAnimationStart(animation);
                            invalidate();
                        }
                    });
                }
                mShowAni.start();
            }
        }
    
        public void hide() {
            if (mIsShow) {
                mIsShow = false;
                if (mHideAni == null) {
                    switch (mDirection) {
                        case TOP:
                        case BOTTOM:
                            mHideAni = ObjectAnimator.ofFloat(mTranslationView, "translationY", mTranslationView.getTranslationY(), 0);
                            break;
                        case START:
                        case END:
                            mHideAni = ObjectAnimator.ofFloat(mTranslationView, "translationX", mTranslationView.getTranslationX(), 0);
                            break;
                    }
                    mHideAni.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            super.onAnimationEnd(animation);
                            invalidate();
                        }
                    });
                }
                mHideAni.start();
            }
        }
    
        public void setShadowColor(@ColorInt int color) {
            mShadowColor = color;
        }
    }
    

    效果图如下:

    TranslationView.gif

    2017.7.3 更新 v1.0.1

    相关文章

      网友评论

      本文标题:一个可以在界面顶部展现的自定义 View

      本文链接:https://www.haomeiwen.com/subject/hkxnittx.html