android4.0 Launcher仿三星平板workspace页面编辑(即页面增减)

本文主要讲android4.0 Launcher添加页面编辑功能,样式同三星的平板。

一、已实现功能:

1、页面增减(最多8个页面、拖动删除、最后一个添加页面、有内容的页面不可删除)

2、自由拖动交换位置

3、页面位置布局(完全仿三星位置布局)、目前是固定大小的。。。为什么要仿它,纠结

布局图:



二、开始思路

1、自定义一个view,实现添加、自由拖动、删除等功能

2、开始页面编辑时,将已有的页面生成缩略图传入到自定义的view中显示

3、编辑完成后,将新的页面顺序传出来,对Workspace的CellLayout进行重新排序(调整顺序,生成新加的页面,删除删除了的页面)

4、更新数据库中CellLayout上面item的screen属性

5、将现有的页面数量定入到文件中或者数据库

6、启动launcher时,workspace中CellLayout的数量初始化通过读取已保存的页面数据

三、相关代码

1、自定义的页面编辑View-------HomeEditView.java

package com.launcher.edit;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LightingColorFilter;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.view.ViewGroup;
import android.widget.TextView;

import com.android.launcher.R;

/**
 * 页面编辑
 * 当前页面是否可删除用view的id判断,当id > 0时不可删除,否则是可以删除的,所以在传入view时,要根据是否可删除设置好id
 * 用tag标记页面原来的页面索引
 * @author wusj
 * 
 */
