在项目中看着这个旋转效果挺炫的,就抽取出来做个记录。主要是使用CarrouselLayout 稍微修改

CarrouselLayout代码Demo下载z地址:GitHub

https://github.com/lyfkai/AndroidCarrouselLayout

主要代码如下 :

public class CarrouselLayout extends RelativeLayout {

private Context mContext;
    //自动旋转 默认不自动
    private boolean mAutoRotation;

//旋转间隔时间  默认设置为2秒
    private int mRotationTime;

//旋转木马旋转半径  圆的半径
    private float mCarrouselR;

//camera和旋转木马距离
    private float mDistance = 2f * mCarrouselR;

//旋转方向 分0顺时针和 1逆时针 俯视旋转木马看
    private int mRotateDirection;

//handler
    private CarrouselRotateHandler mHandler;

//手势处理
    private GestureDetector mGestureDetector;

//x旋转
    private int mRotationX;

//Z旋转
    private int mRotationZ;

//旋转的角度
    private float mAngle = 0;

//旋转木马子view
    private List<View> mCarrouselViews = new ArrayList<>();

//旋转木马子view的数量
    private int viewCount;

//半径扩散动画
    private ValueAnimator mAnimationR;

//记录最后的角度 用来记录上一次取消touch之后的角度
    private float mLastAngle;

//是否在触摸
    private boolean isTouching;

//旋转动画
    private ValueAnimator restAnimator;

//选中item
    private int selectItem;

//item选中回调接口
    private OnCarrouselItemSelectedListener mOnCarrouselItemSelectedListener;

//item点击回调接口
    private OnCarrouselItemClickListener mOnCarrouselItemClickListener;

//x轴旋转动画
    private ValueAnimator xAnimation;

//z轴旋转动画
    private ValueAnimator zAnimation;

private Boolean isfinish = true;//惯性动画是否结束
    private boolean isFling;

public CarrouselLayout(Context context) {
        this(context, null);
    }

public CarrouselLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

public CarrouselLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

private void init(Context context, AttributeSet attrs) {
        this.mContext = context;
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarrouselLayout);
        mAutoRotation = typedArray.getBoolean(R.styleable.CarrouselLayout_autoRotation, false);
        mRotationTime = typedArray.getInt(R.styleable.CarrouselLayout_rotationTime, 2000);
        mCarrouselR = typedArray.getDimension(R.styleable.CarrouselLayout_r, 200);
        mRotateDirection = typedArray.getInt(R.styleable.CarrouselLayout_rotateDirection, 0);
        typedArray.recycle();
        mGestureDetector = new GestureDetector(context, getGestureDetectorController());
        initHandler();
    }

/**
     * 初始化handler对象
     */
    private void initHandler() {
        mHandler = new CarrouselRotateHandler(mAutoRotation, mRotationTime, mRotateDirection) {
            @Override
            public void onRotating(CarrouselRotateDirection rotateDirection) {//接受到需要旋转指令
                try {
                    if (viewCount != 0) {//判断自动滑动从那边开始
                        int perAngle = 0;
                        switch (rotateDirection) {
                            case clockwise:
                                perAngle = 360 / viewCount;
                                break;
                            case anticlockwise:
                                perAngle = -360 / viewCount;
                                break;
                        }
                        if (mAngle == 360) {
                            mAngle = 0f;
                        }

if (isfinish)
                            startAnimRotation(mAngle + perAngle, null);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

}
        };
    }

private GestureDetector.SimpleOnGestureListener getGestureDetectorController() {

return new GestureDetector.SimpleOnGestureListener() {

@Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                isFling = true;

if (e2.getX() - e1.getX() < 0) { // 左滑
                    setAutoScrollDirection(CarrouselRotateDirection.clockwise);
                } else {
                    setAutoScrollDirection(CarrouselRotateDirection.anticlockwise);
                }
                return true;
            }

@Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                isFling = false;
                if (distanceX > 0) { // 左滑
                    setAutoScrollDirection(CarrouselRotateDirection.clockwise);
                } else {
                    setAutoScrollDirection(CarrouselRotateDirection.anticlockwise);
                }
                //转换成弧度
                double radians = Math.toRadians(mRotationZ);
                //Math.cos(radians) 返回对应的radians弧度的余弦值
                mAngle += Math.cos(radians) * (distanceX / 4) + Math.sin(radians) * (distanceY / 4);
                // Log.e("mAngle", mAngle + "");
                Log.e("mRotationZ", mRotationZ + "");
                //初始化
                refreshLayout();
                return true;
            }

};
    }

/**
     * 初始化 计算平均角度后各个子view的位置
     */
    public void refreshLayout() {
        for (int i = 0; i < mCarrouselViews.size(); i++) {
            double radians = mAngle + 180 - i * 360 / viewCount;
            float x0 = (float) Math.sin(Math.toRadians(radians)) * mCarrouselR;
            float y0 = (float) Math.cos(Math.toRadians(radians)) * mCarrouselR;
            float scale0 = (mDistance - y0) / (mDistance + mCarrouselR);
            mCarrouselViews.get(i).setScaleX(scale0 < 0.5f ? 0.5f : scale0);
            mCarrouselViews.get(i).setScaleY(scale0 < 0.5f ? 0.5f : scale0);
            if (mCarrouselViews.get(i) instanceof RelativeLayout) {
                if (((RelativeLayout) mCarrouselViews.get(i)).getChildCount() >= 2) {
                    if (((RelativeLayout) mCarrouselViews.get(i)).getChildAt(0) instanceof ImageView) {
                        int value = Float.valueOf((scale0 < 0.5f ? 0.5f : scale0) * 255).intValue();
                        ((ImageView) ((RelativeLayout) mCarrouselViews.get(i)).getChildAt(0)).setImageAlpha(value);
//                        ((ImageView)((RelativeLayout) mCarrouselViews.get(i)).getChildAt(0)).setAlpha(scale0);
                    }
//                    ((RelativeLayout) mCarrouselViews.get(i)).getChildAt(0).setAlpha(scale0);
                    if (((RelativeLayout) mCarrouselViews.get(i)).getChildAt(1) instanceof TextView) {
                        ((RelativeLayout) mCarrouselViews.get(i)).getChildAt(1).setAlpha(scale0 < 0.5f ? 0.5f : scale0);
                    }
                }
            }
            float rotationX_y = (float) Math.sin(Math.toRadians(mRotationX * Math.cos(Math.toRadians(radians)))) * mCarrouselR;
            float rotationZ_y = -(float) Math.sin(Math.toRadians(-mRotationZ)) * x0;
            float rotationZ_x = (((float) Math.cos(Math.toRadians(-mRotationZ)) * x0) - x0);
            mCarrouselViews.get(i).setTranslationX(x0 + rotationZ_x);
            mCarrouselViews.get(i).setTranslationY(rotationX_y + rotationZ_y);
        }
        List<View> arrayViewList = new ArrayList<>();
        arrayViewList.clear();
        for (int i = 0; i < mCarrouselViews.size(); i++) {
            arrayViewList.add(mCarrouselViews.get(i));
        }
        sortList(arrayViewList);
        postInvalidate();
    }

