分享一个看着不错的效果,看效果图:gitHub地址:https://github.com/dalong982242260/LoopRotarySwitch
效果如上图:
代码实现步骤:
1、首先确定这是一个自定义View,再有就是是一个ViewGroup,那么必须继承Layout(线性相对都可以)
2、需要收拾操作,要使用GestureDetector(手势检测)
3、很明显选择是动画效果,使用ValueAnimation
4、计算旋转的角度,执行动画效果
代码里边都添加了注释,就不细说了。
下边就看代码:
package com.example.looprotaryswitchlibrary.view;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.ListIterator;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Message;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.widget.RelativeLayout;
@SuppressLint("NewApi")
public class LoopRotarySwitchView extends RelativeLayout{
private Context mContext;//上下文
private GestureDetector mGestureDetector; //手势操作
private float angle; //旋转的角度
private List<View> views = new ArrayList<View>();//子view列表
private int size; //子控件个数
private OnItemClickListener mItemClickListener;
private OnItemSelcetListener mItemSelcetListener;
private OnLoopViewTouchListener mLoopViewTouchListener;
private boolean isCanCilckListener = false; //是否可以点击
private int selectPosition; //现在所选择的item
private float last_angle; //最后的角度,用来记录上一次取消touch之后的角度
private final static int LoopR = 200;
private float r = LoopR;
private float multiple = .8f; //倍数,view之间的比例
private float distance = multiple * r; //子view之间的距离
private boolean touching = false;//判断是否手指在touch
private float distancX ;//在x轴上边移动的距离
private float limitX = 30; //滑动的阀值,用来判段能不能点击
private ValueAnimator restAnimator = null;//复位动画
private ValueAnimator roAnimator = null;//旋转动画
private boolean isAuto = false;
private boolean isLeftToRightScroll = true; //默认自动滚动是从右往左
private AutoScrollHandler mHandler = new AutoScrollHandler(){
void scroll() {
if (size != 0) {//判断自动滑动从那边开始
int perAngle = 0;
if (isLeftToRightScroll) {
perAngle = 360 /size;
}else {
perAngle = -360/size;
}
if (angle == 360) {
angle = 0f;
}
AnimRotationTo(angle + perAngle, null);
}
};
};
/**
* 构造方法
* @param context
* @param attrs
* @param defStyle
*/
public LoopRotarySwitchView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
init(context);
}
/**
* 构造方法
* @param context
* @param attrs
*/
public LoopRotarySwitchView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
/**
* 构造方法
* @param context
*/
public LoopRotarySwitchView(Context context) {
super(context);
init(context);
}
public void init(Context context){
this.mContext = context;
mGestureDetector = new GestureDetector(mContext, getGestureController());//创建手势对象
}
/**
* 手势操作改变angle
* @return
*/
private GestureDetector.SimpleOnGestureListener getGestureController(){
return new GestureDetector.SimpleOnGestureListener(){
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
angle += distanceX/views.size(); //计算滑动的角度
initView();
return true;
}
};
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//进行控件界面的初始化
initView();
if (isAuto) {
mHandler.sendEmptyMessageDelayed(AutoScrollHandler.messageId, mHandler.scroll_interval);
}
}
private void initView() {
int width = getWidth();
for (int i = 0; i < views.size(); i++) {
float x0 = (float)Math.sin(Math.toRadians(angle + 180 - i * 360 / size)) * r;
float y0 = (float)Math.cos(Math.toRadians(angle + 180 - i * 360 / size)) * r;
float scale0 = (distance - y0) / (distance + r);//计算子view之间的比例,可以看到distance越大的话 比例越小,也就是大小就相差越小
views.get(i).setScaleX(scale0);//对view进行缩放
views.get(i).setScaleY(scale0);//对view进行缩放
views.get(i).setX(width /2 + x0 - views.get(i).getWidth() /2); //设置他的坐标
}
List<View > arr = new ArrayList<View>();
for (int i = 0; i < views.size(); i++) {
arr.add(views.get(i));
views.get(i).setTag(i);
}
sortList(arr);
postInvalidate();
}
//對子View 排序,然后根据变化选中是否重绘,这样是为了实现view 在显示的时候来控制当前要显示的是哪三个view,可以改变排序看下效果
@SuppressWarnings("unchecked")
private <T> void sortList(List<View> arr) {
@SuppressWarnings("rawtypes")
Comparator comparator = new SortComparator();
T[] array = arr.toArray((T[]) new Object[arr.size()]);
Arrays.sort(array, comparator);
int i = 0;
ListIterator<T> it = (ListIterator<T>) arr.listIterator();
while (it.hasNext()) {
it.next();
it.set(array[i++]);
}
for (int j = 0; j < arr.size(); j++) {
arr.get(j).bringToFront();
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//对容器中的子view放置位置
if (changed) {
checkChildView();
if (mItemSelcetListener != null) {
isCanCilckListener = true;
mItemSelcetListener.onSelect(selectPosition, views.get(selectPosition));
}
Ranimation();//子view初始化动画
}
}
public void Ranimation(){
Ranimation(1f, r);
}
public void Ranimation(boolean fromZeroToR){
if (fromZeroToR) {
Ranimation(1f, LoopR);
}else {
Ranimation(LoopR, 1f);
}
}
public void Ranimation(float from, float to) {
roAnimator = ValueAnimator.ofFloat(from,to);
roAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
r = (Float)animation.getAnimatedValue();
initView();
}
});
roAnimator.setInterpolator(new DecelerateInterpolator());
roAnimator.setDuration(2000);
roAnimator.start();
}
public void checkChildView(){
for (int i = 0; i < views.size(); i++) {//先清空views里边可能存在的view防止重复
views.remove(i);
}
final int count = getChildCount(); //获取子View的个数
size = count;
for (int i = 0; i < count; i++) {
View view = getChildAt(i); //获取指定的子view
final int position = i;
views.add(view);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//对子view添加点击事件
if (position != calculateItem()) {//
setSelectItem(position);;
}else {
if (isCanCilckListener && mItemClickListener != null) {
mItemClickListener.onItemClick(position, views.get(position));
}
}
}
});
}
}
/**
* 设置所选中的item
* @param selectItem
*/
public void setSelectItem(int selectItem){
if (selectItem >= 0) {
float corner = 0;
if (getSelectPosition() == 0) {
if (selectItem == views.size() - 1) {
corner = angle - (360 / size);
}else {
corner = angle +(360 / size);
}
}else if (getSelectPosition() == views.size() - 1) {
if (selectItem == 0) {
corner = angle + (360 / size);
}else {
corner = angle - (360 /size);
}
}else {
if (selectItem > getSelectPosition()) {
corner = angle +(360 /size);
}else {
corner = angle - (360 /size);
}
}
float position = 0 ;
float per = 360 /size;
if (corner < 0) {
per = -per;
}
float minValue = (int)(corner /per) * per;
float maxValue = (int)(corner / per) * per;
if (corner >= 0) {
if (corner - last_angle > 0) {
position = maxValue;
}else {
position = minValue;
}
}else {
if (corner - last_angle < 0) {
position = maxValue;
}else {
position = minValue;
}
}
if (size > 0) {//旋转动画
AnimRotationTo(position, null);
}
}
}
public float getAngle() {
return angle;
}
public void setAngle(float angle) {
this.angle = angle;
}
public void setmItemClickListener(OnItemClickListener mItemClickListener) {
this.mItemClickListener = mItemClickListener;
}
public void setmItemSelcetListener(OnItemSelcetListener mItemSelcetListener) {
this.mItemSelcetListener = mItemSelcetListener;
}
public void setmLoopViewTouchListener(
OnLoopViewTouchListener mLoopViewTouchListener) {
this.mLoopViewTouchListener = mLoopViewTouchListener;
}
/**
* 计算现在选中item
* @return
*/
public int calculateItem(){
return (int)(angle / ( 360 / size)) % size;
}
public int getSelectPosition() {
return selectPosition;
}
public float getR() {
return r;
}
public LoopRotarySwitchView setR(float r) {
this.r = r;
distance = multiple * r;
return this;
}
private boolean onTouch(MotionEvent event){
if (event.getAction() == MotionEvent.ACTION_DOWN) {
last_angle = angle ;
touching = true;
}
boolean sc = mGestureDetector.onTouchEvent(event);
if (sc) {
this.getParent().requestDisallowInterceptTouchEvent(true);
}
if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) {
touching = false;
restPosition();//重置
return true;
}
return true;
}
private void restPosition() {
if (size == 0) {
return ;
}
float position = 0;
float per = 360 /size;
if (angle < 0) {
per = -per;
}
float minValue = (int)(angle / per) * per;
float maxValue = (int)(angle / per) * per + per;
if (angle > 0) {
if (angle -last_angle > 0) {
position = maxValue;
}else {
position = minValue;
}
}else {
if (angle - last_angle > 0) {
position = maxValue;
}else {
position = minValue;
}
}
AnimRotationTo(position,null);
}
/**
* 动画
* @param position
* @param object
*/
private void AnimRotationTo(float position, final Runnable runnable) {
if (angle == position) {//如果相同说明不需要旋转
return;
}
restAnimator = ValueAnimator.ofFloat(angle,position);
restAnimator.setInterpolator(new DecelerateInterpolator());//设置旋转减速插值器
restAnimator.setDuration(300);
restAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (touching == false) {
angle = (Float)animation.getAnimatedValue();
initView();
}
}
});
restAnimator.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
if (touching == false) {
selectPosition = calculateItem();
}
if (mItemSelcetListener != null) {
mItemSelcetListener.onSelect(selectPosition, views.get(selectPosition));
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
});
if (runnable != null) {
restAnimator.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
runnable.run();
}
@Override
public void onAnimationCancel(Animator animation) {
// TODO Auto-generated method stub
}
});
}
restAnimator.start();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mLoopViewTouchListener != null) {
mLoopViewTouchListener.onTouch(event);
}
isCanClickListener(event);
return true;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
onTouch(ev);
if (mLoopViewTouchListener != null) {
mLoopViewTouchListener.onTouch(ev);
}
isCanClickListener(ev);
return super.dispatchTouchEvent(ev);
}
public void isCanClickListener(MotionEvent event){
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
distancX = event.getX();
if (isAuto) {
mHandler.removeMessages(AutoScrollHandler.messageId);
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (isAuto) {
mHandler.sendEmptyMessageDelayed(AutoScrollHandler.messageId, mHandler.scroll_interval);
}
if (event.getX() - distancX > limitX || distancX - event.getX() > limitX) {
isCanCilckListener = false;
}else{
isCanCilckListener = true;
}
break;
case MotionEvent.ACTION_MOVE:
break;
default:
break;
}
}
/**
* 自动滚动
*/
public void AutoScroll() {
angle = angle + (360/size);
initView();
}
public boolean isAuto() {
return isAuto;
}
public void setAuto(boolean isAuto) {
this.isAuto = isAuto;
}
public boolean isLeftScroll() {
return isLeftToRightScroll;
}
public void setLeftScroll(boolean isLeftToRightScroll) {
this.isLeftToRightScroll = isLeftToRightScroll;
}
public void setScrollInterval(long time){
if (mHandler != null) {
mHandler.setScroll_interval(time);
}
}
}
</pre><pre code_snippet_id="1651394" snippet_file_name="blog_20160418_3_3380648" name="code" class="java">
监听器接口类:
package com.example.looprotaryswitchlibrary.view;
import android.view.View;
public interface OnItemClickListener {
void onItemClick(int position,View view);
}
package com.example.looprotaryswitchlibrary.view;
import android.view.View;
public interface OnItemSelcetListener {
void onSelect(int position, View View);
}
package com.example.looprotaryswitchlibrary.view;
import android.view.MotionEvent;
public interface OnLoopViewTouchListener {
void onTouch(MotionEvent event);
}
package com.example.looprotaryswitchlibrary.view;
import java.util.Comparator;
import android.annotation.SuppressLint;
import android.view.View;
public class SortComparator implements Comparator<View>{
@SuppressLint("NewApi")
@Override
public int compare(View lhs, View rhs) {
int result = 0;
result = (int)(1000 * lhs.getScaleX() - 1000 * rhs.getScaleX());
return result;
}
}
package com.example.looprotaryswitchlibrary.view;
import android.os.Handler;
import android.os.Message;
public abstract class AutoScrollHandler extends Handler{
long scroll_interval = 3000;//自动滚动时间间隔,默认值
final static int messageId = 1000;
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
int what = msg.what;
switch (what) {
case messageId:
scroll();
sendMessage();
break;
default:
break;
}
super.handleMessage(msg);
}
public long getScroll_interval() {
return scroll_interval;
}
public void setScroll_interval(long scroll_interval) {
this.scroll_interval = scroll_interval;
}
public void sendMessage(){
removeMessages(messageId);
sendEmptyMessageDelayed(messageId,scroll_interval);
}
abstract void scroll();
}
主要代码就是这么多。
Demo下载地址:http://download.csdn.net/detail/u012808234/9494457