public class HomeEditView extends ViewGroup implements OnTouchListener,
		OnLongClickListener {
	private static final String TAG = "HomeEditView";
	private Context mContext;
	private List<View> mChilds;
	// view从一点移到另一点时的平移动画时长
	private static final int ANIMATION_DURATION = 250;

	// 小于等于4个时的宽度和高度
	private static final int SMALL_WIDTH = 300;
	private static final int SMALL_HEIGHT = 300;
	// 大于4个页面时,单个页面显示的宽度和高度
	private static final int BIG_WIDTH = 250;
	private static final int BIG_HEIGHT = 250;
	// 水平两页面之间的间隙
	private static final int PAGE_GAP_WIDTH = 5;
	// 竖直两页面之间的间隙
	private static final int PAGE_GAP_HEIGHT = 5;
	// 删除区域的高度
	private static final int DELETE_ZONE_HEIGHT = 40;

	int dragged = -1;
	// 当前是否在移动view
	private boolean movingView = false;

	private int dragLeft;
	private int dragTop;
	private TextView add;
	
	
	public interface OnClickPageListener {
		public void onClickPage(int index, List<PageInfo> pages);
	}
	
	private OnClickPageListener clickPage;
	public void setOnClickPageListener(OnClickPageListener listener) {
		this.clickPage = listener;
	}
	
	private OnClickListener onClickListener = new OnClickListener() {
		
		@Override
		public void onClick(View v) {
			Log.d(TAG, "onClick");
			if (v == add) {
				View view = createView(getChildCount() - 2);
				addView(view, getChildCount() - 2);
				// view.setOnClickListener(this);
				if (getChildCount() == 10) {
					Log.d(TAG, "已经达到最大页面数, 删除添加按钮");
					removeView(add);
				}
				
				requestLayout();
			} else {
				if (clickPage != null) {
					List<PageInfo> pages = new ArrayList<PageInfo>();
					for (int i = 0; i < getChildCount() - 1; i++) {
						View child = getChildAt(i);
						if (child == add) {
							continue;
						}
						
						PageInfo info = new PageInfo();
						info.originPage = (Integer) child.getTag();
//						Log.d(TAG, "tag : " + info.originPage);
						info.currentPage = i;
						pages.add(info);
					}
					
					clickPage.onClickPage(0, pages);
				}
			}
		}
	};

	// 前面是view后面是view所在的位置
	private SparseArray<Integer> newPositions = new SparseArray<Integer>();

	private int initX;
	private int initY;
	private int lastTouchX;
	private int lastTouchY;

	private int lastTarget = -1;
	private Point startPoint;

	private DeleteDropZoneView deleteZone;

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

	public HomeEditView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		this.mContext = context;
		setOnTouchListener(this);
		setOnLongClickListener(this);

	}

	public void addViews(List<View> views) {
		removeAllViews();
		
		for (int i = 0; i < views.size(); i++) {
			View view = views.get(i);
			addView(view);
			view.setTag(i);
			newPositions.put(i, i);
		}
		
		if (views.size() < 8) {
			addView(createAddView());
		}

		// 将删除区域放到最后面
		createDeleteZone();
	}

	private void printTag() {

		for (int i = 0; i < getChildCount(); i++) {
			View child = getChildAt(i);
			// Log.d(TAG, "Tag : " + child.getTag());
		}

		// Log.d(TAG, "-----------------------------");
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		int action = event.getAction();

		switch (action & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
			initX = (int) event.getX();
			initY = (int) event.getY();

			lastTouchX = (int) event.getX();
			lastTouchY = (int) event.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			eventMove(event);
			break;
		case MotionEvent.ACTION_UP:
			touchUp(event);
			break;

		default:
			break;
		}

		if (aViewIsDragged()) {
			return true;
		}

		return false;
	}

	private void touchUp(MotionEvent event) {
		if (!aViewIsDragged()) {
			if (onClickListener != null) {
				View clickedView = getChildAt(getTargetAtCoor((int) event.getX(), (int) event.getY()));
				Log.d(TAG, "clickedView : " + clickedView);
                if(clickedView != null)
                    onClickListener.onClick(clickedView);
			}
		} else {
			hideDeleteView();
			manageChildrenReordering();
			movingView = false;
			dragged = -1;
			lastTarget = -1;
			cancelAnimations();
		}
	}

	private void cancelAnimations() {
		for (int i = 0; i < getChildCount(); i++) {
			if (i != dragged) {
				View child = getChildAt(i);
				child.clearAnimation();
			}
		}
	}

	private void manageChildrenReordering() {
		boolean draggedDeleted = touchUpInDeleteZoneDrop(lastTouchX, lastTouchY);
		if (draggedDeleted) {
			View view = getChildAt(dragged);
			int id = view.getId();
			Log.d(TAG, "id : " + id);
			if (id > 0) {
				draggedDeleted = false;
			}
		}
		

		if (draggedDeleted) {
			animateDeleteDragged();
			reorderChildrenWhenDraggedIsDeleted();
		} else {
			reorderChildren();
		}
	}
	
	// 当有页面被删除时,重新排列view
	private void reorderChildrenWhenDraggedIsDeleted() {
		Integer newDraggedPosition = newPositions.get(dragged,dragged);

		List<View> children = cleanUnorderedChildren();
		addReorderedChildrenToParent(children);

//		tellAdapterDraggedIsDeleted(newDraggedPosition);
		removeViewAt(newDraggedPosition);
		if (add != null && add.getParent() != this) {
			addView(createAddView(), getChildCount() - 1);
		}

		requestLayout();
	}
	
	// 删除时的缩小动画
	private void animateDeleteDragged() {
		ScaleAnimation scale = new ScaleAnimation(1.4f, 0f, 1.4f, 0f);//, biggestChildWidth / 2 , biggestChildHeight / 2);
		scale.setDuration(200);
		scale.setFillAfter(true);
		scale.setFillEnabled(true);

		getChildAt(dragged).clearAnimation();
		getChildAt(dragged).startAnimation(scale);
	}

	private void reorderChildren() {
		List<View> children = cleanUnorderedChildren();
		addReorderedChildrenToParent(children);
		requestLayout();
	}

	private void removeItemChildren(List<View> children) {
		for (View child : children) {
			removeView(child);
		}
	}

	private List<View> cleanUnorderedChildren() {
		List<View> children = saveChildren();
		removeItemChildren(children);
		return children;
	}

	private void addReorderedChildrenToParent(List<View> children) {
		List<View> reorderedViews = reeorderView(children);
		newPositions.clear();

		for (View view : reorderedViews) {
			if (view != null)
				addView(view);
		}
	}

	// 重新排列view
	private List<View> reeorderView(List<View> children) {
		View[] views = new View[children.size()];

		for (int i = 0; i < children.size(); i++) {
			int position = newPositions.get(i, -1);
			if (position != -1) {
				views[position] = children.get(i);
			} else {
				views[i] = children.get(i);
			}
		}

		return new ArrayList<View>(Arrays.asList(views));
	}

	// 将view保存返回
	private List<View> saveChildren() {
		List<View> children = new ArrayList<View>();
		for (int i = 0; i < getChildCount(); i++) {
			View child = getChildAt(i);
			child.clearAnimation();
			children.add(child);
		}
		return children;
	}

	private void eventMove(MotionEvent event) {
		if (movingView && aViewIsDragged()) {
			lastTouchX = (int) event.getX();
			lastTouchY = (int) event.getY();

			moveDraggedView(lastTouchX, lastTouchY);
			manageSwapPosition(lastTouchX, lastTouchY);
			manageDeleteZoneHover(lastTouchX, lastTouchY);
		}
	}

	// 移动时的位置交换管理
	private void manageSwapPosition(int x, int y) {
		int target = getTargetAtCoor(x, y);
		// Log.d(TAG, "target : " + target);
		if (target != -1 && target != lastTarget) {
			animateGap(target);
			lastTarget = target;
		}
	}

	// 通过后面的位置(value),得到前面的view(key)
	private int currentViewAtPosition(int targetLocation) {
		int viewAtPosition = targetLocation;
		for (int i = 0; i < newPositions.size(); i++) {
			int value = newPositions.valueAt(i);
			if (value == targetLocation) {
				viewAtPosition = newPositions.keyAt(i);
				break;
			}
		}

		return viewAtPosition;
	}

	private void animateGap(int target) {
		int viewAtPosition = currentViewAtPosition(target);
		if (getChildAt(viewAtPosition) == add) {
			return;
		}

		// 源始位置
		View origin = getChildAt(viewAtPosition);
		int originLeft = origin.getLeft();
		int originTop = origin.getTop();
		if (viewAtPosition == dragged) {
			originLeft = dragLeft;
			originTop = dragTop;
		}

		// 当前位置
		View curr = getChildAt(target);
		int currLeft = curr.getLeft();
		int currTop = curr.getTop();
		if (target == dragged) {
			currLeft = dragLeft;
			currTop = dragTop;
		}

		// 将要到达位置
		View tar = getChildAt(newPositions.get(dragged, dragged));
		int tarLeft = tar.getLeft();
		int tarTop = tar.getTop();
		if (newPositions.get(dragged, dragged) == dragged) {
			tarLeft = dragLeft;
			tarTop = dragTop;
		}

		Point oldOffset = new Point();
		oldOffset.x = currLeft - originLeft;
		oldOffset.y = currTop - originTop;

		Point newOffset = new Point();
		newOffset.x = tarLeft - originLeft;
		newOffset.y = tarTop - originTop;

		animateMoveToNewPosition(getChildAt(viewAtPosition), oldOffset,
				newOffset);

		saveNewPosition(target, viewAtPosition);
	}

	private void saveNewPosition(int target, int viewInTarget) {
		newPositions.put(viewInTarget, newPositions.get(dragged, dragged));
		newPositions.put(dragged, target);

		
	}

	// 取得指定点上的view的索引
	private int getTargetAtCoor(int x, int y) {
		int ret = -1;
		// 减1说明:最后的deleteZone
		for (int i = 0; i < getChildCount() - 1; i++) {
			View child = getChildAt(i);
			if (child == getChildAt(dragged)) {
				// if (dragged != i)
				int count = getChildCount();
				if (count < 5) {
					if ((x > dragLeft && x < dragLeft + SMALL_WIDTH)
							&& (y > dragTop && y < dragTop + SMALL_HEIGHT)) {
						return i;
					}
				} else {
					if ((x > dragLeft && x < dragLeft + BIG_WIDTH)
							&& (y > dragTop && y < dragTop + BIG_HEIGHT)) {
						return i;
					}
				}

				continue;
			}

			if (isPointInsideView(x, y, child)) {
				return i;
			}
		}

		return ret;
	}

	// 移动被拖曳的view
	private void moveDraggedView(int x, int y) {
		View childAt = getChildAt(dragged);
		int width = childAt.getMeasuredWidth();
		int height = childAt.getMeasuredHeight();

		int l = x - (1 * width / 2);
		int t = y - (1 * height / 2);

		childAt.layout(l, t, l + width, t + height);
	}

	// 调整view的大小
	private void updateSize() {
		int count = getChildCount() - 1;
		if (count < 5) {
			for (int i = 0; i < count; i++) {
				View view = getChildAt(i);
				float wid = view.getWidth();
				float hei = view.getHeight();
				view.setScaleX(SMALL_WIDTH / wid);
				view.setScaleY(SMALL_HEIGHT / hei);
			}
		} else {
			for (int i = 0; i < count; i++) {
				View view = getChildAt(i);
				float wid = view.getWidth();
				float hei = view.getHeight();
				view.setScaleX(BIG_WIDTH / wid);
				view.setScaleY(BIG_HEIGHT / hei);
			}
		}
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		int count = getChildCount() - 1;
		// updateSize();
		Log.d(TAG, "count == " + count);
		if (count < 3) {
			int left = getWidth() / 2 - (SMALL_WIDTH * 2 + PAGE_GAP_WIDTH) / 2;
			for (int i = 0; i < count; i++) {
				View child = getChildAt(i);
				if (child.getVisibility() == View.GONE) {
					continue;
				}

				child.layout(left, DELETE_ZONE_HEIGHT, left + SMALL_WIDTH,
						SMALL_HEIGHT + DELETE_ZONE_HEIGHT);
				left += SMALL_WIDTH + PAGE_GAP_WIDTH;
			}
		} else if (count == 3) {
			int left = getWidth() / 2 - (SMALL_WIDTH * 2 + PAGE_GAP_WIDTH) / 2;
			for (int i = 0; i < count; i++) {
				View child = getChildAt(i);
				if (child.getVisibility() == View.GONE) {
					continue;
				}

				if (i == 2) {
					child.layout(getWidth() / 2 - SMALL_WIDTH / 2, SMALL_HEIGHT
							+ PAGE_GAP_HEIGHT + DELETE_ZONE_HEIGHT, getWidth()
							/ 2 - SMALL_WIDTH / 2 + SMALL_WIDTH, SMALL_HEIGHT
							* 2 + PAGE_GAP_HEIGHT + DELETE_ZONE_HEIGHT);
				} else {
					child.layout(left, DELETE_ZONE_HEIGHT, left + SMALL_WIDTH,
							SMALL_HEIGHT + DELETE_ZONE_HEIGHT);
					left += SMALL_WIDTH + PAGE_GAP_WIDTH;
				}

			}
		} else if (count == 4) {
			int left = getWidth() / 2 - (SMALL_WIDTH * 2 + PAGE_GAP_WIDTH) / 2;
			int left2 = getWidth() / 2 - (SMALL_WIDTH * 2 + PAGE_GAP_WIDTH) / 2;
			for (int i = 0; i < count; i++) {
				View child = getChildAt(i);
				if (child.getVisibility() == View.GONE) {
					continue;
				}

				if (i >= 2) {
					child.layout(left2, SMALL_HEIGHT + PAGE_GAP_HEIGHT
							+ DELETE_ZONE_HEIGHT, left2 + SMALL_WIDTH,
							SMALL_HEIGHT * 2 + PAGE_GAP_HEIGHT
									+ DELETE_ZONE_HEIGHT);
					left2 += SMALL_WIDTH + PAGE_GAP_WIDTH;
				} else {
					child.layout(left, DELETE_ZONE_HEIGHT, left + SMALL_WIDTH,
							SMALL_HEIGHT + DELETE_ZONE_HEIGHT);
					left += SMALL_WIDTH + PAGE_GAP_WIDTH;
				}

			}
		} else if (count == 5) {
			int left1 = getWidth() / 2 - (BIG_WIDTH * 2 + PAGE_GAP_WIDTH) / 2;
			int left2 = getWidth() / 2 - (BIG_WIDTH / 2);
			int left3 = getWidth() / 2 - (BIG_WIDTH * 2 + PAGE_GAP_WIDTH) / 2;

			for (int i = 0; i < count; i++) {
				View child = getChildAt(i);
				if (child.getVisibility() == View.GONE) {
					continue;
				}

				if (i < 2) {
					child.layout(left1, DELETE_ZONE_HEIGHT, left1 + BIG_WIDTH,
							BIG_HEIGHT + DELETE_ZONE_HEIGHT);
					left1 += BIG_WIDTH + PAGE_GAP_WIDTH;
				} else if (i == 2) {
					child.layout(left2, BIG_HEIGHT + PAGE_GAP_HEIGHT
							+ DELETE_ZONE_HEIGHT, left2 + BIG_WIDTH, BIG_HEIGHT
							* 2 + PAGE_GAP_HEIGHT + DELETE_ZONE_HEIGHT);
				} else {
					child.layout(left3, BIG_HEIGHT * 2 + PAGE_GAP_HEIGHT * 2
							+ DELETE_ZONE_HEIGHT, left3 + BIG_WIDTH, BIG_HEIGHT
							* 2 + PAGE_GAP_HEIGHT * 2 + BIG_HEIGHT
							+ DELETE_ZONE_HEIGHT);
					left3 += BIG_WIDTH + PAGE_GAP_WIDTH;
				}

			}
		} else if (count == 6) {
			int left1 = getWidth() / 2 - (BIG_WIDTH * 2 + PAGE_GAP_WIDTH) / 2;
			int left2 = left1;
			int left3 = getWidth() / 2 - (BIG_WIDTH * 2 + PAGE_GAP_WIDTH) / 2;

			for (int i = 0; i < count; i++) {
				View child = getChildAt(i);
				if (child.getVisibility() == View.GONE) {
					continue;
				}

				if (i < 2) {
					child.layout(left1, DELETE_ZONE_HEIGHT, left1 + BIG_WIDTH,
							BIG_HEIGHT + DELETE_ZONE_HEIGHT);
					left1 += BIG_WIDTH + PAGE_GAP_WIDTH;
				} else if (i >= 2 && i < 4) {
					child.layout(left2, BIG_HEIGHT + PAGE_GAP_HEIGHT
							+ DELETE_ZONE_HEIGHT, left2 + BIG_WIDTH, BIG_HEIGHT
							* 2 + PAGE_GAP_HEIGHT + DELETE_ZONE_HEIGHT);
					left2 += BIG_WIDTH + PAGE_GAP_WIDTH;
				} else {
					child.layout(left3, BIG_HEIGHT * 2 + PAGE_GAP_HEIGHT * 2
							+ DELETE_ZONE_HEIGHT, left3 + BIG_WIDTH, BIG_HEIGHT
							* 2 + PAGE_GAP_HEIGHT * 2 + BIG_HEIGHT
							+ DELETE_ZONE_HEIGHT);
					left3 += BIG_WIDTH + PAGE_GAP_WIDTH;
				}

			}
		} else if (count == 7) {
			int left1 = getWidth() / 2 - (BIG_WIDTH * 2 + PAGE_GAP_WIDTH) / 2;
			int left2 = getWidth() / 2 - (BIG_WIDTH * 3 + 2 * PAGE_GAP_WIDTH)
					/ 2;
			int left3 = getWidth() / 2 - (BIG_WIDTH * 2 + PAGE_GAP_WIDTH) / 2;

			for (int i = 0; i < count; i++) {
				View child = getChildAt(i);
				if (child.getVisibility() == View.GONE) {
					continue;
				}

				if (i < 2) {
					child.layout(left1, DELETE_ZONE_HEIGHT, left1 + BIG_WIDTH,
							BIG_HEIGHT + DELETE_ZONE_HEIGHT);
					left1 += BIG_WIDTH + PAGE_GAP_WIDTH;
				} else if (i >= 2 && i < 5) {
					child.layout(left2, BIG_HEIGHT + PAGE_GAP_HEIGHT
							+ DELETE_ZONE_HEIGHT, left2 + BIG_WIDTH, BIG_HEIGHT
							* 2 + PAGE_GAP_HEIGHT + DELETE_ZONE_HEIGHT);
					left2 += BIG_WIDTH + PAGE_GAP_WIDTH;
				} else {
					child.layout(left3, BIG_HEIGHT * 2 + PAGE_GAP_HEIGHT * 2
							+ DELETE_ZONE_HEIGHT, left3 + BIG_WIDTH, BIG_HEIGHT
							* 2 + PAGE_GAP_HEIGHT * 2 + BIG_HEIGHT
							+ DELETE_ZONE_HEIGHT);
					left3 += BIG_WIDTH + PAGE_GAP_WIDTH;
				}

			}
		} else if (count == 8) {
			int left1 = getWidth() / 2 - (BIG_WIDTH * 3 + 2 * PAGE_GAP_WIDTH)
					/ 2;
			int left2 = getWidth() / 2 - (BIG_WIDTH * 2 + PAGE_GAP_WIDTH) / 2;
			int left3 = left1;

			for (int i = 0; i < count; i++) {
				View child = getChildAt(i);
				if (child.getVisibility() == View.GONE) {
					continue;
				}

				if (i < 3) {
					child.layout(left1, DELETE_ZONE_HEIGHT, left1 + BIG_WIDTH,
							BIG_HEIGHT + DELETE_ZONE_HEIGHT);
					left1 += BIG_WIDTH + PAGE_GAP_WIDTH;
				} else if (i >= 3 && i < 5) {
					child.layout(left2, BIG_HEIGHT + PAGE_GAP_HEIGHT
							+ DELETE_ZONE_HEIGHT, left2 + BIG_WIDTH, BIG_HEIGHT
							* 2 + PAGE_GAP_HEIGHT + DELETE_ZONE_HEIGHT);
					left2 += BIG_WIDTH + PAGE_GAP_WIDTH;
				} else {
					child.layout(left3, BIG_HEIGHT * 2 + PAGE_GAP_HEIGHT * 2
							+ DELETE_ZONE_HEIGHT, left3 + BIG_WIDTH, BIG_HEIGHT
							* 2 + PAGE_GAP_HEIGHT * 2 + BIG_HEIGHT
							+ DELETE_ZONE_HEIGHT);
					left3 += BIG_WIDTH + PAGE_GAP_WIDTH;
				}

			}
		}
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		Log.d(TAG, "onMeasure");
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		int count = getChildCount();
		
//		updateSize();

		if (count < 5) {
			for (int i = 0; i < count; i++) {
				View child = getChildAt(i);
				child.measure(MeasureSpec.makeMeasureSpec(SMALL_WIDTH,
						MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
						SMALL_HEIGHT, MeasureSpec.EXACTLY));
			}
		} else {
			for (int i = 0; i < count; i++) {
				View child = getChildAt(i);
				child.measure(MeasureSpec.makeMeasureSpec(BIG_WIDTH,
						MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
						BIG_HEIGHT, MeasureSpec.EXACTLY));
			}
		}

		measureChild(deleteZone, MeasureSpec.makeMeasureSpec(getWidth(),
				MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
				DELETE_ZONE_HEIGHT, MeasureSpec.EXACTLY));
	}

	private float getPixelFromDip(int size) {
		Resources r = getResources();
		float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size,
				r.getDisplayMetrics());
		return px;
	}

	@Override
	public boolean onLongClick(View v) {
		if ((dragged = positionForView()) != -1) {
			if (getChildAt(dragged) == add) {
				// 长按的是添加按钮则不可移动
				return true;
			}
			
			startPoint = new Point();
			// getLeft()和getTop()取相对父控件的距离
			dragLeft = (int) getChildAt(dragged).getLeft();
			dragTop = (int) getChildAt(dragged).getTop();

			movingView = true;
			animateMoveAllItems();
			animateDragged();
			popDeleteView();

			return true;
		}

		return false;
	}

	// 长按时,判断当前按下的是不是一个页面
	private int positionForView() {
		for (int i = 0; i < getChildCount(); i++) {
			View child = getChildAt(i);
			if (isPointInsideView(initX, initY, child)) {
				return i;
			}
		}

		return -1;
	}

	// 判断按下的点是不是在view上
	private boolean isPointInsideView(float x, float y, View view) {
		int viewX = view.getLeft();
		int viewY = view.getTop();

		if (pointIsInsideViewBounds(x, y, view, viewX, viewY)) {
			return true;
		} else {
			return false;
		}
	}

	private boolean pointIsInsideViewBounds(float x, float y, View view,
			int viewX, int viewY) {
		return (x > viewX && x < (viewX + view.getWidth()))
				&& (y > viewY && y < (viewY + view.getHeight()));
	}

	// 启动其它页面的跳动动画
	private void animateMoveAllItems() {
		Animation rotateAnimation = createFastRotateAnimation();

		for (int i = 0; i < getChildCount() - 1; i++) {
			View child = getChildAt(i);
			child.startAnimation(rotateAnimation);
		}
	}

	// 拖拽时其它页面的跳动动画
	private Animation createFastRotateAnimation() {
		Animation rotate = new RotateAnimation(-2.0f, 2.0f,
				Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
				0.5f);

		rotate.setRepeatMode(Animation.REVERSE);
		rotate.setRepeatCount(Animation.INFINITE);
		rotate.setDuration(60);
		rotate.setInterpolator(new AccelerateDecelerateInterpolator());

		return rotate;
	}

	// 给被拖曳的item加放大动画
	private void animateDragged() {
		ScaleAnimation scale = new ScaleAnimation(1f, 1.4f, 1f, 1.4f);// ,
																		// biggestChildWidth
																		// / 2 ,
																		// biggestChildHeight
																		// / 2);

		scale.setDuration(200);
		scale.setFillAfter(true);
		scale.setFillEnabled(true);

		if (aViewIsDragged()) {
			getChildAt(dragged).clearAnimation();
			getChildAt(dragged).startAnimation(scale);
		}
	}

	// old动画起始点相对view的距离,new动画终点相对view的距离
	private TranslateAnimation createTranslateAnimation(Point oldOffset,
			Point newOffset) {
		TranslateAnimation translate = new TranslateAnimation(
				Animation.ABSOLUTE, oldOffset.x, Animation.ABSOLUTE,
				newOffset.x, Animation.ABSOLUTE, oldOffset.y,
				Animation.ABSOLUTE, newOffset.y);
		translate.setDuration(ANIMATION_DURATION);
		translate.setFillEnabled(true);
		translate.setFillAfter(true);
		translate.setInterpolator(new AccelerateDecelerateInterpolator());
		return translate;
	}

	private void animateMoveToNewPosition(View targetView, Point oldOffset,
			Point newOffset) {
		AnimationSet set = new AnimationSet(true);

		Animation rotate = createFastRotateAnimation();
		Animation translate = createTranslateAnimation(oldOffset, newOffset);

		set.addAnimation(rotate);
		set.addAnimation(translate);

		targetView.clearAnimation();
		targetView.startAnimation(set);

	}

	private boolean aViewIsDragged() {
		return dragged != -1;
	}

	// 点击添加时、创建一个新view
	private View createView(int count) {
		TextView tv = new TextView(mContext);
		tv.setWidth(50);
		tv.setHeight(50);
		tv.setBackgroundColor(Color.CYAN);
		tv.setTag(-1);
		tv.setId(0);
		tv.setText("" + count);
		return tv;
	}

	// 显示删除区域
	private void popDeleteView() {

		deleteZone.setVisibility(View.VISIBLE);

		deleteZone.layout(0, 0, deleteZone.getMeasuredWidth(),
				deleteZone.getMeasuredHeight());
	}

	private void createDeleteZone() {
		deleteZone = new DeleteDropZoneView(getContext());
		addView(deleteZone);
		deleteZone.setVisibility(View.GONE);
	}
	
	// 创建显示在最后的添加view
	private TextView createAddView() {
		if (add != null) {
			return add;
		}
		
		add = new TextView(getContext());
		add.setBackgroundColor(Color.CYAN);
		add.setGravity(Gravity.CENTER);
		add.setText("+");
		return add;
	}

	private void hideDeleteView() {
		deleteZone.setVisibility(View.GONE);
	}

	// 拖动放开时,判断是否在删除区域中
	private boolean touchUpInDeleteZoneDrop(int x, int y) {
		Rect zone = new Rect();
		deleteZone.getHitRect(zone);

		if (zone.intersect(x, y, x + 1, y + 1)) {
			deleteZone.smother();
			return true;
		}
		return false;
	}

	// 移动到删除区域时,高亮显示
	private void manageDeleteZoneHover(int x, int y) {
		Rect zone = new Rect();
		deleteZone.getHitRect(zone);

		if (zone.intersect(x, y, x + 1, y + 1)) {
			deleteZone.highlight();
		} else {
			deleteZone.smother();
		}
	}
}

