Android仿百度福袋红包界面

首先来看一下效果图:

1.编程思路

看看界面,不难发现,其就是一个放入九张图片的容器,绘制其实可以在其上面另创建一个透明View负责绘制线与圆圈。下面我们将介绍一下实现过程。

㈠自定义ViewGroup

我们知道,自定义ViewGroup一定需要实现其onLayout()方法。该方法是设置子View位置与尺寸的时候调用。还有一个onMeasure()方法,该方法是测量view及其内容来确定view的宽度和高度。

㈡存储其点与圆的位置及绘制参数

当重回界面的时候,是不会保存上一次绘制界面的内容,必须存储以备重绘时候绘制到界面

㈢简单的缩放动画

㈣自定义View实现绘制界面

㈤绘制完成时,清除界面绘制内容,并且保证不连接重复图片

下面我们将完成这些步骤。

2.自定义ViewGroup

开始的任务就是将九张图片平均分布到图片的位置,显示在手机界面中。其代码如下:

public class LYJViewGroup extends ViewGroup implements LYJGestureDrawline.OnAnimationCallback{
  /**
   * 每个点区域的宽度
   */
  private int childWidth;
  /***
   * 上下文
   */
  private Context context;
  /***
   * 保存图片点的位置
   */
  private List<LYJGesturePoint> list;
  /***
   * 创建view使其在ViewGroup之上。
   */
  private LYJGestureView gestureDrawline;
  private int baseNum = 5;
  public LYJViewGroup(Context context) {
    super(context);
    this.context = context;
    this.list = new ArrayList<>();
    DisplayMetrics metric = new DisplayMetrics();
    ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metric);
    childWidth = metric.widthPixels / 3;   // 屏幕宽度(像素)
    addChild();
    // 初始化一个可以画线的view
    gestureDrawline = new LYJGestureView(context, list);
    gestureDrawline.setAnimationCallback(this);
  }
  public void setParentView(ViewGroup parent){
    // 得到屏幕的宽度
    DisplayMetrics metric = new DisplayMetrics();
    ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metric);
    int width = metric.widthPixels;
    LayoutParams layoutParams = new LayoutParams(width, width);
    this.setLayoutParams(layoutParams);
    gestureDrawline.setLayoutParams(layoutParams);
    parent.addView(this);
    parent.addView(gestureDrawline);
  }
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    for (int i = 0; i < getChildCount(); i++) {
      //第几行
      int rowspan = i / 3;
      //第几列
      int column = i % 3;
      android.view.View v = getChildAt(i);
      v.layout(column * childWidth + childWidth / baseNum, rowspan * childWidth + childWidth / baseNum,
          column * childWidth + childWidth - childWidth / baseNum, rowspan * childWidth + childWidth - childWidth / baseNum);
    }
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    // 遍历设置每个子view的大小
    for (int i = 0; i < getChildCount(); i++) {
      View v = getChildAt(i);
      v.measure(widthMeasureSpec, heightMeasureSpec);
    }
  }

  private void addChild() {
    for (int i = 0; i < 9; i++) {
      ImageView image = new ImageView(context);
      image.setBackgroundResource(R.drawable.marker);
      this.addView(image);
      invalidate();
      // 第几行
      int rowspan = i / 3;
      // 第几列
      int column = i % 3;
      // 定义点的左上角与右下角的坐标
      int leftX = column * childWidth + childWidth / baseNum;
      int topY = rowspan * childWidth + childWidth / baseNum;
      int rightX = column * childWidth + childWidth - childWidth / baseNum;
      int bottomY = rowspan * childWidth + childWidth - childWidth / baseNum;
      LYJGesturePoint p = new LYJGesturePoint(leftX, topY, rightX,bottomY,i);
      this.list.add(p);
    }
  }

  @Override
  public void startAnimationImage(int i) {
    Animation animation= AnimationUtils.loadAnimation(getContext(), R.anim.gridlayout_child_scale_anim);
    getChildAt(i).startAnimation(animation);
  }
}

3.自定义点类

顾名思义,就是为了获取点的相关的属性,其中基础属性图片左上角坐标与右下角坐标,计算图片中心位置以便获取图片中心点。状态标记,表示该点是否绘制到图片。下面是其实体类:

public class LYJGesturePoint {
  private Point pointLeftTop;//左上角坐标
  private Point pointRightBottom;//右下角坐标
  private int centerX;//图片中心点X坐标
  private int centerY;//图片中心点Y坐标
  private int pointState;//是否点击了该图片

  private int num;

  public int getNum() {
    return num;
  }

  public int getPointState() {
    return pointState;
  }

  public void setPointState(int pointState) {
    this.pointState = pointState;
  }

  public Point getPointLeftTop() {
    return pointLeftTop;
  }

