利用自定义View实现扫雷游戏

游戏规则:

简单版的扫雷事实上就是一个9×9的矩阵,其中有十个点是雷,非雷方块的数字代表该方块周围八个方块中雷的个数。通过长按某一方块(方块会变红)认定该方块为玩家认为的雷,通过短按某一方块来“展开”该方块。

展开:如果该方块为雷,则游戏失败;如果该方块下为非零数字,则将该方块的数字告诉玩家;如果该方块下的数字为零,则展开该方块周围区域,直到展开到数字或者雷为止。

实现难点:

  1. 如何生成不重复的十个雷
  2. 如何生成非雷区域的数字
  3. 如何实现“展开”

基本思路:

首先定义两个9×9的矩阵,其中一个矩阵用来存放各个方块下的数字(-1代表雷),另一个用来存放该方块的颜色(0代表灰色,即默认色;1代表白色,即普通展开;2代表红色,即认定的雷)。

通过自定义View来实现。并且该自定义View的宽高设置为固定的901px(小方格的边长为100px,线的宽度为1px)。

每点击一次方块都会调用view的invalidate方法,进而会触发onDraw方法。在点击事件中更改颜色矩阵的值,并在onDraw方法中根据两个矩阵的值进行重绘。

代码展示:

  • 布局文件