class DeleteDropZoneView extends View {

	private Paint textPaintStraight;
	private Paint textPaintRed;
	private Paint bitmapPaint;
	private Paint bitmapPaintRed;
	private boolean straight = true;

	private Bitmap trash;
	private Rect bounds;

	public DeleteDropZoneView(Context context) {
		super(context);

		bounds = new Rect();

		textPaintStraight = createTextPaint();
		textPaintStraight.setColor(Color.WHITE);

		textPaintRed = createTextPaint();
		textPaintRed.setColor(Color.RED);

		bitmapPaint = createBaseBitmapPaint();

		bitmapPaintRed = createBaseBitmapPaint();
		ColorFilter filter = new LightingColorFilter(Color.RED, 1);
		bitmapPaintRed.setColorFilter(filter);

		setBackgroundColor(Color.BLACK);
		getBackground().setAlpha(200);
	}

	private Paint createTextPaint() {
		Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		textPaint.setStyle(Style.FILL);
		textPaint.setTextAlign(Paint.Align.CENTER);
		textPaint.setTypeface(Typeface.DEFAULT_BOLD);
		return textPaint;
	}

	private Paint createBaseBitmapPaint() {
		Paint bitmapPaint = new Paint();
		bitmapPaint.setAntiAlias(true);
		bitmapPaint.setFilterBitmap(true);
		bitmapPaint.setDither(true);
		return bitmapPaint;
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);

