android自定义控件江河画卷,以及ListView百叶窗效果.

徐徐展开的画卷

写了个简单的自定义控件,能把View像打开画卷一样徐徐展开的ViewGroup,山河画卷.放在这个ViewGroup里面的View可以被这个ViewGroup控件徐徐打开,然后图穷匕首现

效果如下

这里写图片描述

“1”是可以更换的图片,最初想法是一个向右的箭头.

继承FrameLayout,因为FrameLayout耗资源少,而且onLayout什么的都写好了

//像山河画卷一样展开的View
public class PictureScrollView extends FrameLayout {
    public PictureScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public PictureScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public PictureScrollView(Context context) {
        super(context);
        init();
    }

先把子控件画出来,右侧不让显示的地方我们用画笔擦除调,需要申明一个擦除画笔,和一个画卷的引导卷轴,即效果图中的”1”

    //清除画笔
    private Paint mClearPaint;

    // Reel卷轴,指示当前展开到的位置
    private Bitmap reel;

    // 控制卷轴的宽高
    private float mReelWidth, mReelHeight;
    //卷轴绘制到画板的区域
    private RectF dstReel = new RectF();
    //卷轴图片源,即reel的矩形区域
    private Rect srcReel;

    private void init() {
        mClearPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mClearPaint.setColor(Color.BLUE);
        //画笔的混合模式,为清除像素
        mClearPaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));

        reel = BitmapFactory.decodeResource(getResources(), R.drawable.icon_select);
        srcReel = new Rect(0, 0, reel.getWidth(), reel.getHeight());
        Log.d("px", "srcReel-->" + srcReel.toString());
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
        mClearRect.set(mProgress, 0, mWidth, getMeasuredHeight());

        // 设置卷轴的宽高
        if (mReelWidth == 0) {
            // 按图片的实际宽高
            // mReelWidth = reel.getWidth();
            // mReelHeight = reel.getHeight();

            // 或者应该指定按控件的百分比,而不是完全按卷轴图片的宽高来
            mReelHeight = mHeight / 10;
            mReelWidth = reel.getWidth() * mReelHeight / reel.getHeight();
        }
        //设置卷轴绘画到的区域,应位于当前展开进度的正中心
        dstReel.set(mProgress - mReelWidth / 2  , mHeight / 2 - mReelHeight / 2, mReelWidth / 2 + mProgress, mHeight / 2
                + mReelHeight / 2);
        Log.d("px", "dstReel-->" + dstReel.toString());
    }

mProgress指目前画卷展开的进度,就是x轴的距离.mClearRect等声明在下面

绘制过程,super.dispatchDraw是绘制子View..在用擦除画笔时必须先保存为图层,否则将擦除一切.

    private RectF rect = new RectF(), mClearRect = new RectF();

    @Override
    protected void dispatchDraw(Canvas canvas) {
        rect.set(canvas.getClipBounds());
        // canvas.drawColor(0x00ffffff);

        int saveCount = canvas.saveLayer(rect, null, Canvas.ALL_SAVE_FLAG);//这里null在xml预览报错,要不报错传个全局new paint吧.
        super.dispatchDraw(canvas);

        canvas.drawRect(mClearRect, mClearPaint);
        canvas.restoreToCount(saveCount);

        if (showReel) {
            canvas.drawBitmap(reel, srcReel, dstReel, null);
        }
    }

