本文主要讲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)、因为布局不平均自适应可能不行 等等。。。