/**
     * 排序
     * 對子View 排序,然后根据变化选中是否重绘,这样是为了实现view 在显示的时候来控制当前要显示的是哪三个view,可以改变排序看下效果
     *
     * @param list
     */
    @SuppressWarnings("unchecked")
    private <T> void sortList(List<View> list) {
        @SuppressWarnings("rawtypes")
        Comparator comparator = new SortComparator();
        T[] array = list.toArray((T[]) new Object[list.size()]);
        Arrays.sort(array, comparator);
        int i = 0;
        ListIterator<T> it = (ListIterator<T>) list.listIterator();
        while (it.hasNext()) {
            it.next();
            it.set(array[i++]);
        }
        for (int j = 0; j < list.size(); j++) {
            list.get(j).bringToFront();
        }
    }

/**
     * 筛选器
     */
    private class SortComparator implements Comparator<View> {
        @Override
        public int compare(View o1, View o2) {
            return (int) (1000 * o1.getScaleX() - 1000 * o2.getScaleX());
        }
    }

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        refreshLayout();
        if (mAutoRotation) {
            mHandler.sendEmptyMessage(CarrouselRotateHandler.mMsgWhat);
        }
    }

@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (changed) {
            checkChildView();
            startAnimationR();
        }
    }

/**
     * 旋转木马半径打开动画
     */
    public void startAnimationR() {
        startAnimationR(1f, mCarrouselR);
    }

/**
     * 旋转木马半径动画
     *
     * @param isOpen 是否打开  否则关闭
     */
    public void startAnimationR(boolean isOpen) {
        if (isOpen) {
            startAnimationR(1f, mCarrouselR);
        } else {
            startAnimationR(mCarrouselR, 1f);
        }
    }

/**
     * 半径扩散、收缩动画 根据设置半径来实现
     *
     * @param from
     * @param to
     */
    public void startAnimationR(float from, float to) {
        mAnimationR = ValueAnimator.ofFloat(from, to);
        mAnimationR.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mCarrouselR = (Float) valueAnimator.getAnimatedValue();
                refreshLayout();
            }
        });
        mAnimationR.setInterpolator(new DecelerateInterpolator());
        mAnimationR.setDuration(0);
        mAnimationR.start();
    }

public void checkChildView() {
        //先清空views里边可能存在的view防止重复
        for (int i = 0; i < mCarrouselViews.size(); i++) {
            mCarrouselViews.remove(i);
        }
        final int count = getChildCount(); //获取子View的个数
        viewCount = count;
        for (int i = 0; i < count; i++) {
            final View view = getChildAt(i); //获取指定的子view
            final int position = i;
            mCarrouselViews.add(view);
            view.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mOnCarrouselItemClickListener != null) {
                        mOnCarrouselItemClickListener.onItemClick(view, position);
                    }
                }
            });

}

}

/**
     * 复位
     */
    private void restView() {
        if (viewCount == 0) {
            return;
        }
        float resultAngle = 0;
        //平均角度
        float averageAngle = 360 / viewCount;
        if (mAngle < 0) {
            averageAngle = -averageAngle;
        }
        float minvalue = (int) (mAngle / averageAngle) * averageAngle;//最小角度
        float maxvalue = (int) (mAngle / averageAngle) * averageAngle + averageAngle;//最大角度
        if (mAngle >= 0) {//分为是否小于0的情况
            if (mAngle - mLastAngle > 0) {
                resultAngle = maxvalue;
            } else {
                resultAngle = minvalue;
            }
        } else {
            if (mAngle - mLastAngle < 0) {
                resultAngle = maxvalue;
            } else {
                resultAngle = minvalue;
            }
        }
        startAnimRotation(resultAngle, null);
    }

/**
     * 动画旋转
     *
     * @param resultAngle
     * @param complete
     */
    private void startAnimRotation(float resultAngle, final Runnable complete) {
        if (mAngle == resultAngle) {
            return;
        }
        if (!isfinish) {
            restAnimator = ValueAnimator.ofFloat(mAngle, mAngle + (resultAngle - mAngle) * 5);
            //设置旋转匀速插值器
            restAnimator.setInterpolator(new DecelerateInterpolator());
            restAnimator.setDuration(1000);
        } else {
            restAnimator = ValueAnimator.ofFloat(mAngle, resultAngle);
            //设置旋转匀速插值器
            restAnimator.setInterpolator(new LinearInterpolator());
            restAnimator.setDuration(8000);
        }

restAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                if (isTouching == false) {
                    mAngle = (Float) animation.getAnimatedValue();
                    refreshLayout();
                }
            }
        });
        restAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

}

@Override
            public void onAnimationEnd(Animator animation) {
                if (isTouching == false) {
                    selectItem = calculateItem();
                    if (selectItem < 0) {
                        selectItem = viewCount + selectItem;
                    }
                    if (mOnCarrouselItemSelectedListener != null) {
                        mOnCarrouselItemSelectedListener.selected(mCarrouselViews.get(selectItem), selectItem);
                    }

if (isfinish == false) {
                        isfinish = true;
                    }
                    isFling = false;
                }
            }

