Android星球效果实现
在项目中看着这个旋转效果挺炫的,就抽取出来做个记录。主要是使用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星球效果实现的更多相关文章
- Android动画效果之自定义ViewGroup添加布局动画
前言: 前面几篇文章介绍了补间动画.逐帧动画.属性动画,大部分都是针对View来实现的动画,那么该如何为了一个ViewGroup添加动画呢?今天结合自定义ViewGroup来学习一下布局动画.本文将通 ...
- Android动画效果之Property Animation进阶(属性动画)
前言: 前面初步认识了Android的Property Animation(属性动画)Android动画效果之初识Property Animation(属性动画)(三),并且利用属性动画简单了补间动画 ...
- Android动画效果之初识Property Animation(属性动画)
前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...
- Android动画效果之Frame Animation(逐帧动画)
前言: 上一篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画),今天来总结下Android的另外一种动画Frame ...
- Android动画效果之Tween Animation(补间动画)
前言: 最近公司项目下个版本迭代里面设计了很多动画效果,在以往的项目中开发中也会经常用到动画,所以在公司下个版本迭代开始之前,抽空总结一下Android动画.今天主要总结Tween Animation ...
- Android 抽屉效果的导航菜单实现
Android 抽屉效果的导航菜单实现 抽屉效果的导航菜单 看了很多应用,觉得这种侧滑的抽屉效果的菜单很好. 不用切换到另一个页面,也不用去按菜单的硬件按钮,直接在界面上一个按钮点击,菜单就滑出来,而 ...
- Android Toast效果设置
Android Toast效果设置 Toast是Android中用来显示显示信息的一种机制,和Dialog不一样的是,Toast是没有焦点的,而且Toast显示的时间有限,过一定的时间就会自动消失.总 ...
- Android Toast效果
Android Toast效果是一种提醒方式,在程序中使用一些短小的信息通知用户,过一会儿会自动消失,实现如下: FirstActivity.java package org.elvalad.acti ...
- 十六、Android 滑动效果汇总
Android 滑动效果入门篇(一)—— ViewFlipper Android 滑动效果入门篇(二)—— Gallery Android 滑动效果基础篇(三)—— Gallery仿图像集浏览 And ...
随机推荐
- [原创]K8Cscan插件之FTP弱口令扫描
[原创]K8 Cscan 大型内网渗透自定义扫描器 https://www.cnblogs.com/k8gege/p/10519321.html Cscan简介:何为自定义扫描器?其实也是插件化,但C ...
- react-native热更新之CodePush详细介绍及使用方法
react-native热更新之CodePush详细介绍及使用方法 2018年03月04日 17:03:21 clf_programing 阅读数:7979 标签: react native热更新co ...
- 破解第三课 关键跳和关键CALL
课前自泼凉水: 前两课的介绍的方法,不管是NOP填充还是JUM的无条件跳转,其实都有极大的局限性. 甚至单纯就效果而言,几乎无用. 且不说利用OD搜索关键字本身就很难搜得到. 就现在的软件保护而言,也 ...
- 今日头条面试题——LRU原理和Redis实现
很久前参加过今日头条的面试,遇到一个题,目前半部分是如何实现 LRU,后半部分是 Redis 中如何实现 LRU. 我的第一反应应该是内存不够的场景下,淘汰旧内容的策略.LRU ... Least R ...
- (转)python生态环境简介
Python生态环境简介 作者: Mir Nazim 原文: Python Ecosystem - An Introduction 译者: dccrazyboy 原译: Python生态环境简介 当 ...
- 【SqlServer系列】数据库三大范式
1 概述 一般地,在进行数据库设计时,应遵循三大原则,也就是我们通常说的三大范式,即第一范式要求确保表中每列的原子性,也就是不可拆分:第二范式要求确保表中每列与主键相关,而不能只与主键的某部分相关 ...
- 修改wampsever中MySql5.7.14默认为空的密码
①打开WAMP找中MySql控制台,提示输入密码,开始密码为空,直接按回车 ②输入[use mysql],控制台提示[Database changed] ③输入[update user set aut ...
- Nacos系列:基于Nacos的配置中心
前言 在看正文之前,我想请你回顾一下自己待过的公司都是怎么管理配置的,我想应该会有以下几种方式: 1.硬编码 没有什么配置不配置的,直接写在代码里面,比如使用常量类 优势:对开发友好,开发清楚地知道代 ...
- WPF系列(1)WPF和XAML基础
终于下定决心开始更新WPF一个系列的文章,这里主要是出于两个目的,一是自己对所学的知识有一个系统的总结,二十希望能对其他人有些帮助,如果您觉得我写的不好,欢迎提意见. 那么既然我要开始写WPF,那我们 ...
- Winform下KeyDown,KeyPress,KeyUp事件的总结(转)
原文: http://www.cnblogs.com/xiashengwang/archive/2011/09/15/2578798.html 在winform程序中,经常会用到这几个事件用于控制数字 ...