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

新年到新年到,红包抢不停。在我抢红包的时候意外的发现了百度的福袋界面挺不错的,于是抽时间专门写篇文章来完成百度红包界面吧。

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

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

}

}

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

以上是 模仿百度红包福袋界面实例代码 的全部内容, 来源链接: utcz.com/p/208698.html

回到顶部