@Override
            public void onAnimationCancel(Animator animation) {

}

@Override
            public void onAnimationRepeat(Animator animation) {

}
        });
        restAnimator.start();
    }

/**
     * 通过角度计算是第几个item
     *
     * @return
     */
    private int calculateItem() {
        return (int) (mAngle / (360 / viewCount)) % viewCount;
    }

/**
     * 触摸停止计时器
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return setCanAutoRotation(ev);
    }

/**
     * 触摸方法
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return true;
    }

/**
     * 触摸时停止自动加载
     *
     * @param event
     */

float DownX = 0;
    float DownY = 0;
    long currentMS = 0;

float moveX;
    float moveY;
    long moveTime;

public boolean setCanAutoRotation(MotionEvent event) {

boolean result = mGestureDetector.onTouchEvent(event);
        if (result) {
            this.getParent().requestDisallowInterceptTouchEvent(true);//通知父控件勿拦截本控件
        }
        if (event.getAction() == MotionEvent.ACTION_MOVE) {
            if (!result) {
                this.getParent().requestDisallowInterceptTouchEvent(true);//通知父控件勿拦截本控件
            }
        }

switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastAngle = mAngle;
                DownX = event.getX();//float DownX
                DownY = event.getY();//float DownY
                currentMS = System.currentTimeMillis();//long currentMS     获取系统时间

break;
            case MotionEvent.ACTION_MOVE:
                moveX = event.getX() - DownX;//X轴距离
                moveY = event.getY() - DownY;//Y轴距离
                moveTime = System.currentTimeMillis() - currentMS;//移动时间

if (Math.abs(moveX) > 50 && moveTime > 50) {
                    isTouching = true;
                    isfinish = true;

stopAutoRotation();
                }

break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (Math.abs(moveX) > 50 && moveTime > 50) {
                    isTouching = false;
                    isfinish = true;
                    if (isFling) {
                        isfinish = false;
                    } else {
                        isfinish = true;
                    }
                    restView();
                    resumeAutoRotation();
                }
                break;
        }
        return super.dispatchTouchEvent(event);
    }

/**
     * 停止自动加载
     */
    public void stopAutoRotation() {
        if (mHandler != null && mAutoRotation) {
            mHandler.removeMessages(CarrouselRotateHandler.mMsgWhat);
        }
    }

/**
     * 从新启动自动加载
     */
    public void resumeAutoRotation() {
        if (mHandler != null && mAutoRotation) {
            mHandler.sendEmptyMessageDelayed(CarrouselRotateHandler.mMsgWhat, 1000);
        }
    }

/**
     * 获取所有的view
     *
     * @return
     */
    public List<View> getViews() {
        return mCarrouselViews;
    }

/**
     * 获取角度
     *
     * @return
     */
    public float getAngle() {
        return mAngle;
    }

/**
     * 设置角度
     *
     * @param angle
     */
    public void setAngle(float angle) {
        this.mAngle = angle;
    }

/**
     * 获取距离
     *
     * @return
     */
    public float getDistance() {
        return mDistance;
    }

/**
     * 设置距离
     *
     * @param distance
     */
    public void setDistance(float distance) {
        this.mDistance = distance;
    }

/**
     * 获取半径
     *
     * @return
     */
    public float getR() {
        return mCarrouselR;
    }

/**
     * 获取选择是第几个item
     *
     * @return
     */
    public int getSelectItem() {
        return selectItem;
    }

/**
     * 设置选中方法
     *
     * @param selectItem
     */
    public void setSelectItem(int selectItem) {
        if (selectItem >= 0) {
            float angle = 0;
            if (getSelectItem() == 0) {
                if (selectItem == mCarrouselViews.size() - 1) {
                    angle = mAngle - (360 / viewCount);
                } else {
                    angle = mAngle + (360 / viewCount);
                }
            } else if (getSelectItem() == mCarrouselViews.size() - 1) {
                if (selectItem == 0) {
                    angle = mAngle + (360 / viewCount);
                } else {
                    angle = mAngle - (360 / viewCount);
                }
            } else {
                if (selectItem > getSelectItem()) {
                    angle = mAngle + (360 / viewCount);
                } else {
                    angle = mAngle - (360 / viewCount);
                }
            }

float resultAngle = 0;
            float part = 360 / viewCount;
            if (angle < 0) {
                part = -part;
            }
            //最小角度
            float minvalue = (int) (angle / part) * part;
            Log.e("minvalue", minvalue + "");
            //最大角度
            float maxvalue = (int) (angle / part) * part;

if (angle >= 0) {//分为是否小于0的情况
                if (angle - mLastAngle > 0) {
                    resultAngle = maxvalue;
                    Log.e("maxvalue", resultAngle + "");
                } else {
                    resultAngle = minvalue;
                    Log.e("maxvalue", resultAngle + "");
                }
            } else {
                if (angle - mLastAngle < 0) {
                    resultAngle = maxvalue;
                } else {
                    resultAngle = minvalue;
                }
            }

if (viewCount > 0) startAnimRotation(resultAngle, null);
        }
    }

/**
     * 设置半径
     *
     * @param r
     */
    public CarrouselLayout setR(float r) {
        this.mCarrouselR = r;
        mDistance = 2f * r;
        return this;
    }

/**
     * 选中回调接口实现
     *
     * @param mOnCarrouselItemSelectedListener
     */
    public void setOnCarrouselItemSelectedListener(OnCarrouselItemSelectedListener mOnCarrouselItemSelectedListener) {
        this.mOnCarrouselItemSelectedListener = mOnCarrouselItemSelectedListener;
    }

/**
     * 点击事件回调
     *
     * @param mOnCarrouselItemClickListener
     */
    public void setOnCarrouselItemClickListener(OnCarrouselItemClickListener mOnCarrouselItemClickListener) {
        this.mOnCarrouselItemClickListener = mOnCarrouselItemClickListener;
    }