  public Point getPointRightBottom() {
    return pointRightBottom;
  }

  public LYJGesturePoint(int left,int top,int right,int bottom,int i){
    this.pointLeftTop=new Point(left,top);
    this.pointRightBottom=new Point(right,bottom);
    this.num=i;
  }

  public int getCenterX() {
    this.centerX=(this.pointLeftTop.x+this.pointRightBottom.x)/2;
    return centerX;
  }

  public int getCenterY() {
    this.centerY=(this.pointLeftTop.y+this.pointRightBottom.y)/2;
    return centerY;
  }
}

4.自定义圆类

这个类较简单就三个属性而已(圆中心点坐标及半径),代码如下:

public class LYJCirclePoint {
  private int roundX;//圆中心点X坐标
  private int roundY;//圆中心点Y坐标
  private int radiu;//圆半径

  public int getRadiu() {
    return radiu;
  }

  public int getRoundX() {
    return roundX;
  }

  public int getRoundY() {
    return roundY;
  }

  public LYJCirclePoint(int roundX,int roundY,int radiu){
    this.roundX=roundX;
    this.roundY=roundY;
    this.radiu=radiu;
  }
}

5.实现自定义绘制类View

代码如下:

public class LYJGestureView extends android.view.View {
  /***
   * 声明直线画笔
   */
  private Paint paint;
  /***
   * 声明圆圈画笔
   */
  private Paint circlePaint;
  /***
   * 画布
   */
  private Canvas canvas;
  /***
   * 位图
   */
  private Bitmap bitmap;
  /***
   * 装有各个view坐标的集合,用于判断点是否在其中
   */
  private List<LYJGesturePoint> list;
  /***
   * 记录画过的线
   */
  private List<Pair<LYJGesturePoint, LYJGesturePoint>> lineList;
  /***
   * 记录画过的圆
   */
  private List<LYJCirclePoint> circlePoints;
  /**
   * 手指当前在哪个Point内
   */
  private LYJGesturePoint currentPoint;
  /***
   * 手指按下动画
   */
  private OnAnimationCallback animationCallback;
  public interface OnAnimationCallback{
    public void startAnimationImage(int i);
  }

  public void setAnimationCallback(OnAnimationCallback animationCallback) {
    this.animationCallback = animationCallback;
  }