1 <?xml version="1.0" encoding="utf-8"?>
2 <RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
3     android:layout_width="match_parent"
4     android:layout_height="match_parent">
5     <com.example.lanxingren.minesweeping.MineSweepingView
6         android:layout_width= "901px"
7         android:layout_height="901px"
8         android:layout_centerInParent="true"/>
9 </RelativeLayout>
  • 主活动
 1 package com.example.lanxingren.minesweeping;
 2
 3 import android.support.v7.app.AppCompatActivity;
 4 import android.os.Bundle;
 5
 6 public class MainActivity extends AppCompatActivity {
 7
 8     @Override
 9     protected void onCreate(Bundle savedInstanceState) {
10         super.onCreate(savedInstanceState);
11         setContentView(R.layout.activity_main);
12     }
13 }
  • 自定义View
  1 package com.example.lanxingren.minesweeping;
  2
  3 import android.content.Context;
  4 import android.graphics.Canvas;
  5 import android.graphics.Color;
  6 import android.graphics.Paint;
  7 import android.graphics.Point;
  8 import android.graphics.Typeface;
  9 import android.util.AttributeSet;
 10 import android.view.GestureDetector;
 11 import android.view.MotionEvent;
 12 import android.view.View;
 13 import android.widget.Toast;
 14
 15 import java.util.Random;
 16
 17
 18 public class MineSweepingView extends View {
 19
 20     //    private MineSweepingView (Context context) {
 21     //        super(context);
 22     //    }
 23
 24     Context myContext;
 25
 26
 27     // 触摸方块左上角的点
 28     Point leftTop;
 29
 30     // 默认背景画笔
 31     Paint strokePaint = new Paint();
 32
 33     // 涂色画笔,红色代表玩家认为的雷,白色代表展开该方块
 34     Paint whitePaint = new Paint();
 35     Paint redPaint = new Paint();
 36
 37     //绘制数字的画笔
 38     Paint textPaint = new Paint();
 39
 40     // 代表每个坐标的颜色,其中0代表银灰色,1代表白色,2代表红色
 41     int[][] colors;
 42     // 代表每个坐标的数字,其中-1代表雷
 43     int[][] numbers;
 44
 45     //小格子边长
 46     final int width = 100;
 47
 48     //一行格子数
 49     final int rowCount = 9;
 50
 51     //雷的个数
 52     final int mineCount = 10;
 53
 54     //手势操作监听器
 55     private GestureDetector.OnGestureListener onGestureListener = new GestureDetector.OnGestureListener() {
 56         //防止其他事件不执行,所以返回true
 57         @Override
 58         public boolean onDown(MotionEvent e) {
 59             return true;
 60         }
 61
 62         @Override
 63         public void onShowPress(MotionEvent e) {
 64
 65         }
 66
 67         //短按事件
 68         @Override
 69         public boolean onSingleTapUp(MotionEvent e) {
 70             leftTop = findLeftTop(e.getX(), e.getY());
 71
 72             if (numbers[leftTop.x][leftTop.y] == -1) {
 73                 Toast.makeText(myContext, "你输了!", Toast.LENGTH_SHORT).show();
 74                 reset();
 75                 MineSweepingView.this.invalidate();
 76             } else {
 77                 expand(leftTop.x, leftTop.y);
 78                 MineSweepingView.this.invalidate();
 79             }
 80
 81             return true;
 82         }
 83
 84         //根据扫雷逻辑展开小方块
 85         private void expand(int x, int y) {
 86             //如果是雷
 87             if (numbers[x][y] == -1) {
 88                 return;
 89             } else if (numbers[x][y] == 0 && colors[x][y] == 0) {
 90                 colors[x][y] = 1;
 91
 92                 //左上
 93                 if (x - 1 >= 0 && y - 1 >= 0) {
 94                     expand(x - 1, y - 1);
 95                 }
 96                 //上
 97                 if (y - 1 >= 0) {
 98                     expand(x, y - 1);
 99                 }
100                 //右上
101                 if (x + 1 < rowCount && y - 1 >= 0) {
102                     expand(x + 1, y - 1);
103                 }
104                 //右
105                 if (x + 1 < rowCount) {
106                     expand(x + 1, y);
107                 }
108                 //右下
109                 if (x + 1 < rowCount && y + 1 < rowCount) {
110                     expand(x + 1, y + 1);
111                 }
112                 //下
113                 if (y + 1 < rowCount) {
114                     expand(x, y + 1);
115                 }
116                 //左下
117                 if (x - 1 >= 0 && y + 1 < rowCount) {
118                     expand(x - 1, y + 1);
119                 }
120                 //左
121                 if (x - 1 >= 0) {
122                     expand(x - 1, y);
123                 }
124             } else {
125                 colors[x][y] = 1;
126             }
127
128         }
129
130         @Override
131         public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
132             return false;
133         }
134
135         //长按事件
136         @Override
137         public void onLongPress(MotionEvent e) {
138             leftTop = findLeftTop(e.getX(), e.getY());
139
140             if (colors[leftTop.x][leftTop.y] != 1) {
141                 colors[leftTop.x][leftTop.y] = 2;
142                 MineSweepingView.this.invalidate();
143             }
144         }
145
146         @Override
147         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
148             return false;
149         }
150     };
151
152     private GestureDetector detector = new GestureDetector(onGestureListener);
153
154     public MineSweepingView(Context context, AttributeSet attributeSet) {
155         super(context, attributeSet);
156
157         myContext = context;
158
159         strokePaint.setColor(Color.BLACK);
160         strokePaint.setStrokeWidth(1);
161
162         whitePaint.setStyle(Paint.Style.FILL_AND_STROKE);
163         whitePaint.setColor(Color.WHITE);
164
165         redPaint.setStyle(Paint.Style.FILL_AND_STROKE);
166         redPaint.setColor(Color.RED);
167
168         textPaint.setColor(Color.BLACK);
169         textPaint.setTextAlign(Paint.Align.CENTER);
170         textPaint.setTextSize(50);
171         Typeface typeface = Typeface.createFromAsset(context.getAssets(), "fonts/consola.ttf");
172         textPaint.setTypeface(typeface);
173         textPaint.setStyle(Paint.Style.FILL);
174
175         reset();
176     }
177
178     @Override
179     protected void onDraw(Canvas canvas) {
180         super.onDraw(canvas);
181
182         canvas.drawColor(Color.GRAY);
183
184         for (int i = 0; i <= canvas.getWidth(); i += width) {
185             canvas.drawLine(i, 0, i, canvas.getHeight(), strokePaint);
186         }
187
188         for (int j = 0; j <= canvas.getHeight(); j += width) {
189             canvas.drawLine(0, j, canvas.getWidth(), j, strokePaint);
190         }
191
192         Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
193         float top = fontMetrics.top;
194         float bottom = fontMetrics.bottom;
195
196         int grayCount = 0;
197         int redCount = 0;
198
199         for (int x = 0; x < rowCount; x++) {
200             for (int y = 0; y < rowCount; y++) {
201                 switch (colors[x][y]) {
202                     //宽高各缩减一单位是为了防止把细线也给覆盖了
203                     case 1://白色
204                         canvas.drawRect(x * width + 1, y * width + 1, (x + 1) * width - 1, (y + 1) * width - 1, whitePaint);
205                         if (numbers[x][y] != -1 && numbers[x][y] != 0) {
206                             canvas.drawText(Integer.toString(numbers[x][y]), x * width + 50, y * width + 50 - top / 2 - bottom / 2, textPaint);
207                         } else if (numbers[x][y] == -1) {
208                             canvas.drawRect(x * width + 1, y * width + 1, (x + 1) * width - 1, (y + 1) * width - 1, redPaint);
209                         }
210                         break;
211                     case 2://红色
212                         canvas.drawRect(x * width + 1, y * width + 1, (x + 1) * width - 1, (y + 1) * width - 1, redPaint);
213                         redCount++;
214                         break;
215                     case 0://灰色
216                         grayCount++;
217                         break;
218                     default:
219                         break;
220                 }
221             }
222         }
223
224         if(grayCount == 0 && redCount == 10) {
225             Toast.makeText(myContext, "你赢了!", Toast.LENGTH_LONG).show();
226         }
227     }
228
229     @Override
230     public boolean onTouchEvent(MotionEvent event) {
231 //        super.onTouchEvent(event);
232 //
233 //        if (event.getAction() == MotionEvent.ACTION_DOWN) {
234 //            leftTop = findLeftTop(event.getX(), event.getY());
235 //            colors[leftTop.x][leftTop.y] = 1;
236 //            this.invalidate();
237 //        }
238 //
239 //        return true;
240
241         //使用手势触摸
242         return detector.onTouchEvent(event);
243     }
244
245     //找到触点所在的小方块
246     private Point findLeftTop(float touchX, float touchY) {
247         Point point = new Point();
248
249         for (int i = 0; i < rowCount; i++) {
250             if (touchX - i * width > 0 && touchX - i * width < width) {
251                 point.x = i;
252             }
253             if (touchY - i * width > 0 && touchY - i * width < width) {
254                 point.y = i;
255             }
256         }
257
258         return point;
259     }
260
261     //重置游戏
262     private void reset() {
263         colors = new int[rowCount][rowCount];
264         numbers = new int[rowCount][rowCount];
265
266         createMines();
267     }
268
269     private void createMines() {
270         int x;
271         int y;
272         int minesCount = 0;
273         Random random = new Random();
274
275         //藏雷
276         while (minesCount < mineCount) {
277             x = random.nextInt(rowCount);
278             y = random.nextInt(rowCount);
279
280             if (numbers[x][y] != -1) {
281                 numbers[x][y] = -1;
282                 minesCount++;
283                 plusNumber(x, y);
284             }
285         }
286     }
287
288     //填充雷附近的数字
289     private void plusNumber (int x, int y) {
290         //左上
291         if (x - 1 >= 0 && y - 1 >= 0 && numbers[x - 1][y - 1] != -1) {
292             numbers[x - 1][y - 1]++;
293         }
294         //上
295         if (y - 1 >= 0 && numbers[x][y - 1] != -1) {
296             numbers[x][y - 1]++;
297         }
298         //右上
299         if (x + 1 < rowCount && y - 1 >= 0 && numbers[x + 1][y - 1] != -1) {
300             numbers[x + 1][y - 1]++;
301         }
302         //右
303         if (x + 1 < rowCount && numbers[x + 1][y] != -1) {
304             numbers[x + 1][y]++;
305         }
306         //右下
307         if (x + 1 < rowCount && y + 1 < rowCount && numbers[x + 1][y + 1] != -1) {
308             numbers[x + 1][y + 1]++;
309         }
310         //下
311         if (y + 1 < rowCount && numbers[x][y + 1] != -1) {
312             numbers[x][y + 1]++;
313         }
314         //左下
315         if (x - 1 >= 0 && y + 1 < rowCount && numbers[x - 1][y + 1] != -1) {
316             numbers[x - 1][y + 1]++;
317         }
318         //左
319         if (x - 1 >= 0 && numbers[x - 1][y] != -1) {
320             numbers[x - 1][y]++;
321         }
322     }
323 }