/**
     * 设置是否自动切换
     *
     * @param autoRotation
     */
    public CarrouselLayout setAutoRotation(boolean autoRotation) {
        this.mAutoRotation = autoRotation;
        mHandler.setAutoRotation(autoRotation);
        return this;
    }

/**
     * 获取自动切换时间
     *
     * @return
     */
    public long getAutoRotationTime() {
        return mHandler.getmRotationTime();
    }

/**
     * 设置自动切换时间间隔
     *
     * @param autoRotationTime
     */
    public CarrouselLayout setAutoRotationTime(long autoRotationTime) {
        if (mHandler != null)
            mHandler.setmRotationTime(autoRotationTime);
        return this;
    }

/**
     * 是否自动切换
     *
     * @return
     */
    public boolean isAutoRotation() {
        return mAutoRotation;
    }

/**
     * 设置自动选择方向
     *
     * @param mCarrouselRotateDirection
     * @return
     */
    public CarrouselLayout setAutoScrollDirection(CarrouselRotateDirection mCarrouselRotateDirection) {
        if (mHandler != null)
            mHandler.setmRotateDirection(mCarrouselRotateDirection);
        return this;
    }

public void createXAnimation(int from, int to, boolean start) {
        if (xAnimation != null) if (xAnimation.isRunning() == true) xAnimation.cancel();
        xAnimation = ValueAnimator.ofInt(from, to);
        xAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mRotationX = (Integer) animation.getAnimatedValue();
                refreshLayout();
            }
        });
        xAnimation.setInterpolator(new LinearInterpolator());
        xAnimation.setDuration(2000);
        if (start) xAnimation.start();
    }

public ValueAnimator createZAnimation(int from, int to, boolean start) {
        if (zAnimation != null) if (zAnimation.isRunning() == true) zAnimation.cancel();
        zAnimation = ValueAnimator.ofInt(from, to);
        zAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mRotationZ = (Integer) animation.getAnimatedValue();
                refreshLayout();
            }
        });
        zAnimation.setInterpolator(new LinearInterpolator());
        zAnimation.setDuration(2000);
        if (start) zAnimation.start();
        return zAnimation;
    }

public CarrouselLayout setRotationX(int mRotationX) {
        this.mRotationX = mRotationX;
        return this;
    }

public CarrouselLayout setRotationZ(int mRotationZ) {
        this.mRotationZ = mRotationZ;
        return this;
    }

public float getRotationX() {
        return mRotationX;
    }

public int getRotationZ() {
        return mRotationZ;
    }

public ValueAnimator getRestAnimator() {
        return restAnimator;
    }

public ValueAnimator getAnimationR() {
        return mAnimationR;
    }

public void setAnimationZ(ValueAnimator zAnimation) {
        this.zAnimation = zAnimation;
    }

public ValueAnimator getAnimationZ() {
        return zAnimation;
    }

public void setAnimationX(ValueAnimator xAnimation) {
        this.xAnimation = xAnimation;
    }

public ValueAnimator getAnimationX() {
        return xAnimation;
    }

}

handler消息机制控制动态及旋转

package com.example.administrator.icome.carrousellayout;

import android.os.Handler;
import android.os.Message;

/**
 * 旋转木马自动旋转控制handler
 * Created by dalong on 2016/11/12.
 */

public abstract class CarrouselRotateHandler extends Handler {
    //消息what
    public static final int mMsgWhat = 1000;
    //是否旋转
    private boolean isAutoRotation;
    //旋转事件间隔
    private  long mRotationTime;
    //消息对象
    private Message message;
    //旋转方向
    private CarrouselRotateDirection  mRotateDirection;

public CarrouselRotateHandler(boolean isAutoRotation, int mRotationTime , int mRotateDirection) {
        this.isAutoRotation = isAutoRotation;
        this.mRotationTime = mRotationTime;
        this.mRotateDirection=mRotateDirection==0?CarrouselRotateDirection.clockwise:CarrouselRotateDirection.anticlockwise;
        message=createMessage();
        setAutoRotation(isAutoRotation);
    }

/**
     * 消息处理
     * @param msg
     */
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what){
            case mMsgWhat:
                //如果自动旋转
                if(isAutoRotation){
                    //旋转通知
                    onRotating(mRotateDirection);
                    //再次发送消息  循环
                    sendMessage();
                }
                break;
        }
    }

/**
     * 需要旋转通知方法
     */
    public abstract void onRotating(CarrouselRotateDirection mRotateDirection);

/**
     * 创建消息对象
     * @return
     */
    private Message createMessage(){
        Message message=new Message();
        message.what=mMsgWhat;
        return  message;
    }

/**
     * 发送消息
     */
    public void sendMessage(){
        //清除所有mMsgWhat的消息
        try {
            removeMessages(mMsgWhat);
        } catch (Exception e) {
        }
        message=createMessage();
        this.sendMessageDelayed(message,mRotationTime);

}

/**
     * 获取是否自动旋转
     * @return
     */
    public boolean isAutoRotation() {
        return isAutoRotation;
    }

/**
     * 设置是否自动旋转
     * @param autoRotation
     */
    public void setAutoRotation(boolean autoRotation) {
        isAutoRotation = autoRotation;
        if(autoRotation){//如果需要旋转
            sendMessage();
        }else{//不需要旋转  需要清除所有消息队列中的消息
            removeMessages(mMsgWhat);
        }
    }

/**
     * 获取旋转事件间隔
     * @return
     */
    public long getmRotationTime() {
        return mRotationTime;
    }

/**
     * 设置旋转事件间隔
     * @param mRotationTime
     */
    public void setmRotationTime(long mRotationTime) {
        this.mRotationTime = mRotationTime;
    }

/**
     * 获取旋转方向
     * @return
     */
    public CarrouselRotateDirection getmRotateDirection() {
        return mRotateDirection;
    }