  public LYJGestureView(Context context, List<LYJGesturePoint> list){
    super(context);
    Log.i(getClass().getName(), "GestureDrawline");
    paint = new Paint(Paint.DITHER_FLAG);// 创建一个画笔
    circlePaint=new Paint(Paint.DITHER_FLAG);
    DisplayMetrics metric = new DisplayMetrics();
    ((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(metric);
    Log.i(getClass().getName(), "widthPixels" + metric.widthPixels);
    Log.i(getClass().getName(), "heightPixels" + metric.heightPixels);
    bitmap = Bitmap.createBitmap(metric.widthPixels, metric.heightPixels, Bitmap.Config.ARGB_8888); // 设置位图的宽高
    canvas = new Canvas();
    canvas.setBitmap(bitmap);
    paint.setStyle(Paint.Style.STROKE);// 设置非填充
    paint.setStrokeWidth(20);// 笔宽20像素
    paint.setColor(Color.rgb(245, 142, 33));// 设置默认连线颜色
    paint.setAntiAlias(true);// 不显示锯齿
    circlePaint.setStyle(Paint.Style.FILL);
    circlePaint.setStrokeWidth(1);
    circlePaint.setAntiAlias(true);
    circlePaint.setColor(Color.rgb(245, 142, 33));

    this.list = list;
    this.lineList = new ArrayList<>();
    this.circlePoints=new ArrayList<>();
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()){
      case MotionEvent.ACTION_DOWN:
        // 判断当前点击的位置是处于哪个点之内
        currentPoint = getPointAt((int) event.getX(), (int) event.getY());
        if (currentPoint != null) {
          currentPoint.setPointState(Constants.POINT_STATE_SELECTED);
          this.animationCallback.startAnimationImage(currentPoint.getNum());
          canvas.drawCircle(currentPoint.getCenterX(), currentPoint.getCenterY(), 20, circlePaint);
          circlePoints.add(new LYJCirclePoint(currentPoint.getCenterX(),currentPoint.getCenterY(),20));
        }
        invalidate();
        break;
      case MotionEvent.ACTION_MOVE:
        clearScreenAndDrawList();
        // 得到当前移动位置是处于哪个点内
        LYJGesturePoint pointAt = getPointAt((int) event.getX(), (int) event.getY());
        if (currentPoint == null && pointAt == null) {//你把手指按在屏幕滑动,如果终点与起点都不图片那么返回
          return true;
        } else {// 代表用户的手指移动到了点上
          if (currentPoint == null) {// 先判断当前的point是不是为null
            // 如果为空,那么把手指移动到的点赋值给currentPoint
            currentPoint = pointAt;
            // 把currentPoint这个点设置选中状态;
            currentPoint.setPointState(Constants.POINT_STATE_SELECTED);
          }
        }
        //如果移动到的点不为图片区域或者移动到自己的地方,或者该图片已经为选中状态,直接画直线就可以了
        if(pointAt == null || currentPoint.equals(pointAt) || Constants.POINT_STATE_SELECTED == pointAt.getPointState()){
          canvas.drawCircle(currentPoint.getCenterX(), currentPoint.getCenterY(), 20, circlePaint);
          circlePoints.add(new LYJCirclePoint(currentPoint.getCenterX(), currentPoint.getCenterY(), 20));
          canvas.drawLine(currentPoint.getCenterX(), currentPoint.getCenterY(), event.getX(), event.getY(), paint);
        }else{//其他情况画两点相连直线,并且保存绘制圆与直线,并调用按下图片的缩放动画
          canvas.drawCircle(pointAt.getCenterX(),pointAt.getCenterY(),20,circlePaint);
          circlePoints.add(new LYJCirclePoint(pointAt.getCenterX(), pointAt.getCenterY(), 20));
          this.animationCallback.startAnimationImage(pointAt.getNum());
          pointAt.setPointState(Constants.POINT_STATE_SELECTED);
          canvas.drawLine(currentPoint.getCenterX(), currentPoint.getCenterY(), pointAt.getCenterX(), pointAt.getCenterY(), paint);
          Pair<LYJGesturePoint, LYJGesturePoint> pair = new Pair<>(currentPoint, pointAt);
          lineList.add(pair);
          currentPoint=pointAt;//设置选中点为当前点。
        }
        invalidate();//重绘
        break;
      case MotionEvent.ACTION_UP:
        clearScreenAndDrawList();//防止多出一条没有终点的直线
        new Handler().postDelayed(new clearLineRunnable(), 1000);//1秒后清空绘制界面
        invalidate();//重绘
        break;
      default:
        break;
    }
    return true;
  }

  class clearLineRunnable implements Runnable {
    public void run() {
      // 清空保存点与圆的集合
      lineList.clear();
      circlePoints.clear();
      // 重新绘制界面
      clearScreenAndDrawList();
      for (LYJGesturePoint p : list) {
        //设置其为初始化不选中状态
        p.setPointState(Constants.POINT_STATE_NORMAL);
      }
      invalidate();
    }
  }

  /**
   * 通过点的位置去集合里面查找这个点是包含在哪个Point里面的
   *
   * @param x
   * @param y
   * @return 如果没有找到,则返回null,代表用户当前移动的地方属于点与点之间
   */
  private LYJGesturePoint getPointAt(int x, int y) {

    for (LYJGesturePoint point : list) {
      // 先判断点是否在图片的X坐标内
      int leftX = point.getPointLeftTop().x;
      int rightX = point.getPointRightBottom().x;
      if (!(x >= leftX && x < rightX)) {
        // 如果为假,则跳到下一个对比
        continue;
      }
      //在判断点是否在图片的Y坐标内
      int topY = point.getPointLeftTop().y;
      int bottomY = point.getPointRightBottom().y;
      if (!(y >= topY && y < bottomY)) {
        // 如果为假,则跳到下一个对比
        continue;
      }

      // 如果执行到这,那么说明当前点击的点的位置在遍历到点的位置这个地方
      return point;
    }

    return null;
  }

  /**
   * 清掉屏幕上所有的线,然后画出集合里面的线
   */
  private void clearScreenAndDrawList() {
    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
    for (Pair<LYJGesturePoint, LYJGesturePoint> pair : lineList) {
      canvas.drawLine(pair.first.getCenterX(), pair.first.getCenterY(),
          pair.second.getCenterX(), pair.second.getCenterY(), paint);// 画线
    }
    for(LYJCirclePoint lyjCirclePoint : circlePoints){
      canvas.drawCircle(lyjCirclePoint.getRoundX(),lyjCirclePoint.getRoundY(), lyjCirclePoint.getRadiu(),circlePaint);
    }
  }
  //绘制用bitmap创建出来的画布
  @Override
  protected void onDraw(Canvas canvas) {
    canvas.drawBitmap(bitmap, 0, 0, null);
  }
}

这样就可以得到如下界面效果(当然反编译百度钱包,并没有百度钱包中的图片,只好随便找了一张图片):

以上就是本文的全部内容,希望对大家学习Android有所帮助。

(0)

相关推荐

  • 微信随机生成红包金额算法java版