		int measuredHeight = getMeasuredHeight();
		int measuredWidth = getMeasuredWidth();
		String removeString = "Remove";// getResources().getString(R.string.removeItem);

		initTrashIcon();

		textPaintStraight.getTextBounds(removeString, 0, 6, bounds);

		int proportion = 3 * measuredHeight / 4;
		if (straight) {
			textPaintStraight.setTextSize(proportion);
			canvas.drawText(removeString,
					(measuredWidth / 2) + (trash.getWidth() / 2) + 5,
					measuredHeight - ((measuredHeight - bounds.height()) / 2),
					textPaintStraight);
			canvas.drawBitmap(trash, (measuredWidth / 2) - (bounds.width() / 2)
					- (trash.getWidth() / 2) - 10, 0, bitmapPaint);
		} else {
			textPaintRed.setTextSize(proportion);
			canvas.drawText(removeString,
					(measuredWidth / 2) + (trash.getWidth() / 2) + 5,
					measuredHeight - ((measuredHeight - bounds.height()) / 2),
					textPaintRed);
			canvas.drawBitmap(trash, (measuredWidth / 2) - (bounds.width() / 2)
					- (trash.getWidth() / 2) - 10, 0, bitmapPaintRed);
		}
	}

	private void initTrashIcon() {
		if (trash == null) {
			trash = getImage(R.drawable.content_discard, getMeasuredHeight(),
					getMeasuredHeight());
		}
	}

	public void highlight() {
		straight = false;
		invalidate();
	}

	public void smother() {
		straight = true;
		invalidate();
	}

	private Bitmap getImage(int id, int width, int height) {
		Bitmap bmp = BitmapFactory.decodeResource(getResources(), id);
		Bitmap img = Bitmap.createScaledBitmap(bmp, width, height, true);
		bmp.recycle();
		invalidate();
		return img;
	}

}