/**
     * 设置旋转方向
     * @param mRotateDirection
     */
    public void setmRotateDirection(CarrouselRotateDirection mRotateDirection) {
        this.mRotateDirection = mRotateDirection;
    }

}

public class CarrouselLayout extends RelativeLayout {

    private Context mContext;
//自动旋转 默认不自动
private boolean mAutoRotation; //旋转间隔时间 默认设置为2秒
private int mRotationTime; //旋转木马旋转半径 圆的半径
private float mCarrouselR; //camera和旋转木马距离
private float mDistance = 2f * mCarrouselR; //旋转方向 分0顺时针和 1逆时针 俯视旋转木马看
private int mRotateDirection; //handler
private CarrouselRotateHandler mHandler; //手势处理
private GestureDetector mGestureDetector; //x旋转
private int mRotationX; //Z旋转
private int mRotationZ; //旋转的角度
private float mAngle = ; //旋转木马子view
private List<View> mCarrouselViews = new ArrayList<>(); //旋转木马子view的数量
private int viewCount; //半径扩散动画
private ValueAnimator mAnimationR; //记录最后的角度 用来记录上一次取消touch之后的角度
private float mLastAngle; //是否在触摸
private boolean isTouching; //旋转动画
private ValueAnimator restAnimator; //选中item
private int selectItem; //item选中回调接口
private OnCarrouselItemSelectedListener mOnCarrouselItemSelectedListener; //item点击回调接口
private OnCarrouselItemClickListener mOnCarrouselItemClickListener; //x轴旋转动画
private ValueAnimator xAnimation; //z轴旋转动画
private ValueAnimator zAnimation; private Boolean isfinish = true;//惯性动画是否结束
private boolean isFling; public CarrouselLayout(Context context) {
this(context, null);
} public CarrouselLayout(Context context, AttributeSet attrs) {
this(context, attrs, );
} public CarrouselLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
} private void init(Context context, AttributeSet attrs) {
this.mContext = context;
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarrouselLayout);
mAutoRotation = typedArray.getBoolean(R.styleable.CarrouselLayout_autoRotation, false);
mRotationTime = typedArray.getInt(R.styleable.CarrouselLayout_rotationTime, );
mCarrouselR = typedArray.getDimension(R.styleable.CarrouselLayout_r, );
mRotateDirection = typedArray.getInt(R.styleable.CarrouselLayout_rotateDirection, );
typedArray.recycle();
mGestureDetector = new GestureDetector(context, getGestureDetectorController());
initHandler();
} /**
* 初始化handler对象
*/
private void initHandler() {
mHandler = new CarrouselRotateHandler(mAutoRotation, mRotationTime, mRotateDirection) {
@Override
public void onRotating(CarrouselRotateDirection rotateDirection) {//接受到需要旋转指令
try {
if (viewCount != ) {//判断自动滑动从那边开始
int perAngle = ;
switch (rotateDirection) {
case clockwise:
perAngle = 360 / viewCount;
break;
case anticlockwise:
perAngle = -360 / viewCount;
break;
}
if (mAngle == ) {
mAngle = 0f;
} if (isfinish)
startAnimRotation(mAngle + perAngle, null);
}
} catch (Exception e) {
e.printStackTrace();
} }
};
} private GestureDetector.SimpleOnGestureListener getGestureDetectorController() { return new GestureDetector.SimpleOnGestureListener() { @Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
isFling = true; if (e2.getX() - e1.getX() < ) { // 左滑
setAutoScrollDirection(CarrouselRotateDirection.clockwise);
} else {
setAutoScrollDirection(CarrouselRotateDirection.anticlockwise);
}
return true;
} @Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
isFling = false;
if (distanceX > ) { // 左滑
setAutoScrollDirection(CarrouselRotateDirection.clockwise);
} else {
setAutoScrollDirection(CarrouselRotateDirection.anticlockwise);
}
//转换成弧度
double radians = Math.toRadians(mRotationZ);
//Math.cos(radians) 返回对应的radians弧度的余弦值
mAngle += Math.cos(radians) * (distanceX / ) + Math.sin(radians) * (distanceY / );
// Log.e("mAngle", mAngle + "");
Log.e("mRotationZ", mRotationZ + "");
//初始化
refreshLayout();
return true;
} };
} /**
* 初始化 计算平均角度后各个子view的位置
*/
public void refreshLayout() {
for (int i = ; i < mCarrouselViews.size(); i++) {
double radians = mAngle + 180 - i * 360 / viewCount;
float x0 = (float) Math.sin(Math.toRadians(radians)) * mCarrouselR;
float y0 = (float) Math.cos(Math.toRadians(radians)) * mCarrouselR;
float scale0 = (mDistance - y0) / (mDistance + mCarrouselR);
mCarrouselViews.get(i).setScaleX(scale0 < 0.5f ? 0.5f : scale0);
mCarrouselViews.get(i).setScaleY(scale0 < 0.5f ? 0.5f : scale0);
if (mCarrouselViews.get(i) instanceof RelativeLayout) {
if (((RelativeLayout) mCarrouselViews.get(i)).getChildCount() >= ) {
if (((RelativeLayout) mCarrouselViews.get(i)).getChildAt() instanceof ImageView) {
int value = Float.valueOf((scale0 < 0.5f ? 0.5f : scale0) * ).intValue();
((ImageView) ((RelativeLayout) mCarrouselViews.get(i)).getChildAt()).setImageAlpha(value);
// ((ImageView)((RelativeLayout) mCarrouselViews.get(i)).getChildAt(0)).setAlpha(scale0);
}
// ((RelativeLayout) mCarrouselViews.get(i)).getChildAt(0).setAlpha(scale0);
if (((RelativeLayout) mCarrouselViews.get(i)).getChildAt() instanceof TextView) {
((RelativeLayout) mCarrouselViews.get(i)).getChildAt().setAlpha(scale0 < 0.5f ? 0.5f : scale0);
}
}
}
float rotationX_y = (float) Math.sin(Math.toRadians(mRotationX * Math.cos(Math.toRadians(radians)))) * mCarrouselR;
float rotationZ_y = -(float) Math.sin(Math.toRadians(-mRotationZ)) * x0;
float rotationZ_x = (((float) Math.cos(Math.toRadians(-mRotationZ)) * x0) - x0);
mCarrouselViews.get(i).setTranslationX(x0 + rotationZ_x);
mCarrouselViews.get(i).setTranslationY(rotationX_y + rotationZ_y);
}
List<View> arrayViewList = new ArrayList<>();
arrayViewList.clear();
for (int i = ; i < mCarrouselViews.size(); i++) {
arrayViewList.add(mCarrouselViews.get(i));
}
sortList(arrayViewList);
postInvalidate();
} /**
* 排序
* 對子View 排序,然后根据变化选中是否重绘,这样是为了实现view 在显示的时候来控制当前要显示的是哪三个view,可以改变排序看下效果
*
* @param list
*/
@SuppressWarnings("unchecked")
private <T> void sortList(List<View> list) {
@SuppressWarnings("rawtypes")
Comparator comparator = new SortComparator();
T[] array = list.toArray((T[]) new Object[list.size()]);
Arrays.sort(array, comparator);
int i = ;
ListIterator<T> it = (ListIterator<T>) list.listIterator();
while (it.hasNext()) {
it.next();
it.set(array[i++]);
}
for (int j = ; j < list.size(); j++) {
list.get(j).bringToFront();
}
} /**
* 筛选器
*/
private class SortComparator implements Comparator<View> {
@Override
public int compare(View o1, View o2) {
return (int) (1000 * o1.getScaleX() - 1000 * o2.getScaleX());
}
} @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
refreshLayout();
if (mAutoRotation) {
mHandler.sendEmptyMessage(CarrouselRotateHandler.mMsgWhat);
}
} @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
checkChildView();
startAnimationR();
}
} /**
* 旋转木马半径打开动画
*/
public void startAnimationR() {
startAnimationR(1f, mCarrouselR);
} /**
* 旋转木马半径动画
*
* @param isOpen 是否打开 否则关闭
*/
public void startAnimationR(boolean isOpen) {
if (isOpen) {
startAnimationR(1f, mCarrouselR);
} else {
startAnimationR(mCarrouselR, 1f);
}
} /**
* 半径扩散、收缩动画 根据设置半径来实现
*
* @param from
* @param to
*/
public void startAnimationR(float from, float to) {
mAnimationR = ValueAnimator.ofFloat(from, to);
mAnimationR.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mCarrouselR = (Float) valueAnimator.getAnimatedValue();
refreshLayout();
}
});
mAnimationR.setInterpolator(new DecelerateInterpolator());
mAnimationR.setDuration();
mAnimationR.start();
} public void checkChildView() {
//先清空views里边可能存在的view防止重复
for (int i = ; i < mCarrouselViews.size(); i++) {
mCarrouselViews.remove(i);
}
final int count = getChildCount(); //获取子View的个数
viewCount = count;
for (int i = ; i < count; i++) {
final View view = getChildAt(i); //获取指定的子view
final int position = i;
mCarrouselViews.add(view);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mOnCarrouselItemClickListener != null) {
mOnCarrouselItemClickListener.onItemClick(view, position);
}
}
}); } } /**
* 复位
*/
private void restView() {
if (viewCount == ) {
return;
}
float resultAngle = ;
//平均角度
float averageAngle = 360 / viewCount;
if (mAngle < ) {
averageAngle = -averageAngle;
}
float minvalue = (int) (mAngle / averageAngle) * averageAngle;//最小角度
float maxvalue = (int) (mAngle / averageAngle) * averageAngle + averageAngle;//最大角度
if (mAngle >= ) {//分为是否小于0的情况
if (mAngle - mLastAngle > ) {
resultAngle = maxvalue;
} else {
resultAngle = minvalue;
}
} else {
if (mAngle - mLastAngle < ) {
resultAngle = maxvalue;
} else {
resultAngle = minvalue;
}
}
startAnimRotation(resultAngle, null);
} /**
* 动画旋转
*
* @param resultAngle
* @param complete
*/
private void startAnimRotation(float resultAngle, final Runnable complete) {
if (mAngle == resultAngle) {
return;
}
if (!isfinish) {
restAnimator = ValueAnimator.ofFloat(mAngle, mAngle + (resultAngle - mAngle) * );
//设置旋转匀速插值器
restAnimator.setInterpolator(new DecelerateInterpolator());
restAnimator.setDuration();
} else {
restAnimator = ValueAnimator.ofFloat(mAngle, resultAngle);
//设置旋转匀速插值器
restAnimator.setInterpolator(new LinearInterpolator());
restAnimator.setDuration();
} restAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (isTouching == false) {
mAngle = (Float) animation.getAnimatedValue();
refreshLayout();
}
}
});
restAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) { } @Override
public void onAnimationEnd(Animator animation) {
if (isTouching == false) {
selectItem = calculateItem();
if (selectItem < ) {
selectItem = viewCount + selectItem;
}
if (mOnCarrouselItemSelectedListener != null) {
mOnCarrouselItemSelectedListener.selected(mCarrouselViews.get(selectItem), selectItem);
} if (isfinish == false) {
isfinish = true;
}
isFling = false;
}
} @Override
public void onAnimationCancel(Animator animation) { } @Override
public void onAnimationRepeat(Animator animation) { }
});
restAnimator.start();
} /**
* 通过角度计算是第几个item
*
* @return
*/
private int calculateItem() {
return (int) (mAngle / (360 / viewCount)) % viewCount;
} /**
* 触摸停止计时器
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return setCanAutoRotation(ev);
} /**
* 触摸方法
*
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
return true;
} /**
* 触摸时停止自动加载
*
* @param event
*/ float DownX = ;
float DownY = ;
long currentMS = ; float moveX;
float moveY;
long moveTime; public boolean setCanAutoRotation(MotionEvent event) { boolean result = mGestureDetector.onTouchEvent(event);
if (result) {
this.getParent().requestDisallowInterceptTouchEvent(true);//通知父控件勿拦截本控件
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (!result) {
this.getParent().requestDisallowInterceptTouchEvent(true);//通知父控件勿拦截本控件
}
} switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastAngle = mAngle;
DownX = event.getX();//float DownX
DownY = event.getY();//float DownY
currentMS = System.currentTimeMillis();//long currentMS 获取系统时间 break;
case MotionEvent.ACTION_MOVE:
moveX = event.getX() - DownX;//X轴距离
moveY = event.getY() - DownY;//Y轴距离
moveTime = System.currentTimeMillis() - currentMS;//移动时间 if (Math.abs(moveX) > 50 && moveTime > ) {
isTouching = true;
isfinish = true; stopAutoRotation();
} break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (Math.abs(moveX) > 50 && moveTime > ) {
isTouching = false;
isfinish = true;
if (isFling) {
isfinish = false;
} else {
isfinish = true;
}
restView();
resumeAutoRotation();
}
break;
}
return super.dispatchTouchEvent(event);
} /**
* 停止自动加载
*/
public void stopAutoRotation() {
if (mHandler != null && mAutoRotation) {
mHandler.removeMessages(CarrouselRotateHandler.mMsgWhat);
}
} /**
* 从新启动自动加载
*/
public void resumeAutoRotation() {
if (mHandler != null && mAutoRotation) {
mHandler.sendEmptyMessageDelayed(CarrouselRotateHandler.mMsgWhat, );
}
} /**
* 获取所有的view
*
* @return
*/
public List<View> getViews() {
return mCarrouselViews;
} /**
* 获取角度
*
* @return
*/
public float getAngle() {
return mAngle;
} /**
* 设置角度
*
* @param angle
*/
public void setAngle(float angle) {
this.mAngle = angle;
} /**
* 获取距离
*
* @return
*/
public float getDistance() {
return mDistance;
} /**
* 设置距离
*
* @param distance
*/
public void setDistance(float distance) {
this.mDistance = distance;
} /**
* 获取半径
*
* @return
*/
public float getR() {
return mCarrouselR;
} /**
* 获取选择是第几个item
*
* @return
*/
public int getSelectItem() {
return selectItem;
} /**
* 设置选中方法
*
* @param selectItem
*/
public void setSelectItem(int selectItem) {
if (selectItem >= ) {
float angle = ;
if (getSelectItem() == ) {
if (selectItem == mCarrouselViews.size() - ) {
angle = mAngle - (360 / viewCount);
} else {
angle = mAngle + (360 / viewCount);
}
} else if (getSelectItem() == mCarrouselViews.size() - ) {
if (selectItem == ) {
angle = mAngle + (360 / viewCount);
} else {
angle = mAngle - (360 / viewCount);
}
} else {
if (selectItem > getSelectItem()) {
angle = mAngle + (360 / viewCount);
} else {
angle = mAngle - (360 / viewCount);
}
} float resultAngle = ;
float part = 360 / viewCount;
if (angle < ) {
part = -part;
}
//最小角度
float minvalue = (int) (angle / part) * part;
Log.e("minvalue", minvalue + "");
//最大角度
float maxvalue = (int) (angle / part) * part; if (angle >= ) {//分为是否小于0的情况
if (angle - mLastAngle > ) {
resultAngle = maxvalue;
Log.e("maxvalue", resultAngle + "");
} else {
resultAngle = minvalue;
Log.e("maxvalue", resultAngle + "");
}
} else {
if (angle - mLastAngle < ) {
resultAngle = maxvalue;
} else {
resultAngle = minvalue;
}
} if (viewCount > ) startAnimRotation(resultAngle, null);
}
} /**
* 设置半径
*
* @param r
*/
public CarrouselLayout setR(float r) {
this.mCarrouselR = r;
mDistance = 2f * r;
return this;
} /**
* 选中回调接口实现
*
* @param mOnCarrouselItemSelectedListener
*/
public void setOnCarrouselItemSelectedListener(OnCarrouselItemSelectedListener mOnCarrouselItemSelectedListener) {
this.mOnCarrouselItemSelectedListener = mOnCarrouselItemSelectedListener;
} /**
* 点击事件回调
*
* @param mOnCarrouselItemClickListener
*/
public void setOnCarrouselItemClickListener(OnCarrouselItemClickListener mOnCarrouselItemClickListener) {
this.mOnCarrouselItemClickListener = mOnCarrouselItemClickListener;
} /**
* 设置是否自动切换
*
* @param autoRotation
*/
public CarrouselLayout setAutoRotation(boolean autoRotation) {
this.mAutoRotation = autoRotation;
mHandler.setAutoRotation(autoRotation);
return this;
} /**
* 获取自动切换时间
*
* @return
*/
public long getAutoRotationTime() {
return mHandler.getmRotationTime();
} /**
* 设置自动切换时间间隔
*
* @param autoRotationTime
*/
public CarrouselLayout setAutoRotationTime(long autoRotationTime) {
if (mHandler != null)
mHandler.setmRotationTime(autoRotationTime);
return this;
} /**
* 是否自动切换
*
* @return
*/
public boolean isAutoRotation() {
return mAutoRotation;
} /**
* 设置自动选择方向
*
* @param mCarrouselRotateDirection
* @return
*/
public CarrouselLayout setAutoScrollDirection(CarrouselRotateDirection mCarrouselRotateDirection) {
if (mHandler != null)
mHandler.setmRotateDirection(mCarrouselRotateDirection);
return this;
} public void createXAnimation(int from, int to, boolean start) {
if (xAnimation != null) if (xAnimation.isRunning() == true) xAnimation.cancel();
xAnimation = ValueAnimator.ofInt(from, to);
xAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mRotationX = (Integer) animation.getAnimatedValue();
refreshLayout();
}
});
xAnimation.setInterpolator(new LinearInterpolator());
xAnimation.setDuration();
if (start) xAnimation.start();
} public ValueAnimator createZAnimation(int from, int to, boolean start) {
if (zAnimation != null) if (zAnimation.isRunning() == true) zAnimation.cancel();
zAnimation = ValueAnimator.ofInt(from, to);
zAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mRotationZ = (Integer) animation.getAnimatedValue();
refreshLayout();
}
});
zAnimation.setInterpolator(new LinearInterpolator());
zAnimation.setDuration();
if (start) zAnimation.start();
return zAnimation;
} public CarrouselLayout setRotationX(int mRotationX) {
this.mRotationX = mRotationX;
return this;
} public CarrouselLayout setRotationZ(int mRotationZ) {
this.mRotationZ = mRotationZ;
return this;
} public float getRotationX() {
return mRotationX;
} public int getRotationZ() {
return mRotationZ;
} public ValueAnimator getRestAnimator() {
return restAnimator;
} public ValueAnimator getAnimationR() {
return mAnimationR;
} public void setAnimationZ(ValueAnimator zAnimation) {
this.zAnimation = zAnimation;
} public ValueAnimator getAnimationZ() {
return zAnimation;
} public void setAnimationX(ValueAnimator xAnimation) {
this.xAnimation = xAnimation;
} public ValueAnimator getAnimationX() {
return xAnimation;
} }