接下来主要讲一讲自定义View内部的代码。

这里是通过GestureDetector来实现的区分短按和长按事件,具体实现步骤为:实现GestureDetector.OnGestureListener接口→创建GestureDetector对象(该对象参数为上一步实现类的对象)→在onTouchEvent中调用GestureDetector的onTouchEvent方法

下面说一下监听类的各个方法:

 1 private GestureDetector.OnGestureListener onGestureListener = new GestureDetector.OnGestureListener() {
 2         //防止其他事件不执行,所以返回true
 3         @Override
 4         public boolean onDown(MotionEvent e) {
 5             return true;
 6         }
 7
 8         @Override
 9         public void onShowPress(MotionEvent e) {
10
11         }
12
13         //短按事件
14         @Override
15         public boolean onSingleTapUp(MotionEvent e) {
16             leftTop = findLeftTop(e.getX(), e.getY());
17
18             if (numbers[leftTop.x][leftTop.y] == -1) {
19                 Toast.makeText(myContext, "你输了!", Toast.LENGTH_SHORT).show();
20                 reset();
21                 MineSweepingView.this.invalidate();
22             } else {
23                 expand(leftTop.x, leftTop.y);
24                 MineSweepingView.this.invalidate();
25             }
26
27             return true;
28         }
29
30         //根据扫雷逻辑展开小方块
31         private void expand(int x, int y) {
32             //如果是雷
33             if (numbers[x][y] == -1) {
34                 return;
35             } else if (numbers[x][y] == 0 && colors[x][y] == 0) {
36                 colors[x][y] = 1;
37
38                 //左上
39                 if (x - 1 >= 0 && y - 1 >= 0) {
40                     expand(x - 1, y - 1);
41                 }
42                 //上
43                 if (y - 1 >= 0) {
44                     expand(x, y - 1);
45                 }
46                 //右上
47                 if (x + 1 < rowCount && y - 1 >= 0) {
48                     expand(x + 1, y - 1);
49                 }
50                 //右
51                 if (x + 1 < rowCount) {
52                     expand(x + 1, y);
53                 }
54                 //右下
55                 if (x + 1 < rowCount && y + 1 < rowCount) {
56                     expand(x + 1, y + 1);
57                 }
58                 //下
59                 if (y + 1 < rowCount) {
60                     expand(x, y + 1);
61                 }
62                 //左下
63                 if (x - 1 >= 0 && y + 1 < rowCount) {
64                     expand(x - 1, y + 1);
65                 }
66                 //左
67                 if (x - 1 >= 0) {
68                     expand(x - 1, y);
69                 }
70             } else {
71                 colors[x][y] = 1;
72             }
73
74         }
75
76         @Override
77         public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
78             return false;
79         }
80
81         //长按事件
82         @Override
83         public void onLongPress(MotionEvent e) {
84             leftTop = findLeftTop(e.getX(), e.getY());
85
86             if (colors[leftTop.x][leftTop.y] != 1) {
87                 colors[leftTop.x][leftTop.y] = 2;
88                 MineSweepingView.this.invalidate();
89             }
90         }
91
92         @Override
93         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
94             return false;
95         }
96     };