    最近几年玩得最疯狂的应该是发红包了,尤其是过年的时候特别受欢迎,下面写了红包的随机算法,其实挺简单的,仅是提供一种思路,希望可以给大家一些启发. public class WxAlgorithm{ /** * @param moneySum 输入总金额 * @param redNum 输入红包数量 */ private static void wxAlgorithm(double moneySum, int redNum) { // 设置最小的金额 double moneyMin = 0.01;

  • 分享Android微信红包插件

    本文实例为大家分享了Android微信红包插件,供大家参考,具体内容如下 效果图: 具体代码 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void handleWindowChange(AccessibilityNodeInfo nodeInfo) { switch (Stage.getInstance().getCurrentStage()) { case Stage.OPENING_STAGE: // 调试信息,打印TTL // Lo

  • java实现微信公众平台自定义菜单的创建示例

    复制代码 代码如下: import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL; import org.json.JSONObject; public class MenuUtil { /**  * 获得ACC

  • 微信java开发之实现微信主动推送消息

    1.拉取access_token2.拉取用户信息3.主动推送消息4.接口貌似要申请权限5.依赖httpclient4.2.3 和jackson 2.2.1 复制代码 代码如下: public class WeixinAPIHelper { /**  * 获取token接口  */ private String getTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=

  • Android实现QQ抢红包插件

    又想到快要过年了,到时候还不知道群里要发好多红包,所以我将之前在网上宕的一份微信抢红包的代码修改了一下,实现了QQ抢红包!可以支持抢QQ拼手气红包,普通红包,口令红包,现在再也不怕20年单身手速的人跟我抢红包了! 先看测试效果图: 1.抢QQ口令红包  可以看见,只要红包一发出,自动填写口令并发出,帮你将红包抢到手! 2.抢QQ拼手气红包 拼手气红包也是一样,只要红包一发出,自动帮你把红包抢到手,是不是很爽的感觉? 3.抢QQ好友发送的红包 只要好友或者群里的人把红包一发出,就会第一时间让你抢到

  • Java实现仿微信红包分配规则

    最近过年发红包拜年成为一种新的潮流,作为程序猿对算法的好奇远远要大于对红包的好奇,这里介绍一种自己想到的一种随机红包分配策略,还请大家多多指教. 算法介绍 一.红包金额限制 对于微信红包,我们知道没人随机的最小红包是1分,最大金额是200元,这里我们同样来设置红包的范围,下面代码我们统一金钱的单位为分. //最小红包额度 private static final int MINMONEY = 1; //最大红包额度 private static final int MAXMONEY = 200

  • java开发微信公众号支付

    最近做了微信公众号支付的开发,由于是第一次做也摸索了几天的时间,也只是达到了实现功能的水平,并没有太多考虑到性能问题,所以这篇文章比较适合初学者. 微信公众号支付的总体其实很简单,大致就分为三步.第一步需要获取用户授权:第二步调用统一下单接口获取预支付id:第三步H5调起微信支付的内置的js.下面介绍具体每一步的开发流程. 一    首先要明确微信公众号支付属于网页版支付,所以相较于app的直接调取微信支付要多一步微信授权.也就是需要获取用户的openid.微信公众号使用的交易类型是JSAPI,

  • java微信开发API第一步 服务器接入

    微信开发API如何接入服务器,下面就为大家进行介绍 一.说明 * 本示例根据微信开发文档:http://mp.weixin.qq.com/wiki/home/index.html最新版(4/3/2016 5:34:36 PM )进行开发演示. * 编辑平台:myeclipse10.7+win32+jdk1.7+tomcat7.0  * 服务器:阿里云 windows server 2008 64bits * 平台要求:servlet使用注解方式,平台要求:j2ee6.0+.jdk6.0+.tom

  • Android反编译看看手Q口令红包的实现原理

    首篇作为开始,先讲讲简单的反编译.反编译通常有几种目的:互相学习.借来用用.嘿嘿(干你,又分为小干干类似微信红包,和大干干改别人的apk帮他上架). 因为没带kvm回来,mbpr屏幕太小,所以下文环境为windows. 一.反编译 让我们从实战开始,先实践一下怎么去反编译一个apk,看看某些功能的实现.毕竟没有实践的原理都是耍流氓. 这里我们保留互相学习的心态,所以是友善的第一种目的,嘻嘻. 1.准备 工具 Apktool jadx(新一代反编译大杀器) 安装包 手机QQ 6.2.3 (目标就设

  • 微信公众平台开发实战Java版之微信获取用户基本信息

    在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的.对于不同公众号,同一用户的openid不同). 公众号可通过本接口来根据OpenID获取用户基本信息,包括昵称.头像.性别.所在城市.语言和关注时间. 开发者可通过OpenID来获取用户基本信息.请使用https协议. 我们可以看看官方的文档:获取用户的基本信息. 接口调用请求说明 http请求方式: GET https://api.weixin.qq.com/cgi-b

随机推荐