2、launcher.xml中添加自定义的view,注意要Gone

<com.launcher.edit.HomeEditView
    android:id="@+id/gridview"
    android:visibility="gone"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />

3、在Launcher.java中添加开始编辑方法(触发条件自已加吧), mDragGrid我定义成了成员字段

    private void startEdit() {
    	Log.d(TAG, "*******onLongClick AllAppsButton**********");
		mDragGrid = (HomeEditView) findViewById(R.id.gridview);
		mDragGrid.setOnClickPageListener(listener);
		List<View> views = new ArrayList<View>();
		for (int i = 0; i < mWorkspace.getChildCount(); i++) {
			// 将当前已有的页面生成缩略图,放到HomeEditView中进行显示
			CellLayout view = (CellLayout) mWorkspace.getChildAt(i);
			ImageView image = new ImageView(this);
			Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
			//绘制  
            final Canvas c = new Canvas(bitmap);  
            //设置比例  
            view.dispatchDraw(c);   
            image.setImageBitmap(bitmap);  
			image.setBackgroundColor(Color.GRAY);
			// 通过id判断是否可以删除
			if (((ViewGroup) view.getChildAt(0)).getChildCount() != 0) {
				image.setId(1);
			} else {
				image.setId(0);
			}
			
			views.add(image);
		}
		
		mDragGrid.addViews(views);
		
		
		mDragGrid.setBackgroundColor(Color.RED);
		mDragGrid.setVisibility(View.VISIBLE);
		mWorkspace.setVisibility(View.INVISIBLE); 
		mHotseat.setVisibility(View.GONE);
		mSearchDropTargetBar.setVisibility(View.GONE);
		mDockDivider.setVisibility(View.GONE);
    }

    private OnClickPageListener listener = new OnClickPageListener() {

		@Override
		public void onClickPage(int index, List<PageInfo> pages) {
			Log.d(TAG, "onClickPage");
			mWorkspace.setVisibility(View.VISIBLE); 
			mHotseat.setVisibility(View.VISIBLE);
			mSearchDropTargetBar.setVisibility(View.VISIBLE);
			mDockDivider.setVisibility(View.VISIBLE);
			mDragGrid.setVisibility(View.GONE);
			
			mWorkspace.reorderChildren(pages);
		}
	};

