转发的时候请标记转自:http://blog.csdn.net/zhanwei_30/article/details/7624025
参加工作到现在一年了,天天看别人的博客,自己也开始写一篇博客,对前段时间的项目简单的总结一下大概思路是:
1、多次观察4.0的锁屏 开锁界面 细节 需要几张图片图片的大小 处理等 先做出静态的界面---自定义自己想要的布局
public class Circle_View extends FrameLayout {
private Context mContext;
private ImageView lock_circle, lockView, anchorView, outsideView;
private int wps = ViewGroup.LayoutParams.FILL_PARENT;
// 手指离开时判断小圆是否在大圆内
private boolean IsoutCircle = false;
// 记录下 anchorView的起始位置 当手指离开时在圆内的时候 还回到原来的位置
private int anchorView_left;
private int anchorView_top;
private int anchorView_right;
private int anchorView_bottom;
// 上临界点的y坐标
int critical_top = 0;
// 下临界点的y坐标
int critical_bottom = 0;
private Vibrator mVibrator;
private Rect mRect;
private boolean mTracking;
private boolean mTriggered = false;
public Circle_View(Context context) {
super(context);
mContext = context;
setUp();
}
public Circle_View(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
setUp();
}
public Circle_View(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
}
@Override
protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
int left = getMeasuredWidth() / 2 - child.getMeasuredWidth() / 2;
int right = left + child.getMeasuredWidth();
int top = getMeasuredHeight() / 2 - child.getMeasuredHeight() / 2;
int bottom = top + child.getMeasuredHeight();
child.layout(left, top, right, bottom);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
getChildAt(i).measure(width, height);
}
}
private void setUp() {
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
mRect = new Rect();
outsideView = new ImageView(mContext);
Drawable out = getResources().getDrawable(R.drawable.outside);
outsideView.setImageBitmap(getSmallBitmap(out));
addView(outsideView, 0);
ImageView insideView = new ImageView(mContext);
Drawable in = getResources().getDrawable(R.drawable.inside);
insideView.setImageBitmap(getSmallBitmap(in));
addView(insideView, 1);
anchorView = new ImageView(mContext);
int width = 50;
int height = 50;
Bitmap anchor = Bitmap.createScaledBitmap(((BitmapDrawable) out).getBitmap(), width, height, true);
anchorView.setImageBitmap(anchor);
addView(anchorView, 2);
lockView = new ImageView(mContext);
Drawable lock = getResources().getDrawable(R.drawable.lock);
lockView.setImageDrawable(lock);
addView(lockView, 3);
lock_circle = new ImageView(mContext);
Drawable circle = getResources().getDrawable(R.drawable.lock_cricle);
lock_circle.setImageBitmap(getSmallBitmap(circle));
lock_circle.setLayoutParams(new LayoutParams(wps, wps));
addView(lock_circle, 4);
}
Bitmap getSmallBitmap(Drawable drawable) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
Bitmap newBitmap = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth() / 2, bitmap.getHeight() / 2, true);
return newBitmap;
}
2、拖动过程的动画实现 位置的判断---计算直线与圆的交点(注意这里要对斜率不存在的点处理的过程还需要注意精度)
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final int x = (int) ev.getX();
final int y = (int) ev.getY();
//判断手指的位置是否在解锁的小圆中
lock_circle.getHitRect(mRect);
boolean isValid = mRect.contains(x, y);
//当再次点击的时候 若锁环隐藏就让他马上显示在圆心
if (lock_circle.getVisibility() == 4) {
lockView.setVisibility(0);
lock_circle.layout(anchorView_left, anchorView_top, anchorView_right, anchorView_bottom);
lock_circle.setVisibility(0);
}
//对手指移动前的拦截判断
if (!mTracking && !isValid)
return false;
switch (action) {
case MotionEvent.ACTION_DOWN:
mTracking = true;
final View v = lock_circle;
lockView.setVisibility(View.INVISIBLE);
scale_big(action);
anchorView_left = v.getLeft();
anchorView_top = v.getTop();
anchorView_right = v.getRight();
anchorView_bottom = v.getBottom();
mVibrator.vibrate(50);
break;
}
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mTracking) {
final int action = event.getAction();
final View v = lock_circle;
final int width = v.getRight() - v.getLeft();
final int height = v.getBottom() - v.getTop();
final int radius_big = Math.round(outsideView.getMeasuredWidth() / 2.0f);
critical_top = outsideView.getTop();
critical_bottom = outsideView.getBottom();
// System.out.println("上临界点是" + critical_top + "<--->下临界点是" +
// critical_bottom);
final int radius_x = outsideView.getLeft() + radius_big;
final int radius_y = outsideView.getTop() + radius_big;
final int radius_small = Math.round(width / 2.0f);
switch (action) {
case MotionEvent.ACTION_MOVE:
int spaceX = 0;
int spaceY = 0;
// final int a=(0-(new Random().nextInt(10))+5);
final int x = (int) event.getX();
final int y = (int) event.getY();
final boolean flag = MathEx_circle.isCrossPoint(radius_x, radius_y, radius_big, x, y);
// 在圆内
if (!flag) {
spaceX = x;
spaceY = y;
} else {
// 判断触摸是否有效
/*
* boolean valideTouch_X_Y =
* MathEx_circle.isValideTouchXY(radius_x, radius_y,
* radius_big, x, y); if (!valideTouch_X_Y) return true;
*/
//在这里要取某个范围 这里的用的只都是int型 在float强转int的的时候 会舍去后边的小数 同事在计算率的时候精度也不够 java的精度可能不是太准确 临界点周围的值都会 //计算有很大的误差 包括在计算是否在圆内的时候 经过反复的实验 我感觉那个临界范围刚好是小圆的直径
if (x > radius_x-radius_small && x < radius_x+radius_small) {
spaceX = radius_x;
if (y <= critical_top) {
spaceY = critical_top;
}
else if (y >= critical_bottom) {
spaceY = critical_bottom;
}
if(spaceY==0)spaceY=critical_bottom;;
} else {
spaceX = MathEx_circle.calXBothOnLineAndCircle(radius_x, radius_y, radius_big, x, y);
spaceY = MathEx_circle.calYFromXOnLine(radius_x, radius_y, x, y, spaceX);
}
}
System.err.println("圆心坐标是" + "x=" + radius_x + "\t y=" + radius_y + "手指的位置是x=" + x + "\t y=" + y + "小圆的直径是 width=" + width + "\t height=" + height + "----measureWidth=" + v.getMeasuredWidth() + "\t getLeft=" + v.getLeft() + "\t getRight=" + v.getRight() + "\t gettop=" + v.getTop()
+ "\tgetBottom=" + v.getBottom() + "大圆的半径是" + radius_big);
System.err.println("spaceX=" + spaceX + "\t spaceY=" + spaceY);
if (spaceY<critical_top) {
spaceY=critical_top;
}
if (spaceY>critical_bottom) {
spaceY=critical_bottom;
}
int l = spaceX - radius_small;
int t = spaceY - radius_small;
int r = spaceX + radius_small;
int b = spaceY + radius_small;
v.layout(l, t, r, b);
break;
case MotionEvent.ACTION_UP:
//手指离开后的处理
final int rawX = (int) event.getX();
final int rawY = (int) event.getY();
IsoutCircle = MathEx_circle.isCrossPoint(radius_x, radius_y, radius_big, rawX, rawY);
scale_big(action);
break;
}
}
return mTracking || super.onTouchEvent(event);
}
private void scale_big(int action) {
final int anchor_width = anchorView.getDrawable().getIntrinsicWidth();
final int outCircle_width = outsideView.getDrawable().getIntrinsicWidth();
final float scale = (outCircle_width * 1.00f) / (anchor_width * 1.00f);
ScaleAnimation animation = new ScaleAnimation(1.00f, scale, 1.00f, scale, Animation.RELATIVE_TO_SELF, 0.50f, Animation.RELATIVE_TO_SELF, 0.50f);
animation.setFillAfter(true);
animation.setDuration(1000);
animation.setAnimationListener(new MyAnimationListener(action));
anchorView.startAnimation(animation);
}
class MyAnimationListener implements AnimationListener {
// 滑动的动作
int action;
boolean inside_circle;
public MyAnimationListener(int action) {
this.action = action;
}
@Override
public void onAnimationEnd(Animation animation) {
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
animation.setRepeatMode(Animation.RESTART);
animation.setRepeatCount(Animation.INFINITE);
break;
case MotionEvent.ACTION_UP:
outsideView.setVisibility(0);
lockView.setVisibility(0);
lock_circle.layout(anchorView_left, anchorView_top, anchorView_right, anchorView_bottom);
lock_circle.setVisibility(0);
anchorView.setVisibility(0);
anchorView.clearAnimation();
animation.cancel();
break;
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationStart(Animation animation) {
if (action == MotionEvent.ACTION_UP) {
if (!IsoutCircle) {
outsideView.setVisibility(4);
lock_circle.setVisibility(4);
anchorView.setVisibility(4);
}else {
//手指离开就结束动画 立刻执行解锁
anchorView.clearAnimation();
animation.cancel();
anchorView.setVisibility(4);
mVibrator.vibrate(100);
Toast.makeText(mContext, "在这里进行开锁", 3000).show();
}
}
3、上边类中的引用的MathEx_circle类是一个静态类封装了所有的处理直线与圆的关系方法
主要代码如下 注释很详细
/**
* 已知两点坐标求斜率(这边不考虑x1==x2的情况)
* @param x1 点1的x坐标
* @param y1 点2的y坐标
* @param x2 点3的x坐标
* @param y2 点4的y坐标
* @return
* @throws Exception
*/
public static double calSlope(int x1, int y1, int x2, int y2) {
return (double) (y2 - y1) / (double) (x2 - x1);
}
/**
* 已知斜率和直线上一点坐标,求截距
* @param x 点x坐标
* @param y 点y坐标
* @param k 斜率
* @return
*/
public static double calIntercept(int x, int y, double k) {
return y - k * x;
}
/**
* 已知x坐标、斜率k和截距长度b,使用y=kx+b公式求y坐标
* @param x
* @param k
* @param b
* @return
*/
public static int calYFromXOnLine(int x, double k, double b) {
return (int) (k * x + b);
}
/**
* 已知直线上两点坐标(x1,y1),(x2,y2)和第三点x坐标,求第三点y坐标
* @param x1 点1的x坐标
* @param y1 点1的y坐标
* @param x2 点2的x坐标
* @param y2 点2的y坐标
* @param x 第三点的x坐标
* @return
*/
public static int calYFromXOnLine(int x1, int y1, int x2, int y2, int x) {
double k = calSlope(x1, y1, x2, y2);
double b = calIntercept(x1, y1, k);
return calYFromXOnLine(x, k, b);
}
/**
* 求(x-cx)^2+(y-cy)^2=radius^2圆方程与y=kx+b直线方程的交点x坐标(此处省去两者是否有交点的逻辑判断)
* @param circleCx
* @param circleCy
* @param circleRadius
* @param k
* @param b
* @param y
* @return
*/
public static int[] calXBothOnLineAndCircle(int cx, int cy, int radius,
double k, double b) {
double paramA = k * k + 1;
double paramB = 2 * k * (b - cy) - 2 * cx;
double paramC = cx * cx + (b - cy) * (b - cy) - radius * radius;
int[] result = new int[2];
result[0] = (int) ((-paramB + Math.sqrt(paramB * paramB - 4 * paramA
* paramC)) / (2 * paramA));
result[1] = (int) ((-paramB - Math.sqrt(paramB * paramB - 4 * paramA
* paramC)) / (2 * paramA));
return result;
}
/**
* 已知圆心坐标和半径,有一点与圆心构成以圆心为端点的射线,计算射线与圆的交点x坐标
* @param cx
* @param cy
* @param radius
* @param x
* @param y
* @return
*/
public static int calXBothOnLineAndCircle(int cx, int cy, int radius,
int x, int y) {
double k = calSlope(cx, cy, x, y);
double b = calIntercept(x, y, k);
int[] tempResult = calXBothOnLineAndCircle(cx, cy, radius, k, b);
return x > cx ? Math.max(tempResult[0], tempResult[1]) : Math.min(
tempResult[0], tempResult[1]);
}
4、接下来说一下重点:怎么移植到源码中
把MathEx_circle放在frameworks/base/core/java/com/android/internal/widget/ 下和SildingTab在同一个目录下
然后把Circle_View中的代码移植到Silding中 该删除的删除 编译的过程有问题的地方自己修改就行了
5、在frameworks/base/core/res/res/***layout.xml(找到适合自己手机屏幕的布局界面) 找到keyguard_screen_tab_unlock_.xml keyguard_screen_tab_unlock_land.xml
把一些不要的删除掉(参照4.0的界面)只剩下时间 日期 闹钟 紧急电话 充电显示 把Sliding的布局的高度调高 位置在修改一下就行了