本文主要讲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; } }
<com.launcher.edit.HomeEditView android:id="@+id/gridview" android:visibility="gone" android:layout_width="fill_parent" android:layout_height="fill_parent" />
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); } };
// 页面编辑完成后,重新排列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; }
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); } }
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); }
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)、因为布局不平均自适应可能不行 等等。。。