4、Workspace.java中重新排列CellLayout代码

// 页面编辑完成后,重新排列CellLayout
    public void reorderChildren(List<PageInfo> pages) {
    	List<View> views = removeOldViews();
    	View[] newViews = new View[pages.size()];
    	
    	for (int i = 0; i < newViews.length; i++) {
    		PageInfo info = pages.get(i);
    		if (info.originPage != -1) {
    			newViews[i] = views.get(info.originPage);
    			
    			// 更新此页面上的item的screen信息
    			CellLayout cell = (CellLayout) newViews[i];
    			CellLayoutChildren chi = (CellLayoutChildren) cell.getChildAt(0);
    			for (int j = 0; j < chi.getChildCount(); j++) {
    				ItemInfo in = (ItemInfo) chi.getChildAt(j).getTag();
    				// 设定新的screen
    				in.screen = i;
    				LauncherModel.updateItemInDatabase(getContext(), in);
    			}
    			
    		} else {
    			newViews[i] = new CellLayout(mLauncher);
    		}
    	}
    	
    	for (int i = 0; i < newViews.length; i++) {
    		addView(newViews[i]);
    	}
    	
    	requestLayout();
    }
    
    private List<View> removeOldViews() {
    	List<View> list = new ArrayList<View>();
    	for (int i = 0; i < getChildCount(); i++) {
    		View view = getChildAt(i);
    		list.add(view);
    	}
    	
    	for (View view : list) {
    		removeView(view);
    	}
    	
    	return list;
    }

