模仿百度红包福袋界面实例代码

 更新时间:2016年02月03日 09:18:16   作者:Git_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);
}
}

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

相关文章

  • 解决Java提示正在尝试分配更低的访问权限问题

    解决Java提示正在尝试分配更低的访问权限问题

    在本篇文章里小编给大家整理的是关于解决Java提示正在尝试分配更低的访问权限问题的相关方法内容,有需要的朋友们跟着参考学习下。
    2019-07-07
  • Java线程编程中Thread类的基础学习教程

    Java线程编程中Thread类的基础学习教程

    这篇文章主要介绍了Java线程编程中Thread类的基础学习教程,Thread类包含诸多操作线程的方法,非常重要,需要的朋友可以参考下
    2015-12-12
  • springboot中如何通过cors协议解决跨域问题

    springboot中如何通过cors协议解决跨域问题

    这篇文章主要介绍了springboot中通过cors协议解决跨域问题,cors是一个w3c标准,它允许浏览器(目前ie8以下还不能被支持)像我们不同源的服务器发出xmlHttpRequest请求,我们可以继续使用ajax进行请求访问。具体内容详情大家跟随脚本之家小编一起学习吧
    2018-05-05
  • Spring Boot如何解决Mysql断连问题

    Spring Boot如何解决Mysql断连问题

    本篇文章主要介绍了Spring Boot如何解决Mysql断连问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • springmvc重定向实现方法解析

    springmvc重定向实现方法解析

    这篇文章主要介绍了springmvc重定向实现方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • 冒泡排序的原理及java代码实现

    冒泡排序的原理及java代码实现

    冒泡排序法:关键字较小的记录好比气泡逐趟上浮,关键字较大的记录好比石块下沉,每趟有一块最大的石块沉底。算法本质:(最大值是关键点,肯定放到最后了,如此循环)每次都从第一位向后滚动比较,使最大值沉底,最小值上升一次,最后一位向前推进
    2016-02-02
  • Java实现TopK问题的方法

    Java实现TopK问题的方法

    这篇文章主要介绍了Java实现TopK问题的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • 详解mybatis.generator配上最新的mysql 8.0.11的一些坑

    详解mybatis.generator配上最新的mysql 8.0.11的一些坑

    这篇文章主要介绍了详解mybatis.generator配上最新的mysql 8.0.11的一些坑,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-10-10
  • spring boot整合netty的实现方法

    spring boot整合netty的实现方法

    这篇文章主要介绍了spring boot整合netty的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • 解决springboot 获取form-data里的file文件的问题

    解决springboot 获取form-data里的file文件的问题

    这篇文章主要介绍了解决springboot 获取form-data里的file文件的问题的相关资料,这里提供了详细的解决步骤,需要的朋友可以参考下
    2017-07-07

最新评论