仿百度福袋红包界面

简介: 仿百度福袋红包界面

过年了,红包来袭,时间又是充裕,抢红包的时候意外发现了百度的福袋界面还不错,想想还要专门写一篇博文来完成其界面。


当然啦,这其实就是解锁界面的进化版本。不过其包含的知识点还是挺多的,写篇博文记录一下看看具体有哪些技术点啦。看看百度的效果图:

61.png


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);
    }
}


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


附上本文源码:


http://download.csdn.net/detail/liyuanjinglyj/9426567


62.png


相关文章
|
7月前
|
监控 JavaScript 安全
百度搜索:蓝易云【如何使用nginxWebUI图形化gui界面管理nginx】
虽然Nginx本身没有官方提供的图形化GUI界面,但使用第三方工具nginx-proxy-manager可以实现通过WebUI图形化界面来管理Nginx的反向代理规则。这样,你可以更方便地进行配置和管理,而无需手动修改Nginx配置文件。
112 0
|
定位技术 API 开发工具
百度地图开发-显示地图默认界面 03
百度地图开发-显示地图默认界面 03
293 1
百度地图开发-显示地图默认界面 03
HTML5-12(对百度界面的简单解析)
HTML5-12(对百度界面的简单解析)
362 0
HTML5-12(对百度界面的简单解析)
|
Web App开发 搜索推荐
Chrome 私人珍藏-stylus插件实现个性化百度界面定制
Chrome 私人珍藏-stylus插件实现个性化百度界面定制
346 0
Chrome 私人珍藏-stylus插件实现个性化百度界面定制
|
7月前
|
存储 Kubernetes 容器
百度搜索:蓝易云【Kubernetes使用helm部署NFS Provisioner】
现在,你已经成功使用Helm部署了NFS Provisioner,并且可以在Kubernetes中创建使用NFS存储的PersistentVolumeClaim。
207 10
|
7月前
百度搜索:蓝易云【什么是HTTP长轮询?】
现在,HTTP长轮询逐渐被WebSocket等更高效的实时通信技术所替代,但了解HTTP长轮询仍然有助于理解实时数据推送的基本原理。
130 9
|
7月前
|
移动开发 Shell Linux
百度搜索:蓝易云【Shell错误:/bin/bash^M: bad interpreter: No such file or directory】
将 `your_script.sh`替换为你的脚本文件名。运行此命令后,脚本文件的换行符将被转换为Linux格式,然后就可以在Linux系统上正常执行脚本了。
76 8
|
7月前
百度搜索:蓝易云【ipmitool配置BMC的ip】
以上操作将配置BMC的IP地址为新的值。请注意,操作BMC需要谨慎,确保你对服务器有足够的权限,并且仔细检查新的IP地址、子网掩码和默认网关,以免导致服务器网络失联。
104 7
|
7月前
|
Kubernetes 应用服务中间件 nginx
百度搜索:蓝易云【使用Kubernetes部署Nginx应用教程】
现在,你已经成功在Kubernetes集群上部署了Nginx应用。通过访问Service的外部IP地址,你可以访问Nginx服务。
87 4