5、Workspace.java中初始化CellLayout代码, 此方法最好在构造方法中调initWorkspace()方法的前一行调用(没搞明白,一开始就调用的话会出问题)

private void initCells(Context context) {
    	SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
    	int count = sp.getInt("screen_count", 5);
    			
    	for (int i = 0; i < count; i++) {
    		CellLayout cl = new CellLayout(context);
        	this.addView(cl);
    	}
    }

6、CellLayout.java的单个参数的构造方法要改

public CellLayout(Context context) {
		super(context);
		
		// A ViewGroup usually does not draw, but CellLayout needs to draw a
		// rectangle to show
		// the user where a dragged item will land when dropped.
		setWillNotDraw(false);

		
		mOriginalCellWidth = mCellWidth = getResources().getDimensionPixelSize(
				R.dimen.workspace_cell_width);
		
		mOriginalCellHeight = mCellHeight = getResources()
				.getDimensionPixelSize(R.dimen.workspace_cell_height);
		
		mWidthGap = mOriginalWidthGap = getResources().getDimensionPixelSize(
				R.dimen.workspace_width_gap);
		mHeightGap = mOriginalHeightGap = getResources().getDimensionPixelSize(
				R.dimen.workspace_height_gap);

		mMaxGap = getResources().getDimensionPixelSize(
				R.dimen.workspace_max_gap);

		setPadding(
				getResources().getDimensionPixelSize(
						R.dimen.workspace_left_padding),
				getResources().getDimensionPixelSize(
						R.dimen.workspace_top_padding),
				getResources().getDimensionPixelSize(
						R.dimen.workspace_right_padding),
				getResources().getDimensionPixelSize(
						R.dimen.workspace_bottom_padding));

		mCountX = LauncherModel.getCellCountX();
		mCountY = LauncherModel.getCellCountY();
		mOccupied = new boolean[mCountX][mCountY];

		setAlwaysDrawnWithCacheEnabled(false);

		final Resources res = getResources();

		mNormalBackground = res
				.getDrawable(R.drawable.homescreen_blue_normal_holo);
		mActiveGlowBackground = res
				.getDrawable(R.drawable.homescreen_blue_strong_holo);

		mOverScrollLeft = res.getDrawable(R.drawable.overscroll_glow_left);
		mOverScrollRight = res.getDrawable(R.drawable.overscroll_glow_right);
		mForegroundPadding = res
				.getDimensionPixelSize(R.dimen.workspace_overscroll_drawable_padding);

		mNormalBackground.setFilterBitmap(true);
		mActiveGlowBackground.setFilterBitmap(true);

		// Initialize the data structures used for the drag visualization.

		mCrosshairsDrawable = res.getDrawable(R.drawable.gardening_crosshairs);
		mEaseOutInterpolator = new DecelerateInterpolator(2.5f); // Quint ease
																	// out

		// Set up the animation for fading the crosshairs in and out
		int animDuration = res
				.getInteger(R.integer.config_crosshairsFadeInTime);
		mCrosshairsAnimator = new InterruptibleInOutAnimator(animDuration,
				0.0f, 1.0f);
		mCrosshairsAnimator.getAnimator().addUpdateListener(
				new AnimatorUpdateListener() {
					public void onAnimationUpdate(ValueAnimator animation) {
						mCrosshairsVisibility = ((Float) animation
								.getAnimatedValue()).floatValue();
						invalidate();
					}
				});
		mCrosshairsAnimator.getAnimator().setInterpolator(mEaseOutInterpolator);

		mDragCell[0] = mDragCell[1] = -1;
		for (int i = 0; i < mDragOutlines.length; i++) {
			mDragOutlines[i] = new Point(-1, -1);
		}

		// When dragging things around the home screens, we show a green outline
		// of
		// where the item will land. The outlines gradually fade out, leaving a
		// trail
		// behind the drag path.
		// Set up all the animations that are used to implement this fading.
		final int duration = res
				.getInteger(R.integer.config_dragOutlineFadeTime);
		final float fromAlphaValue = 0;
		final float toAlphaValue = (float) res
				.getInteger(R.integer.config_dragOutlineMaxAlpha);

		Arrays.fill(mDragOutlineAlphas, fromAlphaValue);

		for (int i = 0; i < mDragOutlineAnims.length; i++) {
			final InterruptibleInOutAnimator anim = new InterruptibleInOutAnimator(
					duration, fromAlphaValue, toAlphaValue);
			anim.getAnimator().setInterpolator(mEaseOutInterpolator);
			final int thisIndex = i;
			anim.getAnimator().addUpdateListener(new AnimatorUpdateListener() {
				public void onAnimationUpdate(ValueAnimator animation) {
					final Bitmap outline = (Bitmap) anim.getTag();

					// If an animation is started and then stopped very quickly,
					// we can still
					// get spurious updates we've cleared the tag. Guard against
					// this.
					if (outline == null) {
						if (false) {
							Object val = animation.getAnimatedValue();
							Log.d(TAG, "anim " + thisIndex + " update: " + val
									+ ", isStopped " + anim.isStopped());
						}
						// Try to prevent it from continuing to run
						animation.cancel();
					} else {
						mDragOutlineAlphas[thisIndex] = (Float) animation
								.getAnimatedValue();
						final int left = mDragOutlines[thisIndex].x;
						final int top = mDragOutlines[thisIndex].y;
						CellLayout.this.invalidate(left, top,
								left + outline.getWidth(),
								top + outline.getHeight());
					}
				}
			});
			// The animation holds a reference to the drag outline bitmap as
			// long is it's
			// running. This way the bitmap can be GCed when the animations are
			// complete.
			anim.getAnimator().addListener(new AnimatorListenerAdapter() {
				@Override
				public void onAnimationEnd(Animator animation) {
					if ((Float) ((ValueAnimator) animation).getAnimatedValue() == 0f) {
						anim.setTag(null);
					}
				}
			});
			mDragOutlineAnims[i] = anim;
		}

		mBackgroundRect = new Rect();
		mForegroundRect = new Rect();

		mChildren = new CellLayoutChildren(context);
		mChildren.setCellDimensions(mCellWidth, mCellHeight, mWidthGap,
				mHeightGap);
		addView(mChildren);
		
		
		setBackgroundColor(Color.BLUE);
	}

7、PageInfo.java

public class PageInfo implements Comparable<PageInfo> {
	// 未编辑前是哪个页面
	public int originPage = -1;
	// 编辑完成后是哪个页面
	public int currentPage = -1;
	
	@Override
	public int compareTo(PageInfo another) {
		return currentPage - another.currentPage;
	}
}


最后来一张手机测的效果图





还有好多地方没有完善:1、设置主页       2、固定大小(可以改成固定dip)、因为布局不平均自适应可能不行  等等。。。





  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值