主要的是onSingleTapUp方法以及onLongPress方法,前者是短按事件,后者是长按事件。

先说长按事件,findLeftTop方法用于找到触点所在小方块左上角定点坐标,然后直接把该小方块的颜色改为红色,然后调用invalidate方法用于触发onDraw方法。

短按事件,首先找到触点所在小方块左上角坐标。然后判断该小方块是否为雷,如果是雷,直接重置游戏。否则的话,根据expand的逻辑来展开方块。

expand的逻辑:如果要展开的小方块下为大于零的数字,则展开该方块;如果要展开的小方块下为-1(也就是雷),则直接返回;如果要展开的小方块的数字为零,则展开该方块并将盖方块周围的方块执行expand逻辑。

通过expand方法,就实现了扫雷“展开”的逻辑。

下面说一下onDraw方法:

 1 protected void onDraw(Canvas canvas) {
 2         super.onDraw(canvas);
 3
 4         canvas.drawColor(Color.GRAY);
 5
 6         for (int i = 0; i <= canvas.getWidth(); i += width) {
 7             canvas.drawLine(i, 0, i, canvas.getHeight(), strokePaint);
 8         }
 9
10         for (int j = 0; j <= canvas.getHeight(); j += width) {
11             canvas.drawLine(0, j, canvas.getWidth(), j, strokePaint);
12         }
13
14         Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
15         float top = fontMetrics.top;
16         float bottom = fontMetrics.bottom;
17
18         int grayCount = 0;
19         int redCount = 0;
20
21         for (int x = 0; x < rowCount; x++) {
22             for (int y = 0; y < rowCount; y++) {
23                 switch (colors[x][y]) {
24                     //宽高各缩减一单位是为了防止把细线也给覆盖了
25                     case 1://白色
26                         canvas.drawRect(x * width + 1, y * width + 1, (x + 1) * width - 1, (y + 1) * width - 1, whitePaint);
27                         if (numbers[x][y] != -1 && numbers[x][y] != 0) {
28                             canvas.drawText(Integer.toString(numbers[x][y]), x * width + 50, y * width + 50 - top / 2 - bottom / 2, textPaint);
29                         } else if (numbers[x][y] == -1) {
30                             canvas.drawRect(x * width + 1, y * width + 1, (x + 1) * width - 1, (y + 1) * width - 1, redPaint);
31                         }
32                         break;
33                     case 2://红色
34                         canvas.drawRect(x * width + 1, y * width + 1, (x + 1) * width - 1, (y + 1) * width - 1, redPaint);
35                         redCount++;
36                         break;
37                     case 0://灰色
38                         grayCount++;
39                         break;
40                     default:
41                         break;
42                 }
43             }
44         }
45
46         if(grayCount == 0 && redCount == 10) {
47             Toast.makeText(myContext, "你赢了!", Toast.LENGTH_LONG).show();
48         }
49     }