Android星球效果实现的更多相关文章

  1. Android动画效果之自定义ViewGroup添加布局动画

    前言: 前面几篇文章介绍了补间动画.逐帧动画.属性动画,大部分都是针对View来实现的动画,那么该如何为了一个ViewGroup添加动画呢?今天结合自定义ViewGroup来学习一下布局动画.本文将通 ...

  2. Android动画效果之Property Animation进阶(属性动画)

    前言: 前面初步认识了Android的Property Animation(属性动画)Android动画效果之初识Property Animation(属性动画)(三),并且利用属性动画简单了补间动画 ...

  3. Android动画效果之初识Property Animation(属性动画)

    前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...

  4. Android动画效果之Frame Animation(逐帧动画)

    前言: 上一篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画),今天来总结下Android的另外一种动画Frame ...

  5. Android动画效果之Tween Animation(补间动画)

    前言: 最近公司项目下个版本迭代里面设计了很多动画效果,在以往的项目中开发中也会经常用到动画,所以在公司下个版本迭代开始之前,抽空总结一下Android动画.今天主要总结Tween Animation ...

  6. Android 抽屉效果的导航菜单实现

    Android 抽屉效果的导航菜单实现 抽屉效果的导航菜单 看了很多应用,觉得这种侧滑的抽屉效果的菜单很好. 不用切换到另一个页面,也不用去按菜单的硬件按钮,直接在界面上一个按钮点击,菜单就滑出来,而 ...

  7. Android Toast效果设置

    Android Toast效果设置 Toast是Android中用来显示显示信息的一种机制,和Dialog不一样的是,Toast是没有焦点的,而且Toast显示的时间有限,过一定的时间就会自动消失.总 ...

  8. Android Toast效果

    Android Toast效果是一种提醒方式,在程序中使用一些短小的信息通知用户,过一会儿会自动消失,实现如下: FirstActivity.java package org.elvalad.acti ...

  9. 十六、Android 滑动效果汇总

    Android 滑动效果入门篇(一)—— ViewFlipper Android 滑动效果入门篇(二)—— Gallery Android 滑动效果基础篇(三)—— Gallery仿图像集浏览 And ...

