具有排序,增加,删除,翻页动画的scrollview

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012591964/article/details/70843585

网上主要都是手势拖拽动画,关于按键的动画比较少,因此做了一套。

先来一个效果图:

下面是源码:

public class HorizontalPageScorllView extends HorizontalScrollView implements OnKeyListener {

	private boolean mAnimationEnd = true;
	/**
	 * 刚开始拖拽的item对应的View
	 */
	private View mStartDragItemView = null;
	/**
	 * item镜像的布局参数
	 */
	private WindowManager.LayoutParams mWindowLayoutParams;
	private WindowManager mWindowManager;
	/**
	 * ScrollView距离屏幕顶部的偏移量
	 */
	private int mOffset2Top;

	/**
	 * ScrollView距离屏幕左边的偏移量
	 */
	private int mOffset2Left;
	/**
	 * 用于移动的镜像,这里直接用一个ImageView
	 */
	private ImageView mDragImageView;
	/**
	 * 移动的item对应的Bitmap
	 */
	private Bitmap mDragBitmap;
	/**
	 * 是否可以拖拽,默认不可以
	 */
	private boolean isDrag = false;
	/**
	 * 正在拖拽的position
	 */
	private int mDragPosition = -1;

	/**
	 * 镜像X坐标
	 */
	private int moveX;
	/**
	 * 镜像Y坐标
	 */
	private int moveY;
	/**
	 * 镜像X轴需要移动的距离
	 */
	private int moveXdistance = 0;
	/**
	 * 镜像Y轴需要移动的距离
	 */
	private int moveYdistance = 0;

	/**
	 * 翻页距离
	 */
	private int scrollXDelta;

	private static final int DEFAULT_EDGE_OFFSET = 60;
	private int mEdgeOffset = DEFAULT_EDGE_OFFSET;
	private LinearLayout mLinearLayout;
	private ArrayList<View> childrens;
	private final int ANIMATION_TIME = 800;

	private final int MARGIN_RIGHT = 20;

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

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