余下代码

    // 启动画卷模式
    private boolean startDrawProgress = false;

    // 从0到完全展开的时间
    private long completelyOpenTime = 2000;

    //是否显示引导卷轴
    private boolean showReel = true;

    public void setProgress(float progress) {
        this.mProgress = progress;
        mClearRect.set(progress, 0, mWidth, mHeight);
        invalidate();
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent e) {
        switch (e.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            Log.d("px", "ACTION_DOWN");
            // 点到卷轴,则启动画卷模式
            if (startDrawProgress = checkEventAtReel(e)) {
                mProgress = e.getX();
                mClearRect.set(mProgress, 0, mWidth, mHeight);
                dstReel.left = mProgress - mReelWidth / 2;
                dstReel.right = mProgress + mReelWidth / 2;

                if (observer != null) {
                    observer.onScroll(mProgress);
                }
                invalidate();
            }
            // 点击到进度外看不见的地方,拒绝该事件,也不向下传递事件
            else if (e.getX() > mProgress) {
                return false;
            }
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d("px", "ACTION_MOVE");
            if (startDrawProgress) {
                mProgress = e.getX();
                mClearRect.set(mProgress, 0, mWidth, mHeight);
                dstReel.left = mProgress - mReelWidth / 2;
                dstReel.right = mProgress + mReelWidth / 2;

                if (observer != null) {
                    observer.onScroll(mProgress);
                }
                invalidate();
            }
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            if (startDrawProgress) {
                if (mProgress > mWidth / 2) {
                    // 大于一半自动全部展开
                    openFromCurrentPosition();
                } else {
                    // 小于一半自动全部收回
                    closeFromCurrentPosition();
                }
            }
            break;
        default:
            break;
        }
        super.dispatchTouchEvent(e);
        return true;
    }

    private ValueAnimator animator;
    private ValueAnimator.AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {

        @Override
        public void onAnimationUpdate(ValueAnimator animator) {
            float v = (Float) animator.getAnimatedValue();
            mClearRect.set(mProgress = v, 0, mWidth, getMeasuredHeight());
            dstReel.left = mProgress - mReelWidth / 2;
            dstReel.right = mProgress + mReelWidth / 2;
            if (observer != null) {
                observer.onScroll(mProgress);
            }
            invalidate();
        }
    };

    // 大于一半自动全部展开
    private void openFromCurrentPosition() {
        if (animator != null) {
            animator.cancel();
        }
        animator = ValueAnimator.ofFloat(mProgress, mWidth);
        animator.addUpdateListener(updateListener);
        animator.addListener(new SimpleAnimatorListener() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (observer != null) {
                    observer.onOpen();
                }
            }
        });
        animator.setDuration((long) ((mWidth - mProgress) / mWidth * completelyOpenTime));
        animator.setInterpolator(new DecelerateInterpolator());
        animator.start();
    }

    // 小于一半自动全部收回
    private void closeFromCurrentPosition() {
        if (animator != null) {
            animator.cancel();
        }
        animator = ValueAnimator.ofFloat(mProgress, 0);
        animator.addUpdateListener(updateListener);
        animator.addListener(new SimpleAnimatorListener() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (observer != null) {
                    observer.onClose();
                }
            }
        });
        animator.setDuration((long) (mProgress / mWidth * completelyOpenTime));
        animator.setInterpolator(new DecelerateInterpolator());
        animator.start();
    }

    public boolean isShowReel() {
        return showReel;
    }

    public void setShowReel(boolean showReel) {
        this.showReel = showReel;
    }

    /**
     * 检查事件是否在可点击的卷轴上
     * 
     * @param e
     * @return
     */
    private boolean checkEventAtReel(MotionEvent e) {
        // 卷轴不可见,则非
        if (!showReel) {
            return false;
        }
        if (e.getX() < dstReel.left) {
            return false;
        }
        if (e.getX() > dstReel.right) {
            return false;
        }
        if (e.getY() < dstReel.top) {
            return false;
        }
        if (e.getY() > dstReel.bottom) {
            return false;
        }
        return true;
    }

    /**
     * 观察者,监控展开与收缩的过程
     * @author user
     *
     */
    public interface Observer {
        // 完全展开
        void onOpen();

        // 完全关闭
        void onClose();

        // 滚动中
        void onScroll(float dx);
    }

    // 观察者,监听view的展开和收拢过程
    private Observer observer;

    public Observer getObserver() {
        return observer;
    }

    public void setObserver(Observer observer) {
        this.observer = observer;
    }

观察者模式用来允许使用时拓展,比如

        final PictureScrollView picture = (PictureScrollView) findViewById(R.id.pictureScrollView1);
        picture.setObserver(new Observer() {

            @Override
            public void onScroll(float dx) {
                //随着进度慢慢淡入
                pictureScrollView.setAlpha(dx/pictureScrollView.getWidth());
            }

            @Override
            public void onOpen() {
                //完全展开后不允许再卷回去了
                pictureScrollView.setShowReel(false);
            }

            @Override
            public void onClose() {

            }
        });