随机推荐

  1. 'QueryDict' object is not callable 错误解析

    我把request内置库和 requests库  给搞混了 requests使用来发送请求的, request 而是用来获取数据的 别看只有一个单词只差,却让我找了大半天 requests.post( ...

  2. Android--UI之GridView

    前言 这篇博客介绍一下Android平台下,GridView控件的开发.针对GridView控件的一些常用属性.方法,以及注意事项进行讲解,最后将以一个Demo展示GridView控件的使用. Gri ...

  3. 对小程序框架WePY的精简总结

    一.注意点 关闭ES6转ES5关闭上传代码时样式自动补全关闭代码压缩上传本地开发选择dist目录,dist目录也用在开发者工具上实时预览和调试WePY框架对应的开发目录为src二.代码规范 - 变量方 ...

  4. Elasticsearch实践(二):搜索

    本文以 Elasticsearch 6.2.4为例. 经过前面的基础入门,我们对ES的基本操作也会了.现在来学习ES最强大的部分:全文检索. 准备工作 批量导入数据 先需要准备点数据,然后导入: wg ...

  5. java设计模式之——工厂模式

    对于java的设计模式,我还是第一次认认真真的总结,以前用的时候都不曾留意细节,现在回头再看只知道该怎么设计,却忘记当时为嘛要用它了, 所以这次就做一个demo来再次复习总结一下,希望从中能学到新体悟 ...

  6. 【EF6学习笔记】(十二)EF高级应用场景

    本篇原文链接:Advanced Entity Framework Scenarios 本篇主要讲一些使用Code First建立ASP.NET WEB应用的时候除了基础的方式以外的一些扩展方式方法: ...

  7. WebBrowser使用例子

    本文参考了:http://www.cnblogs.com/txw1958/archive/2012/09/24/CSharp-WebBrowser.html 在上文的基础上加入了 一些处理弹出对话框的 ...

  8. SQL语句方法语法总结(一)

    1.distinct:返回不重复.唯一的值. select distinct col_name from tbl_name --表中的col_name 列的值 如果有10条一样的,仅返回一条. 2.w ...

  9. OnlineJudge难度与正确度的相关性检验

    本着做题的心态,上了东莞理工学院的 oj 网:在选择难度的时候发现有些题目通过率和难度可能存在着某些关系,于是决定爬下这些数据简单查看一下是否存在关系. 一.新建项目 我是用 Scrapy 框架爬取的 ...

  10. 微软改名部又出动啦!微软宣布VSTS改名为Azure DevOps

    本篇为翻译,原文地址:https://azure.microsoft.com/en-us/blog/introducing-azure-devops/ 作者:Jamie Cool,Azure DevO ...