	public HorizontalPageScorllView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context);
	}

	private void init(Context context) {
		setWillNotDraw(false);
		mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
		mWindowLayoutParams = new WindowManager.LayoutParams();
		childrens = new ArrayList<View>();
	}

	@Override
	public void draw(Canvas arg0) {
		super.draw(arg0);
	}

	/**
	 * 设置边缘间隙值,默认为60px
	 * 
	 * @param tOffSet
	 */
	public void setEdgeOffset(int tOffSet) {
		mEdgeOffset = tOffSet;
	}

	public void setChildren(ArrayList<View> views) {
		if (views == null || views.size() == 0)
			return;
		mLinearLayout = (LinearLayout) findViewById(R.id.linearlayout);
		for (int i = 0; i < views.size(); i++) {
			LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(200, 200);
			layoutParams.rightMargin = MARGIN_RIGHT;
			mLinearLayout.addView(views.get(i), layoutParams);
			childrens.add(views.get(i));
		}
		childrens.get(0).post(new Runnable() {
			@Override
			public void run() {
				childrens.get(0).requestFocusFromTouch();
			}
		});
	}

	public void addChildren(View v, final int index) {
		childrens.add(index, v);
		LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(200, 200);
		layoutParams.rightMargin = MARGIN_RIGHT;
		mLinearLayout.addView(v, index, layoutParams);
		final ViewTreeObserver observer = getViewTreeObserver();
		observer.addOnPreDrawListener(new OnPreDrawListener() {
			@Override
			public boolean onPreDraw() {
				observer.removeOnPreDrawListener(this);
				animateMove(index, true);
				return true;
			}
		});
	}

	public void removeChildren(final int index) {
		childrens.remove(index);
		final ViewTreeObserver observer = mLinearLayout.getViewTreeObserver();
		observer.addOnPreDrawListener(new OnPreDrawListener() {
			@Override
			public boolean onPreDraw() {
				observer.removeOnPreDrawListener(this);
				animateMove(index, false);
				return true;
			}
		});
		mLinearLayout.removeViewAt(index);
	}

	/**
	 * Compute the amount to scroll in the X direction in order to get a
	 * rectangle completely on the screen (or, if taller than the screen, at
	 * least the first screen size chunk of it).
	 *
	 * @param rect
	 *            The rect.
	 * @return The scroll delta.
	 */
	protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
		/**
		 * 
		 * 切页处理原理: 和recyclerview不同,scrollview里面所有的元素都是可见的,所以不需要对边界元素焦点控制等做特殊处理。
		 * scrollview只会对未完全显示在范围内的子元素做滚动处理,
		 * 所以只需要对滚动偏移量scrollXDelta的计算进行处理即可,并没有太复杂的计算处理。
		 * 
		 */
		if (getChildCount() == 0)
			return 0;
		int width = getWidth();
		int screenLeft = getScrollX();
		int screenRight = screenLeft + width;
		int fadingEdge = getHorizontalFadingEdgeLength();
		if (rect.left > 0) {
			screenLeft += fadingEdge;
		}
		if (rect.right < getChildAt(0).getWidth()) {
			screenRight -= fadingEdge;
		}
		scrollXDelta = 0;
		if (rect.right > screenRight && rect.left > screenLeft) {
			scrollXDelta += (rect.left - screenLeft) - mEdgeOffset; // 修改
			int right = getChildAt(0).getRight();
			int distanceToRight = right - screenRight;
			scrollXDelta = Math.min(scrollXDelta, distanceToRight);

		} else if (rect.left < screenLeft && rect.right < screenRight) {
			scrollXDelta -= (screenRight - rect.right) - mEdgeOffset; // 修改
			scrollXDelta = Math.max(scrollXDelta, -getScrollX());
		}
		return scrollXDelta;
	}

	/**
	 * 创建拖动的镜像
	 * 
	 * @param bitmap
	 * @param downX
	 *            按下的点相对父控件的X坐标
	 * @param downY
	 *            按下的点相对父控件的X坐标
	 */
	private void createDragImage(Bitmap bitmap, int downX, int downY) {
		mWindowLayoutParams.format = PixelFormat.TRANSLUCENT; // 图片之外的其他地方透明
		mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
		mWindowLayoutParams.x = downX + mOffset2Left;
		mWindowLayoutParams.y = downY + mOffset2Top;
		mWindowLayoutParams.alpha = 0.55f; // 透明度
		mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
		mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
		mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
				| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;

		mDragImageView = new ImageView(getContext());
		mDragImageView.setImageBitmap(bitmap);
		mWindowManager.addView(mDragImageView, mWindowLayoutParams);
	}

	private void removeDragImage() {
		if (mDragImageView != null) {
			mWindowManager.removeView(mDragImageView);
			mDragImageView = null;
		}
	}

	@Override
	public boolean dispatchKeyEvent(KeyEvent event) {
		if (mAnimationEnd != true)
			return true;
		if (event.getAction() == KeyEvent.ACTION_DOWN) {
			switch (event.getKeyCode()) {
			case KeyEvent.KEYCODE_DPAD_UP:
			case KeyEvent.KEYCODE_DPAD_DOWN:
				if (isDrag) {
					return true;
				}
			}
		}
		return super.dispatchKeyEvent(event);
	}

	@Override
	public boolean onKey(View v, int keyCode, KeyEvent event) {
		if (event.getAction() == KeyEvent.ACTION_DOWN) {
			switch (event.getKeyCode()) {
			case KeyEvent.KEYCODE_ENTER:
				if (!isDrag)
					beginDrag(v);
				else
					stopDrag(v);
				break;
			case KeyEvent.KEYCODE_DPAD_RIGHT:
			case KeyEvent.KEYCODE_DPAD_LEFT:
				moveAction(keyCode);
				break;
			}
		}
		return false;
	}

	public void moveAction(int keycode) {
		if (!isDrag)
			return;
		int newposition = 0;
		if (isDrag && mStartDragItemView != null) {
			switch (keycode) {
			case KeyEvent.KEYCODE_DPAD_RIGHT:
				newposition = mDragPosition + 1;
				break;
			case KeyEvent.KEYCODE_DPAD_LEFT:
				newposition = mDragPosition - 1;
				break;
			}

		}
		if (newposition < 0 || newposition > childrens.size() - 1)
			return;
		moveXdistance = (int) mLinearLayout.getChildAt(newposition).getX()
				- (int) mLinearLayout.getChildAt(mDragPosition).getX();
		moveYdistance = 0;
		moveX = (int) mLinearLayout.getChildAt(newposition).getX() - getScrollX();
		moveY = (int) mLinearLayout.getChildAt(newposition).getY();
		onSwapItem(newposition);
	}

	public void beginDrag(View v) {
		// TODO 自动生成的方法存根
		isDrag = true;
		mDragPosition = mLinearLayout.indexOfChild(v);

		mOffset2Top = this.getTop();
		mOffset2Left = this.getLeft();
		mStartDragItemView = v;
		mStartDragItemView.setAlpha(0);// 隐藏该item

		// 开启mDragItemView绘图缓存
		mStartDragItemView.setDrawingCacheEnabled(true);
		// 获取mDragItemView在缓存中的Bitmap对象
		mDragBitmap = Bitmap.createBitmap(mStartDragItemView.getDrawingCache());
		// 这一步很关键,释放绘图缓存,避免出现重复的镜像
		mStartDragItemView.destroyDrawingCache();

		// 根据我们按下的点显示item镜像
		createDragImage(mDragBitmap, (int) mStartDragItemView.getX() - getScrollX(), (int) mStartDragItemView.getY());
	}

	public void stopDrag(View v) {
		if (!isDrag)
			return;
		isDrag = false;
		v.setAlpha(100);
		removeDragImage();
	}

	private void onMoveImage(int moveX, int moveY) {
		if (scrollXDelta == 0) {
			ValueAnimator mAnimator = ValueAnimator.ofInt(moveX - scrollXDelta + mOffset2Left - moveXdistance,
					moveX - scrollXDelta + mOffset2Left);
			mAnimator.addUpdateListener(new AnimatorUpdateListener() {

				@Override
				public void onAnimationUpdate(ValueAnimator animation) {
					// 3.为目标对象的属性设置计算好的属性值
					mWindowLayoutParams.x = Integer.parseInt(animation.getAnimatedValue().toString());
					mWindowManager.updateViewLayout(mDragImageView, mWindowLayoutParams);
				}
			});
			// 4.设置动画的持续时间、是否重复及重复次数等属性
			mAnimator.setDuration(ANIMATION_TIME);
			mAnimator.setRepeatCount(0);
			// 5.为ValueAnimator设置目标对象并开始执行动画
			mAnimator.setTarget(mDragImageView);
			mAnimator.start();
		} else {
			mWindowLayoutParams.x = moveX + mOffset2Left;
			mWindowLayoutParams.y = moveY + mOffset2Top;
			mWindowManager.updateViewLayout(mDragImageView, mWindowLayoutParams); // 更新镜像的位置
		}

	}

	/**
	 * 交换item,并且控制item之间的显示与隐藏效果
	 * 
	 * @param moveX
	 * @param moveY
	 */
	private void onSwapItem(final int tempPosition) {
		// 假如tempPosition 改变了并且tempPosition不等于-1,则进行交换
		String t = ((TextView) childrens.get(mDragPosition)).getText().toString();
		String t1 = ((TextView) childrens.get(tempPosition)).getText().toString();
		((TextView) childrens.get(mDragPosition)).setText(t1);
		((TextView) childrens.get(tempPosition)).setText(t);
		childrens.get(mDragPosition).setAlpha(100);
		childrens.get(tempPosition).setAlpha(0);
		if (tempPosition != mDragPosition) {
			final ViewTreeObserver observer = getViewTreeObserver();
			observer.addOnPreDrawListener(new OnPreDrawListener() {
				@Override
				public boolean onPreDraw() {
					observer.removeOnPreDrawListener(this);
					animateReorder(mDragPosition, tempPosition);
					mDragPosition = tempPosition;
					return true;
				}
			});
		}
	}

	/**
	 * item的交换动画效果
	 * 
	 * @param oldPosition
	 * @param newPosition
	 */
	private void animateReorder(final int oldPosition, final int newPosition) {
		final boolean isForward = newPosition > oldPosition;
		List<Animator> resultList = new LinkedList<Animator>();
		if (isForward) {
			View view = mLinearLayout.getChildAt(oldPosition);
			resultList.add(createTranslationAnimations(view, view.getWidth(), 0, 0, 0));
			view = mLinearLayout.getChildAt(newPosition);
			resultList.add(createTranslationAnimations(view, -view.getWidth(), 0, 0, 0));
		} else {
			View view = mLinearLayout.getChildAt(oldPosition);
			resultList.add(createTranslationAnimations(view, -view.getWidth(), 0, 0, 0));
			view = mLinearLayout.getChildAt(newPosition);
			resultList.add(createTranslationAnimations(view, view.getWidth(), 0, 0, 0));
		}

		AnimatorSet resultSet = new AnimatorSet();
		resultSet.playTogether(resultList);
		if (scrollXDelta == 0)
			resultSet.setDuration(ANIMATION_TIME);
		else
			resultSet.setDuration(200);
		resultSet.setInterpolator(new AccelerateDecelerateInterpolator());
		resultSet.addListener(new AnimatorListenerAdapter() {
			@Override
			public void onAnimationStart(Animator animation) {
				mAnimationEnd = false;
				moveXdistance = moveXdistance - scrollXDelta;
				onMoveImage(moveX - scrollXDelta, moveY);
			}

			@Override
			public void onAnimationEnd(Animator animation) {
				mAnimationEnd = true;
			}
		});
		resultSet.start();
	}

	private void animateMove(int index, boolean isForward) {
		List<Animator> resultList = new LinkedList<Animator>();
		if (isForward) {
			for (int i = index + 1; i < childrens.size(); i++) {
				View view = childrens.get(i);
				resultList.add(createTranslationAnimations(view, -view.getWidth(), 0, 0, 0));
			}
		} else {
			for (int i = index; i < childrens.size(); i++) {
				View view = childrens.get(i);
				resultList.add(createTranslationAnimations(view, view.getWidth(), 0, 0, 0));
			}
		}

		AnimatorSet resultSet = new AnimatorSet();
		resultSet.playTogether(resultList);
		if (scrollXDelta == 0)
			resultSet.setDuration(ANIMATION_TIME);
		else
			resultSet.setDuration(200);
		resultSet.setInterpolator(new AccelerateDecelerateInterpolator());
		resultSet.addListener(new AnimatorListenerAdapter() {
			@Override
			public void onAnimationStart(Animator animation) {
				mAnimationEnd = false;
			}

			@Override
			public void onAnimationEnd(Animator animation) {
				mAnimationEnd = true;
			}
		});
		resultSet.start();
	}

	/**
	 * 创建移动动画
	 * 
	 * @param view
	 * @param startX
	 * @param endX
	 * @param startY
	 * @param endY
	 * @return
	 */
	private AnimatorSet createTranslationAnimations(View view, float startX, float endX, float startY, float endY) {
		ObjectAnimator animX = ObjectAnimator.ofFloat(view, "translationX", startX, endX);
		ObjectAnimator animY = ObjectAnimator.ofFloat(view, "translationY", startY, endY);
		AnimatorSet animSetXY = new AnimatorSet();
		animSetXY.playTogether(animX, animY);
		return animSetXY;
	}

}


 主要思路是开始排序动画的时候,先把原来位置的隐藏,生成一个假的镜像。

猜你喜欢

转载自blog.csdn.net/u012591964/article/details/70843585
今日推荐