具体的步骤为:用灰色充当背景色,涂色→画好竖线以及横线,实现了9×9的矩阵→根据小方块颜色来涂色

根据小方块颜色涂色的逻辑为:如果颜色是白色,则先把小方块变成白色,然后在小方块上画上小方块内要显示的数字(如果是零,则不显示);如果颜色是红色,则把小方块颜色涂成红色。

涂色之后可以获取到灰色小方块的个数以及红色小方块的个数。当灰色小方块的个数为零并且红色小方块的个数为十的时候证明游戏成功!

接下来说一下reset方法,该方法生成了雷以及雷周围的数字:

 1 //重置游戏
 2     private void reset() {
 3         colors = new int[rowCount][rowCount];
 4         numbers = new int[rowCount][rowCount];
 5
 6         createMines();
 7     }
 8
 9     private void createMines() {
10         int x;
11         int y;
12         int minesCount = 0;
13         Random random = new Random();
14
15         //藏雷
16         while (minesCount < mineCount) {
17             x = random.nextInt(rowCount);
18             y = random.nextInt(rowCount);
19
20             if (numbers[x][y] != -1) {
21                 numbers[x][y] = -1;
22                 minesCount++;
23                 plusNumber(x, y);
24             }
25         }
26     }
27
28     //填充雷附近的数字
29     private void plusNumber (int x, int y) {
30         //左上
31         if (x - 1 >= 0 && y - 1 >= 0 && numbers[x - 1][y - 1] != -1) {
32             numbers[x - 1][y - 1]++;
33         }
34         //上
35         if (y - 1 >= 0 && numbers[x][y - 1] != -1) {
36             numbers[x][y - 1]++;
37         }
38         //右上
39         if (x + 1 < rowCount && y - 1 >= 0 && numbers[x + 1][y - 1] != -1) {
40             numbers[x + 1][y - 1]++;
41         }
42         //右
43         if (x + 1 < rowCount && numbers[x + 1][y] != -1) {
44             numbers[x + 1][y]++;
45         }
46         //右下
47         if (x + 1 < rowCount && y + 1 < rowCount && numbers[x + 1][y + 1] != -1) {
48             numbers[x + 1][y + 1]++;
49         }
50         //下
51         if (y + 1 < rowCount && numbers[x][y + 1] != -1) {
52             numbers[x][y + 1]++;
53         }
54         //左下
55         if (x - 1 >= 0 && y + 1 < rowCount && numbers[x - 1][y + 1] != -1) {
56             numbers[x - 1][y + 1]++;
57         }
58         //左
59         if (x - 1 >= 0 && numbers[x - 1][y] != -1) {
60             numbers[x - 1][y]++;
61         }
62     }

reset方法首先重置了颜色矩阵和数字矩阵。

接下来通过随机数的方式随机生成一个雷,并把数字矩阵下该坐标所对应的值改为-1;接着把该雷周围一圈数字都加一;然后生成第二个雷。

这样循环了十个雷之后雷以及数字就生成完毕。

以上就是通过自定义View的方式实现的一个简易版扫雷。

第一次写博客,比较生疏。如有建议,欢迎评论~

原文地址:https://www.cnblogs.com/lanxingren/p/9105147.html

时间: 2024-11-13 06:33:16

利用自定义View实现扫雷游戏的相关文章

利用自定义View实现头像截取页面

在一些应用中,特别是有帐号体系的应用,通过相册选择图片和照相机拍照,然后对所获取的头像进行截取,最后获取选择框中的内容作为头像,一般效果会如下显示: 那么,如何制作这样一个效果呢,关键点在哪里呢? 做法其实可以有多种, 1)可以直接继承ImageView,然后在ImageView的OnDraw函数中直接绘制圆形或者方形的高亮圈: 2)也可以直接继承View,自己将Bitmap给传进来,在onDraw函数中先绘制图片,接着利用Path来绘制这个圆形或者方形的选择区域,利用Region.OP.DIF