实现点击卷轴自动展开,从右向左展开,竖向展开等等,可以修改一些代码做到

不是所有效果都需要自定义View,有时候一个Action也可以完成

百叶窗

下面是一个百叶窗效果的实现,利用View的绕x=a轴旋转变换
这里写图片描述

大致效果图是这样,但GIF没有我的手机上表现流畅

    private ListView docScanListView;
    private boolean isBlindAniming = false;
    private Handler handler = new Handler();

    /**
     * 
     * @描述:百叶窗效果
     * 
     * @作者 [pWX273343] 2015年11月6日
     */
    private void doBlind() {
        docScanListView.setVisibility(View.VISIBLE);

        docScanListView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

            @SuppressLint("NewApi")
            @Override
            public void onGlobalLayout() {
                docScanListView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                if (isBlindAniming) {
                    return;
                } else {
                    isBlindAniming = true;
                }
                final int count = docScanListView.getChildCount();

                // 设置初始状态,-90从开始出现
                for (int i = 0; i < count; i++) {
                    final View view;

                    view = docScanListView.getChildAt(i);

                    view.setPivotX(view.getWidth() / 2f);
                    view.setPivotY(view.getHeight() / 2f);
                    view.setRotationX(90);
                }

                // 对每个item项做翻转动画
                for (int i = 0; i < count; i++) {
                    final View view;

                    view = docScanListView.getChildAt(count - i - 1);

                    final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
                    animator.setDuration(250);
                    animator.setInterpolator(new DecelerateInterpolator());
                    animator.addUpdateListener(new AnimatorUpdateListener() {

                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {
                            float v = (Float) animation.getAnimatedValue();
                            view.setScaleX(0.8f + 0.2f * v);
                            view.setScaleY(0.8f + 0.2f * v);
                            view.setRotationX(90 - 90 * v);
                        }
                    });
                    // 最后一个View动画添加结束监听
                    if (i == count - 1) {
                        animator.addListener(new SimpleAnimatorListener() {
                            public void onAnimationEnd(Animator animation) {
                                isBlindAniming = false;
                            }
                        });
                    }
                    handler.postDelayed(new Runnable() {

                        @Override
                        public void run() {
                            animator.start();
                        }
                    }, 100 * i);
                }

            }
        });
    }

    /**
     * 
     * @描述:百叶窗效果退出
     * 
     * @param needDismiss
     *            是否需要关闭弹窗
     * @作者 [pWX273343] 2015年11月11日
     */
    public void dismissBlind() {
        if (isBlindAniming) {
            return;
        } else {
            isBlindAniming = true;
        }
        if (docScanListView.getVisibility() != View.VISIBLE) {
            isBlindAniming = false;
            return;
        }
        final int count = docScanListView.getChildCount();
        for (int i = 0; i < count; i++) {
            final View view = docScanListView.getChildAt(i);

            view.setPivotX(view.getWidth() / 2f);
            view.setPivotY(view.getHeight() / 2f);
            final ValueAnimator animator = ValueAnimator.ofFloat(1, 0);
            animator.setDuration(250);
            animator.setInterpolator(new AccelerateInterpolator());
            animator.addUpdateListener(new AnimatorUpdateListener() {

                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float v = (Float) animation.getAnimatedValue();
                    view.setScaleX(0.8f + 0.2f * v);
                    view.setScaleY(0.8f + 0.2f * v);
                    view.setRotationX(90 - 90 * v);
                }
            });
            // 最后一个做完添加关闭弹窗
            if (i == count - 1)
                animator.addListener(new SimpleAnimatorListener() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        isBlindAniming = false;
                        docScanListView.setVisibility(View.GONE);

                    }
                });
            handler.postDelayed(new Runnable() {

                @Override
                public void run() {
                    animator.start();
                }
            }, 100 * i);
        }
    }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值