首页 > 代码库 > RotateCard(自定义旋转view)
RotateCard(自定义旋转view)
使用方法Demo
package com.example.displaydemo;import java.util.ArrayList;import com.example.displaydemo.RotateCard.OnItemClickListener;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.widget.ImageView;import android.widget.Toast;public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RotateCard card = (RotateCard) findViewById(R.id.card); ArrayList<View> views = new ArrayList<View>(); ImageView img; img = new ImageView(MainActivity.this); img.setImageResource(R.drawable.p1); views.add(img); img = new ImageView(MainActivity.this); img.setImageResource(R.drawable.p2); views.add(img); img = new ImageView(MainActivity.this); img.setImageResource(R.drawable.p3); views.add(img); img = new ImageView(MainActivity.this); img.setImageResource(R.drawable.p4); views.add(img); img = new ImageView(MainActivity.this); img.setImageResource(R.drawable.p5); views.add(img); img = new ImageView(MainActivity.this); img.setImageResource(R.drawable.p6); views.add(img); // 传入view列表和view的宽高 card.commitViews(views, 200, 200);
// 添加点击事件监听 card.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClickListener(View view, int index) { // TODO Auto-generated method stub Toast.makeText(MainActivity.this, "The " + index + " is clicked!", Toast.LENGTH_SHORT) .show(); } }); }}
RotateCard.java
package com.example.displaydemo;import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import android.animation.Animator;import android.animation.AnimatorListenerAdapter;import android.animation.AnimatorSet;import android.animation.ObjectAnimator;import android.animation.ValueAnimator;import android.animation.ValueAnimator.AnimatorUpdateListener;import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewTreeObserver;import android.view.animation.DecelerateInterpolator;import android.widget.FrameLayout;/** * RotateCard * * @author 小明湖畔 * * @Enail 463785757@qq.com * * @time 2015-1-17 18:02 */public class RotateCard extends FrameLayout { private static String INFO_TAG = "RotateCard"; private ArrayList<RotateCardViewHolder> viewHolderList; private ArrayList<View> viewList; private int ovalA, ovalB;// 椭圆轨迹的长半轴,短半轴 private int parentWidth, parentHeight;// RotateCard的宽高 private int viewWidth, viewHeight;// viewSet中的View的宽高 FrameLayout.LayoutParams viewpParams;// view的LayoutParams private float angleOffset;// 手指每滑动一像素需要绕椭圆中心旋转的度数 private RotateCardOnTouchListener mTouchListener;// 供RotateCard和子View同时使用,处理滑动事件 // 增加绘制前的监听以获得RotateCard的宽高 private ViewTreeObserver.OnPreDrawListener onPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { parentWidth = RotateCard.this.getMeasuredWidth(); parentHeight = RotateCard.this.getMeasuredHeight(); angleOffset = (float) ((180.0 / parentWidth) * 1.5); // Log.i(INFO_TAG, "parentWidth:" + parentWidth); // Log.i(INFO_TAG, "parentHeight:" + parentHeight); // Log.i(INFO_TAG, "angleOffset:" + angleOffset); if (viewList != null && viewList.size() > 0) { afterGetParamsAndViews(); } // 除去监听 RotateCard.this.getViewTreeObserver().removeOnPreDrawListener( onPreDrawListener); return true; } }; public RotateCard(Context context) { super(context); // TODO Auto-generated constructor stub init(); } public RotateCard(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub init(); } public RotateCard(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub init(); } private void init() { // 增加RotateCard绘制之前的监听 RotateCard.this.getViewTreeObserver().addOnPreDrawListener( onPreDrawListener); // 增加RotateCard的滑动监听 mTouchListener = new RotateCardOnTouchListener(); this.setOnTouchListener(mTouchListener); } /** * 必须保证传入的View集合的每个View的大小一致,否则显示可能不准确 * * @param viewSet * View集合 */ public void commitViews(ArrayList<View> viewList, int viewWidth, int viewHeight) { this.removeAllViews(); this.viewList = viewList; this.viewWidth = viewWidth; this.viewHeight = viewHeight; viewpParams = new LayoutParams(viewWidth, viewHeight); // Log.i(INFO_TAG, "viewWidth:" + viewWidth); // Log.i(INFO_TAG, "viewHeight:" + viewHeight); if (parentWidth != 0 && parentHeight != 0) { afterGetParamsAndViews(); } } /** * 在获得RotateCard后并且调用过commitViews函数后,此函数被调用 */ private void afterGetParamsAndViews() { ovalA = (parentWidth - viewWidth) / 2; ovalB = (int) ((parentHeight - viewHeight * 1.5) / 2); int viewNum = viewList.size(); float anglePerView = (float) (360.0 / viewNum); // Log.i(INFO_TAG, "ovalA:" + ovalA); // Log.i(INFO_TAG, "ovalB:" + ovalB); // Log.i(INFO_TAG, "viewNum:" + viewNum); // Log.i(INFO_TAG, "anglePerView:" + anglePerView); if (viewHolderList == null) { viewHolderList = new ArrayList<RotateCardViewHolder>(); } else { viewHolderList.clear(); } float tmpAngle = 270.0f;// 第一个view在270度的位置,即最前的位置 for (int i = 0; i < viewNum; ++i) { int index = i + 1; View view = viewList.get(i); // 添加子view的滑动事件监听 view.setOnTouchListener(mTouchListener); // 添加子view的点击事件监听 view.setOnClickListener(new ItemOnClickListener(index)); // 为子view创建一个RotateCardViewHolder RotateCardViewHolder holder = new RotateCardViewHolder(view, tmpAngle, index); // 将holder捆绑在子view上,方面从子view直接获取holder view.setTag(holder); viewHolderList.add(holder); // 增加anglePerView的角度,用于确定下一个view的位置 tmpAngle += anglePerView; } // 把子view添加到RotateCard中 loadViewBySequence(); } /** * 把所有view旋转angleDiff的角度 * * @param angleDiff * 旋转的角度 */ private void rotateViewsByAngle(float angleDiff) { // 若有动画在进行,则不进行旋转 if (set != null && set.isRunning()) { return; } // 旋转每一个view for (RotateCardViewHolder holder : viewHolderList) { holder.setAngle(holder.getAngle() + angleDiff); } // 重新载入view以确保他们正确的遮挡关系 loadViewBySequence(); } /** * 通过对vew排序后重新把view添加到RotateCard中来决定他们的遮挡关系 ,越靠前的view越慢添加 */ private void loadViewBySequence() { Collections.sort(viewHolderList, new RotateCardViewHolderComparator()); this.removeAllViews(); for (RotateCardViewHolder holder : viewHolderList) { this.addView(holder.getView(), viewpParams); } } /** * 封装对view的一些操作 */ public class RotateCardViewHolder { private View mView; private float mAngle; private int index; public RotateCardViewHolder(View view, float angle, int index) { this.mView = view; this.mAngle = angle; this.index = index; setAngle(angle); } /** * 设置位置和大小 */ public void resetPositionAndScale() { // 设置大小 float angleDiff = getAngleDiffWith90(); float scale = (float) ((1.0 * angleDiff / 180) * 0.75 + 0.25); mView.setScaleX(scale); mView.setScaleY(scale); // 设置位置 float x = (float) (ovalA * Math.cos(mAngle / 180.0 * Math.PI) + parentWidth / 2 - viewWidth / 2); float y = (float) (parentHeight / 2 - ovalB * Math.sin(mAngle / 180.0 * Math.PI) - viewHeight / 2); mView.setX(x); mView.setY(y); } /** * 设置view的角度,同时改变他的显示的大小和位置 * * @param angle * view的角度 */ public void setAngle(float angle) { // 保证mAngle在0~360之间 mAngle = angle; while (mAngle < 0 || mAngle > 360) { if (mAngle < 0) { mAngle = (mAngle + 360) % 360; } else { mAngle = mAngle % 360; } } resetPositionAndScale(); } /** * 获取view的角度 * * @return view的角度 */ public float getAngle() { return mAngle; } /** * 获取view一开始的序号 * * @return view的序号 */ public int getIndex() { return index; } /** * 获取view的实例 * * @return view的实例 */ public View getView() { return mView; } /** * 获取该view当前位置跟90度位置相差的度数 * * @return 跟90度相差的度数 */ public float getAngleDiffWith90() { float tmpAngle = mAngle > 270 ? mAngle - 360 : mAngle; float angleDiff = Math.abs(tmpAngle - 90); return angleDiff; } /** * 获取该view当前位置跟270度位置相差的度数 * * @return 跟270度相差的度数 */ public float getAngleDiffWith270() { float tmpAngle = mAngle < 90 ? mAngle + 360 : mAngle; float angleDiff = Math.abs(tmpAngle - 270); return angleDiff; } } /** * 比较器,用来把view按照度数越接近270度越靠后的顺序排序 * */ public class RotateCardViewHolderComparator implements Comparator<RotateCardViewHolder> { @Override public int compare(RotateCardViewHolder a, RotateCardViewHolder b) { // TODO Auto-generated method stub return a.getAngleDiffWith270() > b.getAngleDiffWith270() ? -1 : 1; } } private float lastX, nowX;// 上次手指的x坐标位置和当前手指的x坐标位置 private VelocityTracker vTracker;// 监控手指滑动的使用率 private AnimatorSet set;// 动画集 private boolean justCancelAnimation = false;// 是否刚刚通过点击屏幕使正在进行的动画取消,用于取消动画的同事发生的点击事件 @Override public boolean dispatchTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: { // 若动画正在进行,则取消动画 if (set != null && set.isRunning()) { set.cancel(); set = null; justCancelAnimation = true; } // 获取VelocityTracker的一个实例 vTracker = VelocityTracker.obtain(); // 记录lastX lastX = event.getX(); } break; case MotionEvent.ACTION_MOVE: { justCancelAnimation = false; // 把动作送到vTracker vTracker.addMovement(event); // 记录nowX nowX = event.getX(); } break; case MotionEvent.ACTION_UP: { if (justCancelAnimation) { justCancelAnimation = false; return true;// 用来取消点击事件 } // 设置计算单元,为1秒 vTracker.computeCurrentVelocity(1000); // 获取在x轴方向1秒内划过的像素数 float xSpeed = vTracker.getXVelocity(); // 活动过快,则执行动画,让RotateCard再旋转一会才停下 if (Math.abs(xSpeed) > 800.0f) { // 还要旋转的角度 float angleDiff = xSpeed / 10; // 还要旋转的时间 int duration = (int) (Math.abs(angleDiff) / 180 * 1000); // 设置动画集 set = new AnimatorSet(); ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f) .setDuration(duration); animation.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // TODO Auto-generated method stub loadViewBySequence(); } }); set.play(animation); for (RotateCardViewHolder _holder : viewHolderList) { ObjectAnimator oa = ObjectAnimator.ofFloat(_holder, "angle", _holder.getAngle(), _holder.getAngle() + angleDiff).setDuration( duration); oa.setInterpolator(new DecelerateInterpolator()); set.play(oa).with(animation); } // 开始动画集合 set.start(); } } break; } return super.dispatchTouchEvent(event); } private class RotateCardOnTouchListener implements OnTouchListener { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: { } break; case MotionEvent.ACTION_MOVE: { // 此次手指滑动需要旋转的度数 float angleDiff = (nowX - lastX) * angleOffset; // 旋转angleDiff的角度 rotateViewsByAngle(angleDiff); // 记录lastX lastX = nowX; } break; case MotionEvent.ACTION_UP: { } break; } if (v instanceof RotateCard) { // 若是手指的位置是RotateCard,返回true让事件继续传递给RotateCard return true; } else { // 若是手指的位置是子view,返回false让子view的点击事件可以响应 return false; } } } public class ItemOnClickListener implements OnClickListener { private int index; public ItemOnClickListener(int index) { this.index = index; } /** * * 把被点击的view旋转的最前面 */ @Override public void onClick(View v) { // TODO Auto-generated method stub final View tView = v; final RotateCardViewHolder holder = (RotateCardViewHolder) v .getTag(); float angle = holder.getAngle(); // 需要旋转的度数 float angleDiff = angle >= 0.0f && angle <= 90.0f ? -90.0f - angle : 270.0f - angle; // 是否子执行view的点击事件(若被点击view在很靠前,则旋转后执行它的点击事件) final boolean performClick = Math.abs(angleDiff) < 45.0f; int duration = (int) (Math.abs(angleDiff) / 180 * 1000); // 初始化动画集 set = new AnimatorSet(); ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f) .setDuration(duration); animation.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // TODO Auto-generated method stub loadViewBySequence(); } }); set.play(animation); if (performClick) { ObjectAnimator aa = ObjectAnimator.ofFloat(tView, "alpha", 1.0f, 0.5f, 1.0f).setDuration(800); ObjectAnimator saX = ObjectAnimator.ofFloat(tView, "scaleX", 1.0f, 1.5f, 1.0f).setDuration(800); ObjectAnimator saY = ObjectAnimator.ofFloat(tView, "scaleY", 1.0f, 1.5f, 1.0f).setDuration(800); set.play(aa).with(animation); set.play(saX).with(animation); set.play(saY).with(animation); } for (RotateCardViewHolder _holder : viewHolderList) { ObjectAnimator oa = ObjectAnimator.ofFloat(_holder, "angle", _holder.getAngle(), _holder.getAngle() + angleDiff) .setDuration(duration); oa.setInterpolator(new DecelerateInterpolator()); set.play(oa).with(animation); } set.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { // TODO Auto-generated method stub if (mOnItemClickListener != null && performClick) { // 执行外部设置的点击事件 mOnItemClickListener.onItemClickListener(tView, index); } } @Override public void onAnimationCancel(Animator animation) { // TODO Auto-generated method stub tView.setAlpha(1.0f); holder.resetPositionAndScale(); } }); set.start(); } } /** * 子view点击事件监听器OnItemClickListener * * RotateCard内部需要自行处理子vew的点击事件,OnItemClickListener监听器供外部代码使用 */ public interface OnItemClickListener { public void onItemClickListener(View view, int index); } private OnItemClickListener mOnItemClickListener; /** * 添加子view点击事件监听 * * @param onItemClickListener */ public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.mOnItemClickListener = onItemClickListener; }}
RotateCard(自定义旋转view)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。