【Android】利用自定义View的重绘实现拖动移动,获取组件的尺寸

下面利用一个app来说明如何利用自定义View的重绘实现拖动移动,获取组件的尺寸. 如下图,触摸拖动,或者轻轻点击屏幕都能移动图片.如果碰到文字,则会弹出提示. 这里是利用自定义View的重绘来实现的.就是点击屏幕一次,这个自定义View就会重绘一次.虽然这个自定义View里面就只有一个图片. 1.首先在res\values\strings.xml中定义各个字体文件,修改之后如下: <?xml version="1.0" encoding="utf-8"?&g

自定义View 之利用ViewPager 实现画廊效果(滑动放大缩小)

自定义View 之利用ViewPager 实现画廊效果(滑动放大缩小) 转载请标明出处: http://blog.csdn.net/lisdye2/article/details/52315008 本文出自:[Alex_MaHao的博客] 项目中的源码已经共享到github,有需要者请移步[Alex_MaHao的github] 基本介绍 画廊在很多的App设计中都有,如下图所示: 该例子是我没事的时候写的一个小项目,具体源码地址请访问https://github.com/AlexSmille/Y

Android ——利用OnDraw实现自定义View(转)

自定义View的实现方式大概可以分为三种,自绘控件.组合控件.以及继承控件.本文将介绍自绘控件的用法.自绘控件的意思是,这个控件上的内容是用onDraw函数绘制出来的.关于onDraw函数的介绍可参看 Android视图绘制流程完全解析,带你一步步深入了解View(二) . 例子1:在layout文件中使用自绘控件 出处:http://blog.csdn.net/guolin_blog/article/details/17357967 下面我们准备来自定义一个计数器View,这个View可以响应

【朝花夕拾】Android自定义View篇之(八)多点触控(上)基础知识

前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/11155259.html],谢谢! 在前面的文章中,介绍了不少触摸相关的知识,但都是基于单点触控的,即一次只用一根手指.但是在实际使用App中,常常是多根手指同时操作,这就需要用到多点触控相关的知识了.多点触控是在Android2.0开始引入的,在现在使用的Android手机上都是支持多点触控的.本系列文章将对常见的多点触控相关的重点知识进行总结,并使用多点触控来实现一些常见的效果,从而达到将

自定义View 篇三 《手动打造ViewPage》

有了之前自定义View的理论基础,有了ViewPage.事件分发机制.滑动冲突.Scroller使用等相关知识的铺垫,今天纯手动打造一款ViewPage. 1.完成基本的显示: 在MainActivity中: public class MainActivity extends AppCompatActivity { private MyViewPage mViewPage; int[] imageIds = new int[]{ R.drawable.pic_0, R.drawable.pic_

android 自定义View(1)

自定义View: 第一步:创建一个View的实现类, 创建构造器和重写onDraw() 和onMesure()等方法. TypedArray获取自定义View的属性的数组 context.obtainStyledAttributes(attrs, R.styleable.MyView); // 从数组中根据styleable中定义的对应属性的值 属性是自定义View的名+“-”+属性名===>MyView_textColor int textColor = array.getColor(R.st

自定义进阶2——自定义View显示超大图片

上一节(自定义初学5--自定义View显示图片)已经说了如何自定义View显示图片.做android时,加载图片是避免不了的,加载网络图片还需要异步加载,最烦人的就是经常出现OOM,为了避免这样的问题,我们一般这样解决: 根据图片控件的大小对图片进行压缩显示. 如果图片数量非常多,则会使用LruCache等缓存机制,将所有图片占据的内容维持在一个范围内. 有时加载图片还会遇到特殊情况--就是单个图片非常巨大,还不允许压缩.那么对于这种需求,该如何做呢? 首先不压缩,按照原图尺寸加载,那么屏幕肯定

自定义View系列教程01--常用工具介绍

在自定义View的时候,常常会用到一些Android系统提供的工具.这些工具封装了我们经常会用到的方法,比如拖拽View,计算滑动速度,View的滚动,手势处理等等.如果我们自己去实现这些方法会比较繁琐,而且容易出一些bug.所以,作为自定义View系列教程的开端,先介绍一下这些常用的工具,以便在后续的学习和工作中使用. Configuration ViewConfiguration GestureDetector VelocityTracker Scroller ViewDragHelper