可以无限循环,自动旋转,停靠的3D旋转布局控件
2016-04-18 13:21
513 查看
分享一个看着不错的效果,看效果图:gitHub地址:https://github.com/dalong982242260/LoopRotarySwitch
效果如上图:
代码实现步骤:
1、首先确定这是一个自定义View,再有就是是一个ViewGroup,那么必须继承Layout(线性相对都可以)
2、需要收拾操作,要使用GestureDetector(手势检测)
3、很明显选择是动画效果,使用ValueAnimation
4、计算旋转的角度,执行动画效果
代码里边都添加了注释,就不细说了。
下边就看代码:
监听器接口类:
自定义排序算法
自动滑动Handler回调:
主要代码就是这么多。
Demo下载地址:http://download.csdn.net/detail/u012808234/9494457
效果如上图:
代码实现步骤:
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; } }
自动滑动Handler回调:
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
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories