Android仿百度福袋紅包界面
首先來(lái)看一下效果圖:
1.編程思路
看看界面,不難發(fā)現(xiàn),其就是一個(gè)放入九張圖片的容器,繪制其實(shí)可以在其上面另創(chuàng)建一個(gè)透明View負(fù)責(zé)繪制線與圓圈。下面我們將介紹一下實(shí)現(xiàn)過(guò)程。
㈠自定義ViewGroup
我們知道,自定義ViewGroup一定需要實(shí)現(xiàn)其onLayout()方法。該方法是設(shè)置子View位置與尺寸的時(shí)候調(diào)用。還有一個(gè)onMeasure()方法,該方法是測(cè)量view及其內(nèi)容來(lái)確定view的寬度和高度。
㈡存儲(chǔ)其點(diǎn)與圓的位置及繪制參數(shù)
當(dāng)重回界面的時(shí)候,是不會(huì)保存上一次繪制界面的內(nèi)容,必須存儲(chǔ)以備重繪時(shí)候繪制到界面
㈢簡(jiǎn)單的縮放動(dòng)畫(huà)
㈣自定義View實(shí)現(xiàn)繪制界面
㈤繪制完成時(shí),清除界面繪制內(nèi)容,并且保證不連接重復(fù)圖片
下面我們將完成這些步驟。
2.自定義ViewGroup
開(kāi)始的任務(wù)就是將九張圖片平均分布到圖片的位置,顯示在手機(jī)界面中。其代碼如下:
public class LYJViewGroup extends ViewGroup implements LYJGestureDrawline.OnAnimationCallback{ /** * 每個(gè)點(diǎn)區(qū)域的寬度 */ private int childWidth; /*** * 上下文 */ private Context context; /*** * 保存圖片點(diǎn)的位置 */ private List<LYJGesturePoint> list; /*** * 創(chuàng)建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(); // 初始化一個(gè)可以畫(huà)線的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); // 遍歷設(shè)置每個(gè)子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; // 定義點(diǎn)的左上角與右下角的坐標(biāo) 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.自定義點(diǎn)類(lèi)
顧名思義,就是為了獲取點(diǎn)的相關(guān)的屬性,其中基礎(chǔ)屬性圖片左上角坐標(biāo)與右下角坐標(biāo),計(jì)算圖片中心位置以便獲取圖片中心點(diǎn)。狀態(tài)標(biāo)記,表示該點(diǎn)是否繪制到圖片。下面是其實(shí)體類(lèi):
public class LYJGesturePoint { private Point pointLeftTop;//左上角坐標(biāo) private Point pointRightBottom;//右下角坐標(biāo) private int centerX;//圖片中心點(diǎn)X坐標(biāo) private int centerY;//圖片中心點(diǎn)Y坐標(biāo) private int pointState;//是否點(diǎn)擊了該圖片 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.自定義圓類(lèi)
這個(gè)類(lèi)較簡(jiǎn)單就三個(gè)屬性而已(圓中心點(diǎn)坐標(biāo)及半徑),代碼如下:
public class LYJCirclePoint { private int roundX;//圓中心點(diǎn)X坐標(biāo) private int roundY;//圓中心點(diǎn)Y坐標(biāo) 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.實(shí)現(xiàn)自定義繪制類(lèi)View
代碼如下:
public class LYJGestureView extends android.view.View { /*** * 聲明直線畫(huà)筆 */ private Paint paint; /*** * 聲明圓圈畫(huà)筆 */ private Paint circlePaint; /*** * 畫(huà)布 */ private Canvas canvas; /*** * 位圖 */ private Bitmap bitmap; /*** * 裝有各個(gè)view坐標(biāo)的集合,用于判斷點(diǎn)是否在其中 */ private List<LYJGesturePoint> list; /*** * 記錄畫(huà)過(guò)的線 */ private List<Pair<LYJGesturePoint, LYJGesturePoint>> lineList; /*** * 記錄畫(huà)過(guò)的圓 */ private List<LYJCirclePoint> circlePoints; /** * 手指當(dāng)前在哪個(gè)Point內(nèi) */ private LYJGesturePoint currentPoint; /*** * 手指按下動(dòng)畫(huà) */ 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);// 創(chuàng)建一個(gè)畫(huà)筆 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); // 設(shè)置位圖的寬高 canvas = new Canvas(); canvas.setBitmap(bitmap); paint.setStyle(Paint.Style.STROKE);// 設(shè)置非填充 paint.setStrokeWidth(20);// 筆寬20像素 paint.setColor(Color.rgb(245, 142, 33));// 設(shè)置默認(rèn)連線顏色 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: // 判斷當(dāng)前點(diǎn)擊的位置是處于哪個(gè)點(diǎn)之內(nèi) 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(); // 得到當(dāng)前移動(dòng)位置是處于哪個(gè)點(diǎn)內(nèi) LYJGesturePoint pointAt = getPointAt((int) event.getX(), (int) event.getY()); if (currentPoint == null && pointAt == null) {//你把手指按在屏幕滑動(dòng),如果終點(diǎn)與起點(diǎn)都不圖片那么返回 return true; } else {// 代表用戶的手指移動(dòng)到了點(diǎn)上 if (currentPoint == null) {// 先判斷當(dāng)前的point是不是為null // 如果為空,那么把手指移動(dòng)到的點(diǎn)賦值給currentPoint currentPoint = pointAt; // 把currentPoint這個(gè)點(diǎn)設(shè)置選中狀態(tài); currentPoint.setPointState(Constants.POINT_STATE_SELECTED); } } //如果移動(dòng)到的點(diǎn)不為圖片區(qū)域或者移動(dòng)到自己的地方,或者該圖片已經(jīng)為選中狀態(tài),直接畫(huà)直線就可以了 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{//其他情況畫(huà)兩點(diǎn)相連直線,并且保存繪制圓與直線,并調(diào)用按下圖片的縮放動(dòng)畫(huà) 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;//設(shè)置選中點(diǎn)為當(dāng)前點(diǎn)。 } invalidate();//重繪 break; case MotionEvent.ACTION_UP: clearScreenAndDrawList();//防止多出一條沒(méi)有終點(diǎn)的直線 new Handler().postDelayed(new clearLineRunnable(), 1000);//1秒后清空繪制界面 invalidate();//重繪 break; default: break; } return true; } class clearLineRunnable implements Runnable { public void run() { // 清空保存點(diǎn)與圓的集合 lineList.clear(); circlePoints.clear(); // 重新繪制界面 clearScreenAndDrawList(); for (LYJGesturePoint p : list) { //設(shè)置其為初始化不選中狀態(tài) p.setPointState(Constants.POINT_STATE_NORMAL); } invalidate(); } } /** * 通過(guò)點(diǎn)的位置去集合里面查找這個(gè)點(diǎn)是包含在哪個(gè)Point里面的 * * @param x * @param y * @return 如果沒(méi)有找到,則返回null,代表用戶當(dāng)前移動(dòng)的地方屬于點(diǎn)與點(diǎn)之間 */ private LYJGesturePoint getPointAt(int x, int y) { for (LYJGesturePoint point : list) { // 先判斷點(diǎn)是否在圖片的X坐標(biāo)內(nèi) int leftX = point.getPointLeftTop().x; int rightX = point.getPointRightBottom().x; if (!(x >= leftX && x < rightX)) { // 如果為假,則跳到下一個(gè)對(duì)比 continue; } //在判斷點(diǎn)是否在圖片的Y坐標(biāo)內(nèi) int topY = point.getPointLeftTop().y; int bottomY = point.getPointRightBottom().y; if (!(y >= topY && y < bottomY)) { // 如果為假,則跳到下一個(gè)對(duì)比 continue; } // 如果執(zhí)行到這,那么說(shuō)明當(dāng)前點(diǎn)擊的點(diǎn)的位置在遍歷到點(diǎn)的位置這個(gè)地方 return point; } return null; } /** * 清掉屏幕上所有的線,然后畫(huà)出集合里面的線 */ 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);// 畫(huà)線 } for(LYJCirclePoint lyjCirclePoint : circlePoints){ canvas.drawCircle(lyjCirclePoint.getRoundX(),lyjCirclePoint.getRoundY(), lyjCirclePoint.getRadiu(),circlePaint); } } //繪制用bitmap創(chuàng)建出來(lái)的畫(huà)布 @Override protected void onDraw(Canvas canvas) { canvas.drawBitmap(bitmap, 0, 0, null); } }
這樣就可以得到如下界面效果(當(dāng)然反編譯百度錢(qián)包,并沒(méi)有百度錢(qián)包中的圖片,只好隨便找了一張圖片):
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家學(xué)習(xí)Android有所幫助。
- Java實(shí)現(xiàn)微信紅包分配規(guī)則
- 微信java開(kāi)發(fā)之實(shí)現(xiàn)微信主動(dòng)推送消息
- java開(kāi)發(fā)微信公眾號(hào)支付
- java實(shí)現(xiàn)微信公眾平臺(tái)自定義菜單的創(chuàng)建示例
- 微信公眾平臺(tái)開(kāi)發(fā)實(shí)戰(zhàn)Java版之微信獲取用戶基本信息
- java微信開(kāi)發(fā)API第一步 服務(wù)器接入
- Android反編譯看看手Q口令紅包的實(shí)現(xiàn)原理
- Android實(shí)現(xiàn)QQ搶紅包插件
- 分享Android微信紅包插件
- 微信隨機(jī)生成紅包金額算法java版
相關(guān)文章
android6.0運(yùn)行時(shí)權(quán)限完美封裝方法
今天小編就為大家分享一篇android6.0運(yùn)行時(shí)權(quán)限完美封裝方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07Android無(wú)限循環(huán)RecyclerView的完美實(shí)現(xiàn)方案
這篇文章主要介紹了Android無(wú)限循環(huán)RecyclerView的完美實(shí)現(xiàn)方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06Android Studio導(dǎo)入jar包過(guò)程詳解
這篇文章主要介紹了Android Studio導(dǎo)入jar包過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11Android使用TouchDelegate增加View的觸摸范圍
這篇文章主要為大家詳細(xì)介紹了Android使用TouchDelegate增加View的觸摸范圍,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05Android游戲開(kāi)發(fā) 自定義手勢(shì)--輸入法手勢(shì)技術(shù)
本文主要介紹 Android游戲開(kāi)發(fā)中自定義手勢(shì)--輸入法手勢(shì)技術(shù),這里提供了實(shí)現(xiàn)效果圖以及示例代碼,有開(kāi)發(fā)手機(jī)游戲的朋友可以參考下2016-08-08Android用PopupWindow實(shí)現(xiàn)自定義overflow
這篇文章主要介紹了Android用PopupWindow實(shí)現(xiàn)自定義overflow的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11Android中ImageView.src設(shè)置圖片拉伸、填滿控件的方法
最近公司有個(gè)需求,要展示客戶公司的企業(yè)形象,用一張圖片放在ImageView中實(shí)現(xiàn),但是發(fā)現(xiàn)圖片并沒(méi)有填滿,而是在上下邊上留出了一點(diǎn)空白,下面這篇文章主要跟大家介紹了Android中ImageView.src設(shè)置圖片拉伸、填滿控件的方法,需要的朋友可以參考下。2017-06-06詳解Android應(yīng)用中DialogFragment的基本用法
Android App中建議使用DialogFragment作為對(duì)話框的容器,DialogFragment類(lèi)提供了創(chuàng)建對(duì)話框并管理其外觀需要的所有控件,本文主要內(nèi)容便為詳解Android應(yīng)用中DialogFragment的基本用法,而不再需要調(diào)用Dialog的方法需要的朋友可以參考下2016-05-05