模仿android4.0的开锁界面

转发的时候请标记转自: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的布局的高度调高 位置在修改一下就行了

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值