zoukankan      html  css  js  c++  java
  • RotateCard(自定义旋转view)

    使用方法Demo

    package com.example.displaydemo;
    
    import java.util.ArrayList;
    
    import com.example.displaydemo.RotateCard.OnItemClickListener;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.Toast;
    
    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            RotateCard card = (RotateCard) findViewById(R.id.card);
    
            ArrayList<View> views = new ArrayList<View>();
            ImageView img;
    
            img = new ImageView(MainActivity.this);
            img.setImageResource(R.drawable.p1);
            views.add(img);
    
            img = new ImageView(MainActivity.this);
            img.setImageResource(R.drawable.p2);
            views.add(img);
    
            img = new ImageView(MainActivity.this);
            img.setImageResource(R.drawable.p3);
            views.add(img);
    
            img = new ImageView(MainActivity.this);
            img.setImageResource(R.drawable.p4);
            views.add(img);
    
            img = new ImageView(MainActivity.this);
            img.setImageResource(R.drawable.p5);
            views.add(img);
    
            img = new ImageView(MainActivity.this);
            img.setImageResource(R.drawable.p6);
            views.add(img);
    
            // 传入view列表和view的宽高
            card.commitViews(views, 200, 200);
         // 添加点击事件监听 card.setOnItemClickListener(
    new OnItemClickListener() { @Override public void onItemClickListener(View view, int index) { // TODO Auto-generated method stub Toast.makeText(MainActivity.this, "The " + index + " is clicked!", Toast.LENGTH_SHORT) .show(); } }); } }

     RotateCard.java

    package com.example.displaydemo;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    
    import android.animation.Animator;
    import android.animation.AnimatorListenerAdapter;
    import android.animation.AnimatorSet;
    import android.animation.ObjectAnimator;
    import android.animation.ValueAnimator;
    import android.animation.ValueAnimator.AnimatorUpdateListener;
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.VelocityTracker;
    import android.view.View;
    import android.view.ViewTreeObserver;
    import android.view.animation.DecelerateInterpolator;
    import android.widget.FrameLayout;
    
    /**
     * RotateCard
     * 
     * @author 小明湖畔
     * 
     * @Enail 463785757@qq.com
     * 
     * @time 2015-1-17 18:02
     */
    public class RotateCard extends FrameLayout {
        private static String INFO_TAG = "RotateCard";
        private ArrayList<RotateCardViewHolder> viewHolderList;
        private ArrayList<View> viewList;
        private int ovalA, ovalB;// 椭圆轨迹的长半轴,短半轴
        private int parentWidth, parentHeight;// RotateCard的宽高
        private int viewWidth, viewHeight;// viewSet中的View的宽高
        FrameLayout.LayoutParams viewpParams;// view的LayoutParams
        private float angleOffset;// 手指每滑动一像素需要绕椭圆中心旋转的度数
    
        private RotateCardOnTouchListener mTouchListener;// 供RotateCard和子View同时使用,处理滑动事件
    
        // 增加绘制前的监听以获得RotateCard的宽高
        private ViewTreeObserver.OnPreDrawListener onPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                parentWidth = RotateCard.this.getMeasuredWidth();
                parentHeight = RotateCard.this.getMeasuredHeight();
                angleOffset = (float) ((180.0 / parentWidth) * 1.5);
    
                // Log.i(INFO_TAG, "parentWidth:" + parentWidth);
                // Log.i(INFO_TAG, "parentHeight:" + parentHeight);
                // Log.i(INFO_TAG, "angleOffset:" + angleOffset);
    
                if (viewList != null && viewList.size() > 0) {
                    afterGetParamsAndViews();
                }
    
                // 除去监听
                RotateCard.this.getViewTreeObserver().removeOnPreDrawListener(
                        onPreDrawListener);
                return true;
            }
        };
    
        public RotateCard(Context context) {
            super(context);
            // TODO Auto-generated constructor stub
            init();
        }
    
        public RotateCard(Context context, AttributeSet attrs) {
            super(context, attrs);
            // TODO Auto-generated constructor stub
            init();
        }
    
        public RotateCard(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            // TODO Auto-generated constructor stub
            init();
        }
    
        private void init() {
            // 增加RotateCard绘制之前的监听
            RotateCard.this.getViewTreeObserver().addOnPreDrawListener(
                    onPreDrawListener);
    
            // 增加RotateCard的滑动监听
            mTouchListener = new RotateCardOnTouchListener();
            this.setOnTouchListener(mTouchListener);
        }
    
        /**
         * 必须保证传入的View集合的每个View的大小一致,否则显示可能不准确
         * 
         * @param viewSet
         *            View集合
         */
        public void commitViews(ArrayList<View> viewList, int viewWidth,
                int viewHeight) {
            this.removeAllViews();
            this.viewList = viewList;
            this.viewWidth = viewWidth;
            this.viewHeight = viewHeight;
            viewpParams = new LayoutParams(viewWidth, viewHeight);
            // Log.i(INFO_TAG, "viewWidth:" + viewWidth);
            // Log.i(INFO_TAG, "viewHeight:" + viewHeight);
    
            if (parentWidth != 0 && parentHeight != 0) {
                afterGetParamsAndViews();
            }
        }
    
        /**
         * 在获得RotateCard后并且调用过commitViews函数后,此函数被调用
         */
        private void afterGetParamsAndViews() {
    
            ovalA = (parentWidth - viewWidth) / 2;
            ovalB = (int) ((parentHeight - viewHeight * 1.5) / 2);
    
            int viewNum = viewList.size();
            float anglePerView = (float) (360.0 / viewNum);
    
            // Log.i(INFO_TAG, "ovalA:" + ovalA);
            // Log.i(INFO_TAG, "ovalB:" + ovalB);
            // Log.i(INFO_TAG, "viewNum:" + viewNum);
            // Log.i(INFO_TAG, "anglePerView:" + anglePerView);
    
            if (viewHolderList == null) {
                viewHolderList = new ArrayList<RotateCardViewHolder>();
            } else {
                viewHolderList.clear();
            }
    
            float tmpAngle = 270.0f;// 第一个view在270度的位置,即最前的位置
            for (int i = 0; i < viewNum; ++i) {
                int index = i + 1;
                View view = viewList.get(i);
                // 添加子view的滑动事件监听
                view.setOnTouchListener(mTouchListener);
                // 添加子view的点击事件监听
                view.setOnClickListener(new ItemOnClickListener(index));
                // 为子view创建一个RotateCardViewHolder
                RotateCardViewHolder holder = new RotateCardViewHolder(view,
                        tmpAngle, index);
                // 将holder捆绑在子view上,方面从子view直接获取holder
                view.setTag(holder);
                viewHolderList.add(holder);
    
                // 增加anglePerView的角度,用于确定下一个view的位置
                tmpAngle += anglePerView;
            }
            // 把子view添加到RotateCard中
            loadViewBySequence();
        }
    
        /**
         * 把所有view旋转angleDiff的角度
         * 
         * @param angleDiff
         *            旋转的角度
         */
        private void rotateViewsByAngle(float angleDiff) {
            // 若有动画在进行,则不进行旋转
            if (set != null && set.isRunning()) {
                return;
            }
            // 旋转每一个view
            for (RotateCardViewHolder holder : viewHolderList) {
                holder.setAngle(holder.getAngle() + angleDiff);
            }
            // 重新载入view以确保他们正确的遮挡关系
            loadViewBySequence();
        }
    
        /**
         * 通过对vew排序后重新把view添加到RotateCard中来决定他们的遮挡关系 ,越靠前的view越慢添加
         */
        private void loadViewBySequence() {
            Collections.sort(viewHolderList, new RotateCardViewHolderComparator());
            this.removeAllViews();
            for (RotateCardViewHolder holder : viewHolderList) {
                this.addView(holder.getView(), viewpParams);
            }
        }
    
        /**
         * 封装对view的一些操作
         */
        public class RotateCardViewHolder {
            private View mView;
            private float mAngle;
            private int index;
    
            public RotateCardViewHolder(View view, float angle, int index) {
                this.mView = view;
                this.mAngle = angle;
                this.index = index;
                setAngle(angle);
            }
    
            /**
             * 设置位置和大小
             */
            public void resetPositionAndScale() {
                // 设置大小
                float angleDiff = getAngleDiffWith90();
                float scale = (float) ((1.0 * angleDiff / 180) * 0.75 + 0.25);
                mView.setScaleX(scale);
                mView.setScaleY(scale);
    
                // 设置位置
                float x = (float) (ovalA * Math.cos(mAngle / 180.0 * Math.PI)
                        + parentWidth / 2 - viewWidth / 2);
                float y = (float) (parentHeight / 2 - ovalB
                        * Math.sin(mAngle / 180.0 * Math.PI) - viewHeight / 2);
                mView.setX(x);
                mView.setY(y);
            }
    
            /**
             * 设置view的角度,同时改变他的显示的大小和位置
             * 
             * @param angle
             *            view的角度
             */
            public void setAngle(float angle) {
                // 保证mAngle在0~360之间
                mAngle = angle;
                while (mAngle < 0 || mAngle > 360) {
                    if (mAngle < 0) {
                        mAngle = (mAngle + 360) % 360;
                    } else {
                        mAngle = mAngle % 360;
                    }
                }
                resetPositionAndScale();
            }
    
            /**
             * 获取view的角度
             * 
             * @return view的角度
             */
            public float getAngle() {
                return mAngle;
            }
    
            /**
             * 获取view一开始的序号
             * 
             * @return view的序号
             */
            public int getIndex() {
                return index;
            }
    
            /**
             * 获取view的实例
             * 
             * @return view的实例
             */
            public View getView() {
                return mView;
            }
    
            /**
             * 获取该view当前位置跟90度位置相差的度数
             * 
             * @return 跟90度相差的度数
             */
            public float getAngleDiffWith90() {
                float tmpAngle = mAngle > 270 ? mAngle - 360 : mAngle;
                float angleDiff = Math.abs(tmpAngle - 90);
                return angleDiff;
            }
    
            /**
             * 获取该view当前位置跟270度位置相差的度数
             * 
             * @return 跟270度相差的度数
             */
            public float getAngleDiffWith270() {
                float tmpAngle = mAngle < 90 ? mAngle + 360 : mAngle;
                float angleDiff = Math.abs(tmpAngle - 270);
                return angleDiff;
            }
    
        }
    
        /**
         * 比较器,用来把view按照度数越接近270度越靠后的顺序排序
         * 
         */
        public class RotateCardViewHolderComparator implements
                Comparator<RotateCardViewHolder> {
    
            @Override
            public int compare(RotateCardViewHolder a, RotateCardViewHolder b) {
                // TODO Auto-generated method stub
                return a.getAngleDiffWith270() > b.getAngleDiffWith270() ? -1 : 1;
    
            }
        }
    
        private float lastX, nowX;// 上次手指的x坐标位置和当前手指的x坐标位置
        private VelocityTracker vTracker;// 监控手指滑动的使用率
        private AnimatorSet set;// 动画集
        private boolean justCancelAnimation = false;// 是否刚刚通过点击屏幕使正在进行的动画取消,用于取消动画的同事发生的点击事件
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            int action = event.getAction();
            switch (action) {
            case MotionEvent.ACTION_DOWN: {
                // 若动画正在进行,则取消动画
                if (set != null && set.isRunning()) {
                    set.cancel();
                    set = null;
                    justCancelAnimation = true;
                }
                // 获取VelocityTracker的一个实例
                vTracker = VelocityTracker.obtain();
                // 记录lastX
                lastX = event.getX();
            }
                break;
            case MotionEvent.ACTION_MOVE: {
                justCancelAnimation = false;
                // 把动作送到vTracker
                vTracker.addMovement(event);
                // 记录nowX
                nowX = event.getX();
            }
                break;
            case MotionEvent.ACTION_UP: {
                if (justCancelAnimation) {
                    justCancelAnimation = false;
                    return true;// 用来取消点击事件
                }
                // 设置计算单元,为1秒
                vTracker.computeCurrentVelocity(1000);
                // 获取在x轴方向1秒内划过的像素数
                float xSpeed = vTracker.getXVelocity();
                // 活动过快,则执行动画,让RotateCard再旋转一会才停下
                if (Math.abs(xSpeed) > 800.0f) {
                    // 还要旋转的角度
                    float angleDiff = xSpeed / 10;
                    // 还要旋转的时间
                    int duration = (int) (Math.abs(angleDiff) / 180 * 1000);
    
                    // 设置动画集
                    set = new AnimatorSet();
                    ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f)
                            .setDuration(duration);
                    animation.addUpdateListener(new AnimatorUpdateListener() {
    
                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {
                            // TODO Auto-generated method stub
                            loadViewBySequence();
                        }
    
                    });
                    set.play(animation);
                    for (RotateCardViewHolder _holder : viewHolderList) {
                        ObjectAnimator oa = ObjectAnimator.ofFloat(_holder,
                                "angle", _holder.getAngle(),
                                _holder.getAngle() + angleDiff).setDuration(
                                duration);
                        oa.setInterpolator(new DecelerateInterpolator());
                        set.play(oa).with(animation);
                    }
                    // 开始动画集合
                    set.start();
                }
            }
                break;
            }
            return super.dispatchTouchEvent(event);
        }
    
        private class RotateCardOnTouchListener implements OnTouchListener {
    
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                int action = event.getAction();
                switch (action) {
                case MotionEvent.ACTION_DOWN: {
    
                }
                    break;
                case MotionEvent.ACTION_MOVE: {
                    // 此次手指滑动需要旋转的度数
                    float angleDiff = (nowX - lastX) * angleOffset;
                    // 旋转angleDiff的角度
                    rotateViewsByAngle(angleDiff);
                    // 记录lastX
                    lastX = nowX;
                }
                    break;
                case MotionEvent.ACTION_UP: {
    
                }
                    break;
                }
    
                if (v instanceof RotateCard) {
                    // 若是手指的位置是RotateCard,返回true让事件继续传递给RotateCard
                    return true;
                } else {
                    // 若是手指的位置是子view,返回false让子view的点击事件可以响应
                    return false;
                }
            }
        }
    
        public class ItemOnClickListener implements OnClickListener {
    
            private int index;
    
            public ItemOnClickListener(int index) {
                this.index = index;
            }
    
            /**
             * * 把被点击的view旋转的最前面
             */
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                final View tView = v;
                final RotateCardViewHolder holder = (RotateCardViewHolder) v
                        .getTag();
                float angle = holder.getAngle();
                // 需要旋转的度数
                float angleDiff = angle >= 0.0f && angle <= 90.0f ? -90.0f - angle
                        : 270.0f - angle;
                // 是否子执行view的点击事件(若被点击view在很靠前,则旋转后执行它的点击事件)
                final boolean performClick = Math.abs(angleDiff) < 45.0f;
                int duration = (int) (Math.abs(angleDiff) / 180 * 1000);
    
                // 初始化动画集
                set = new AnimatorSet();
                ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f)
                        .setDuration(duration);
                animation.addUpdateListener(new AnimatorUpdateListener() {
    
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        // TODO Auto-generated method stub
                        loadViewBySequence();
                    }
    
                });
                set.play(animation);
                if (performClick) {
                    ObjectAnimator aa = ObjectAnimator.ofFloat(tView, "alpha",
                            1.0f, 0.5f, 1.0f).setDuration(800);
                    ObjectAnimator saX = ObjectAnimator.ofFloat(tView, "scaleX",
                            1.0f, 1.5f, 1.0f).setDuration(800);
                    ObjectAnimator saY = ObjectAnimator.ofFloat(tView, "scaleY",
                            1.0f, 1.5f, 1.0f).setDuration(800);
                    set.play(aa).with(animation);
                    set.play(saX).with(animation);
                    set.play(saY).with(animation);
                }
                for (RotateCardViewHolder _holder : viewHolderList) {
                    ObjectAnimator oa = ObjectAnimator.ofFloat(_holder, "angle",
                            _holder.getAngle(), _holder.getAngle() + angleDiff)
                            .setDuration(duration);
                    oa.setInterpolator(new DecelerateInterpolator());
                    set.play(oa).with(animation);
                }
                set.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        // TODO Auto-generated method stub
                        if (mOnItemClickListener != null && performClick) {
                            // 执行外部设置的点击事件
                            mOnItemClickListener.onItemClickListener(tView, index);
                        }
                    }
    
                    @Override
                    public void onAnimationCancel(Animator animation) {
                        // TODO Auto-generated method stub
                        tView.setAlpha(1.0f);
                        holder.resetPositionAndScale();
                    }
                });
                set.start();
            }
        }
    
        /**
         * 子view点击事件监听器OnItemClickListener
         * 
         * RotateCard内部需要自行处理子vew的点击事件,OnItemClickListener监听器供外部代码使用
         */
        public interface OnItemClickListener {
            public void onItemClickListener(View view, int index);
        }
    
        private OnItemClickListener mOnItemClickListener;
    
        /**
         * 添加子view点击事件监听
         * 
         * @param onItemClickListener
         */
        public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
            this.mOnItemClickListener = onItemClickListener;
        }
    }
  • 相关阅读:
    __init__ 构造行数的用法
    Android SDK下载安装及配置教程
    每条用例执行10次
    Python Json模块中dumps、loads、dump、load函数介绍
    Python接口测试实战2
    linux 下添加环境变量 和刷新
    mysql 练习题笔记
    http请求脚本排错指南
    docker命令及其常用事项
    anaconda 环境
  • 原文地址:https://www.cnblogs.com/xiaominghupan/p/4253686.html
Copyright © 2011-2022 走看看