New Year is coming, and red envelopes are endless. When I was grabbing red envelopes, I accidentally discovered that Baidu's lucky bag interface is quite good, so I took the time to write an article to complete the Baidu red envelope interface.
Of course, this is actually an evolutionary version of the unlock interface. However, it contains quite a lot of knowledge points. Write a blog post to record it and see what specific technical points are there. Check out Baidu's renderings:
1. Programming ideas
Looking at the interface, it is not difficult to find that it is a container that puts nine pictures. Drawing can actually create another transparent view on it and is responsible for drawing lines and circles. Next we will introduce the implementation process.
1. Custom ViewGroup
We know that custom ViewGroup must implement its onLayout() method. This method is called when setting the position and size of the subView. There is also an onMeasure() method, which measures the view and its contents to determine the width and height of the view.
㈡Storing the positions of its points and circles and drawing parameters
When returning to the interface, the content of the last drawing interface will not be saved. It must be stored in case of redrawing to draw to the interface
㈢Simple zoom animation
㈣Custom View implementation drawing interface
㈤When drawing is completed, clear the interface drawing content and ensure that duplicate pictures are not connected.
We will complete these steps below.
2. Customize ViewGroup
The initial task is to evenly distribute the nine pictures to the location of the pictures and display them in the mobile phone interface. The code is as follows:
public class LYJViewGroup extends ViewGroup implements LYJGestureDrawline.OnAnimationCallback{/*** Width of each point area*/private int childWidth;/**** Context*/private Context context;/**** Location of the image point*/private List<LYJGesturePoint> list;/**** Create a view to be above the ViewGroup. */private LYJGestureView gearDrawline;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; // Screen width (pixels) addChild();// Initialize a viewgestureDrawline that can draw lines = new LYJGestureView(context, list);gestureDrawline.setAnimationCallback(this);}public void setParentView(ViewGroup parent){// Get the width of the screen 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);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {for (int i = 0; i < getChildCount(); i++) {//What row int rowspan = i / 3;//What column 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);}}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);// traversal sets the size of each child 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();// Which row int rowspan = i / 3;// Which column int column = i % 3;// Define the coordinates of the upper left and lower right corners of the point 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);}}@Overridepublic void startAnimationImage(int i) {Animation animation= AnimationUtils.loadAnimation(getContext(), R.anim.gridlayout_child_scale_anim);getChildAt(i).startAnimation(animation);}}3. Customize point classes
As the name suggests, it is to obtain the relevant attributes of the point, among which the coordinates of the upper left corner of the basic attribute picture and the coordinates of the lower right corner of the picture, calculate the center position of the picture to obtain the center point of the picture. Status mark indicating whether the point is drawn to the picture. Here are the physical classes:
public class LYJGesturePoint {private Point pointLeftTop;//upper left corner coordinate private Point pointRightBottom;//lower right corner coordinate private int centerX;//image center point X coordinate private int centerY;//image center point Y coordinate private int pointState;//whether the image was clicked 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. Custom circle class
This class is simpler and has three properties (coordinates and radius of the center point of the circle). The code is as follows:
public class LYJCirclePoint {private int roundX;//Circle center point X coordinate private int roundY;//Circle center point Y coordinate private int radiu;//Circle radius 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. Implement custom drawing class View
The code is as follows:
public class LYJGestureView extends android.view.View {/**** Declare straight line brush*/private Paint paint;/**** Declare circle brush*/private Paint circlePaint;/**** Canvas*/private Canvas canvas;/**** Bitmap*/private Bitmap bitmap;/**** A collection of each view coordinates is used to determine whether the points are in it*/private List<LYJGesturePoint> list;/**** Record the drawn lines*/private List<Pair<LYJGesturePoint, LYJGesturePoint>> lineList;/**** Record the drawn circle*/private List<LYJCirclePoint> circlePoints;/*** Which Point is the finger currently in*/private LYJGesturePoint currentPoint;/**** Press the animation*/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);// Create a brush 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); // Set the width and height of the bitmap canvas = new Canvas();canvas.setBitmap(bitmap);paint.setStyle(Paint.Style.STROKE);// Set non-fill paint.setStrokeWidth(20);// Pen width 20 pixels paint.setColor(Color.rgb(245, 142, 33));// Set the default connection color paint.setAntiAlias(true);// Not displayed jagged 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<>();}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()){case MotionEvent.ACTION_DOWN:// determines which point the current click is in 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();// Get the point where the current moving position is LYJGesturePoint pointAt = getPointAt((int) event.getX(), (int) event.getY());if (currentPoint == null && pointAt == null) {//Press your finger on the screen to slide it. If the end point and the starting point are not the picture, return true;} else {// It means that the user's finger is moved to the point if (currentPoint == null) {// First determine whether the current point is null// If it is empty, then assign the point you moved to to currentPointcurrentPoint = pointAt;// Set the currentPoint point to select state; currentPoint.setPointState(Constants.POINT_STATE_SELECTED);}}//If the point you moved to is not the image area or move to your own place, or the image is already selected, just draw a straight line if(pointAt == null || currentPoint.equals(pointAt) || Constants.POINT_STATE_SELECTED == pointAt.getPointState()){canvas.drawCircle(currentPoint.getCenterX(), currentPoint.getCenterY(), 20, circlePaint);circlePoint.add(new LYJCirclePoint(currentPoint.getCenterX(), currentPoint.getCenterY(), 20));canvas.drawLine(currentPoint.getCenterX(), currentPoint.getCenterY(), event.getX(), event.getY(), paint);}else{//Draw two straight lines in other situations, save the drawing circle and the straight line, and call the zoom animation of the picture pressed 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;//Set the selected point to the current point. }invalidate();//Repaint break;case MotionEvent.ACTION_UP:clearScreenAndDrawList();//Prevent an extra line with no end point new Handler().postDelayed(new clearLineRunnable(), 1000);//Clear the drawing interface after 1 second invalidate();//Repaint break;default:break;}return true;}class clearLineRunnable implements Runnable {public void run() {// Clear the collection of save points and circles lineList.clear();circlePoints.clear();// Re-draw the interface clearScreenAndDrawList(); for (LYJGesturePoint p : list) {//Set it to initialize the unselected state p.setPointState(Constants.POINT_STATE_NORMAL);}invalidate();}}/*** Through the point position, go to the collection to find which Point this point is included in ** @param x* @param y* @return If not found, return null, which means that the user's current moving place belongs to the point between points*/private LYJGesturePoint getPointAt(int x, int y) {for (LYJGesturePoint point: list) {// First determine whether the point is in the X coordinate of the picture int leftX = point.getPointLeftTop().x;int rightX = point.getPointRightBottom().x;if (!(x >= leftX && x < rightX)) {// If it is false, jump to the next comparison continue;}// In the judgment whether the point is in the Y coordinate of the picture int topY = point.getPointLeftTop().y;int bottomY = point.getPointRightBottom().y;if (!(y >= topY && y < bottomY)) {// If false, skip to the next comparison continue;}// If this is performed, it means that the position of the currently clicked point is traversing to the point. Return point;}return null;}/*** Clear all the lines on the screen, and then draw the lines in the collection*/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);// Draw Line}for(LYJCirclePoint lyjCirclePoint : circlePoints){canvas.drawCircle(lyjCirclePoint.getRoundX(), lyjCirclePoint.getRoundY(), lyjCirclePoint.getRadiu(), circlePaint);}}// Draw the canvas created with bitmap @Overrideprotected void onDraw(Canvas canvas) {canvas.drawBitmap(bitmap, 0, 0, null);}}This way you can get the following interface effect (of course, decompiling Baidu Wallet, there is no picture in Baidu Wallet, so you have to find a random picture):