-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
733 lines (733 loc) · 422 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[创建和运行第一个Flutter项目]]></title>
<url>%2F2019%2F07%2F25%2F%E5%88%9B%E5%BB%BA%E5%92%8C%E8%BF%90%E8%A1%8C%E7%AC%AC%E4%B8%80%E4%B8%AAFlutter%E9%A1%B9%E7%9B%AE%2F</url>
<content type="text"><![CDATA[想要学一门新的技术,最快了解它的方式就是运行写一个简短的demo,然后跑起来。今天教大家用Android Studio创建一个Flutter项目,然后跑起来。然后尝试修改一些值,体验Flutter的热加载。 1.菜单选择New Flutter Project 2.输入项目名、选择Flutter SDK路径、项目存放位置和项目描述,点击Next下一步 3.设置包名,然后点击Finish按钮 4.创建完成后的项目结构 5.main.dartlib文件夹下的main.dart文件,主要看这个文件,一开始看不懂也没关系,先运行起来再说 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( // This is the theme of your application. // // Try running your application with "flutter run". You'll see the // application has a blue toolbar. Then, without quitting the app, try // changing the primarySwatch below to Colors.green and then invoke // "hot reload" (press "r" in the console where you ran "flutter run", // or simply save your changes to "hot reload" in a Flutter IDE). // Notice that the counter didn't reset back to zero; the application // is not restarted. primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Flutter Demo Home Page'), ); }}class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); // This widget is the home page of your application. It is stateful, meaning // that it has a State object (defined below) that contains fields that affect // how it looks. // This class is the configuration for the state. It holds the values (in this // case the title) provided by the parent (in this case the App widget) and // used by the build method of the State. Fields in a Widget subclass are // always marked "final". final String title; @override _MyHomePageState createState() => _MyHomePageState();}class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState(() { // This call to setState tells the Flutter framework that something has // changed in this State, which causes it to rerun the build method below // so that the display can reflect the updated values. If we changed // _counter without calling setState(), then the build method would not be // called again, and so nothing would appear to happen. _counter++; }); } @override Widget build(BuildContext context) { // This method is rerun every time setState is called, for instance as done // by the _incrementCounter method above. // // The Flutter framework has been optimized to make rerunning build methods // fast, so that you can just rebuild anything that needs updating rather // than having to individually change instances of widgets. return Scaffold( appBar: AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. title: Text(widget.title), ), body: Center( // Center is a layout widget. It takes a single child and positions it // in the middle of the parent. child: Column( // Column is also layout widget. It takes a list of children and // arranges them vertically. By default, it sizes itself to fit its // children horizontally, and tries to be as tall as its parent. // // Invoke "debug painting" (press "p" in the console, choose the // "Toggle Debug Paint" action from the Flutter Inspector in Android // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) // to see the wireframe for each widget. // // Column has various properties to control how it sizes itself and // how it positions its children. Here we use mainAxisAlignment to // center the children vertically; the main axis here is the vertical // axis because Columns are vertical (the cross axis would be // horizontal). mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.display1, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), // This trailing comma makes auto-formatting nicer for build methods. ); }} 6.运行点击Run按钮运行 运行结果如下,中间文字显示右下角按钮被点击的次数 7.热重载Ctrl+S保存或者按Hot reload按钮热重载代码,而无需重启应用,应用的原有状态不会改变 将main.dart文件中主题的颜色由blue改成green,然后点击Hot reload按钮 123theme: ThemeData( primarySwatch: Colors.green,), 可以看到应用不用重启主题直接变成绿色,而且状态保留,中间的数字还是保持15,而不是0。 而且热重载的加载速度极快,我试了下顶多一秒,简直爽歪歪。 参考 Test drive]]></content>
</entry>
<entry>
<title><![CDATA[Android Studio安装Flutter plugin和Dart plugin]]></title>
<url>%2F2019%2F07%2F25%2FAndroid-Studio%E5%AE%89%E8%A3%85Flutter-plugin%E5%92%8CDart-plugin%2F</url>
<content type="text"><![CDATA[在安装插件前首先需要安装Android Studio,如果未安装的可以到这里下载安装,这里就不再多说了 File > Settings > Plugins 打开插件安装页面 在Marketplace的搜索框中输入Flutter搜索插件 找到Flutter插件,单击Install安装 安装Flutter过程中会提示安装Dart plugin,点击Yes确认安装 安装完成后按提示重启IDE即可 搜索Flutter插件 提示安装Dart插件 安装完成后,点击Restart IDE重启IDE 参考 Set up an editor]]></content>
</entry>
<entry>
<title><![CDATA[Flutter SDK下载安装及环境变量配置]]></title>
<url>%2F2019%2F07%2F25%2FFlutter-SDK%E4%B8%8B%E8%BD%BD%E5%AE%89%E8%A3%85%E5%8F%8A%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E9%85%8D%E7%BD%AE%2F</url>
<content type="text"><![CDATA[下载点击下载地址下载Flutter SDK包,免安装,解压后把flutter文件夹放在合适的位置,例如我放在D:\Program\flutter路径下 进入flutter文件夹下,找到flutter_console.bat,直接双击打开flutter控制台,在这里可以执行flutter命令 配置环境变量在Win10搜索框输入env,进入编辑系统环境变量,点击环境变量按钮 在环境变量对话框找到用户变量下的PATH,双击打开编辑环境变量对话框,点击新建按钮新建一条记录,值为flutter下的bin文件夹路径,例如我的配置值为D:\Program\flutter\bin 配置完保存后,关闭所有命令行窗口 flutter doctor配置完环境变量后可以重新打开命令行窗口,直接输入flutter doctor命令,该命令会检查系统看有哪些flutter开发环境所需的依赖还未配置或者安装,按照提示安装即可 例如我运行flutter doctor后提示IntelliJ IDEA未安装Flutter plugin和Dart Plugin,但是我的Android Studio上已经有安装了所以不用管 参考 Windows install]]></content>
</entry>
<entry>
<title><![CDATA[Android解决SoundPool只播放十几秒就停止播放的问题]]></title>
<url>%2F2019%2F07%2F23%2FAndroid%E8%A7%A3%E5%86%B3SoundPool%E5%8F%AA%E6%92%AD%E6%94%BE%E5%8D%81%E5%87%A0%E7%A7%92%E5%B0%B1%E5%81%9C%E6%AD%A2%E6%92%AD%E6%94%BE%E7%9A%84%E9%97%AE%E9%A2%98%2F</url>
<content type="text"><![CDATA[问题描述项目中用SoundPool播放一段背景音乐,音乐时长大概一分钟,但是每次只播放了十几秒就停止了 12345678910SoundPool soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC,0);int soundID = soundPool.load(context, R.raw.sound, 1);soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() { @Override public void onLoadComplete(SoundPool soundPool, int sampleId, int status) { if (sampleId == soundID) { soundPool.play(soundID, 1, 1, 0, 0, 1); } }}); 解决方法SoundPool本来设计就是用来播放短暂音频的,网上有人说可以降低原来音频的抽样率来减少音频的大小,但我还是不推荐,可能不是那么长的音频还可以用该方法,如果像我用的超过一分钟的甚至更长的音频肯定不行。所以还是果断弃坑,推荐用MediaPlayer代替,如下代码所示 12MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.sound);mediaPlayer.start();]]></content>
</entry>
<entry>
<title><![CDATA[Android SoundPool循环播放]]></title>
<url>%2F2019%2F07%2F23%2FAndroid-SoundPool%E5%BE%AA%E7%8E%AF%E6%92%AD%E6%94%BE%2F</url>
<content type="text"><![CDATA[SoundPool适用于同时播放多个短促的音乐,如游戏的音效,支持单次播放、多次播放和无限循环播放 源码以下源码的注释对loop参数说得很明白,总结如下: 0表示不循环 -1表示无限循环(注意该情况需主动调用stop()方法停止播放) 大于0的值具体表示重复播放的次数,总的播放次数=loop+1(第一次播放加上重复播放的次数) SondPool源码开头中loop的相关说明 12345* <p>Sounds can be looped by setting a non-zero loop value. A value of -1* causes the sound to loop forever. In this case, the application must * explicitly call the stop() function to stop the sound. Any other non-zero* value will cause the sound to repeat the specified number of times, e.g.* a value of 3 causes the sound to play a total of 4 times.</p> play()方法的源码,其中包含对loop参数的说明 1234567891011121314151617181920212223242526272829/** * Play a sound from a sound ID. * * Play the sound specified by the soundID. This is the value * returned by the load() function. Returns a non-zero streamID * if successful, zero if it fails. The streamID can be used to * further control playback. Note that calling play() may cause * another sound to stop playing if the maximum number of active * streams is exceeded. A loop value of -1 means loop forever, * a value of 0 means don't loop, other values indicate the * number of repeats, e.g. a value of 1 plays the audio twice. * The playback rate allows the application to vary the playback * rate (pitch) of the sound. A value of 1.0 means play back at * the original frequency. A value of 2.0 means play back twice * as fast, and a value of 0.5 means playback at half speed. * * @param soundID a soundID returned by the load() function * @param leftVolume left volume value (range = 0.0 to 1.0) * @param rightVolume right volume value (range = 0.0 to 1.0) * @param priority stream priority (0 = lowest priority) * @param loop loop mode (0 = no loop, -1 = loop forever) * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0) * @return non-zero streamID if successful, zero if failed */public final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate) { baseStart(); return _play(soundID, leftVolume, rightVolume, priority, loop, rate);} 单次播放1soudPool.play(soundID, leftVolume, rightVolume, priority, 0, rate); 多次播放123456// 重复播放1次,也就是总共播放2次soudPool.play(soundID, leftVolume, rightVolume, priority, 1, rate);// 重复播放2次,也就是总共播放3次soudPool.play(soundID, leftVolume, rightVolume, priority, 2, rate);// 重复播放3次,也就是总共播放4次soudPool.play(soundID, leftVolume, rightVolume, priority, 3, rate); 循环播放1soudPool.play(soundID, leftVolume, rightVolume, priority, -1, rate);]]></content>
</entry>
<entry>
<title><![CDATA[Android SoundPool 最大同时播放音频量maxStreams简介]]></title>
<url>%2F2019%2F07%2F23%2FAndroid-SoundPool-%E6%9C%80%E5%A4%A7%E5%90%8C%E6%97%B6%E6%92%AD%E6%94%BE%E9%9F%B3%E9%A2%91%E9%87%8FmaxStreams%E7%AE%80%E4%BB%8B%2F</url>
<content type="text"><![CDATA[SoudPool的构造方法的第一个参数maxStreams表示当前SoundPool最大能够同时播放多少个音频,具体可以看如下SoudPool构造方法的源码 1234567891011121314151617181920/** * Constructor. Constructs a SoundPool object with the following * characteristics: * * @param maxStreams the maximum number of simultaneous streams for this * SoundPool object * @param streamType the audio stream type as described in AudioManager * For example, game applications will normally use * {@link AudioManager#STREAM_MUSIC}. * @param srcQuality the sample-rate converter quality. Currently has no * effect. Use 0 for the default. * @return a SoundPool object, or null if creation failed * @deprecated use {@link SoundPool.Builder} instead to create and configure a * SoundPool instance */public SoundPool(int maxStreams, int streamType, int srcQuality) { this(maxStreams, new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build()); PlayerBase.deprecateStreamTypeForPlayback(streamType, "SoundPool", "SoundPool()");} 在SoudPool源码里还有如下一段对maxStreams的说明,总结为如下几点 SoundPool能够管理同一时间内音频播放的数量 SoundPool跟踪当前播放的音频数量,若数量超了,会按一定规则停止先前播放的音频 数量超过maxStreams停止播放的规则为,首先按音频的优先级,然后按音频已经播放的时长 限制maxStreams的数量,减小对UI交互性能的影响 123456789* <p>In addition to low-latency playback, SoundPool can also manage the number* of audio streams being rendered at once. When the SoundPool object is* constructed, the maxStreams parameter sets the maximum number of streams* that can be played at a time from this single SoundPool. SoundPool tracks* the number of active streams. If the maximum number of streams is exceeded,* SoundPool will automatically stop a previously playing stream based first* on priority and then by age within that priority. Limiting the maximum* number of streams helps to cap CPU loading and reducing the likelihood that* audio mixing will impact visuals or UI performance.</p> maxStreams大小的设置应该根据具体需求具体场景来定,先预估同一时间最多可能有多少个音频同时播放,然后将maxStreams设置为该值,太小了容易出现有的音频没播放完就被停止,设置太大浪费CPU资源,影响性能。]]></content>
</entry>
<entry>
<title><![CDATA[Android SoundPool封装]]></title>
<url>%2F2019%2F07%2F23%2FAndroid-SoundPool%E5%B0%81%E8%A3%85%2F</url>
<content type="text"><![CDATA[SoundPool简介SoundPool常用来同时播放多个短暂的音频 封装这里封装一个简单的SoundPlayer,模拟管理播放王者荣耀里的单杀、双杀、和三杀的音频,支持播放、循环播放、暂停、继续播放等功能 所需的三个音频文件sound_single_kill、sound_double_kill、sound_triple_kill放置在/res/raw文件夹下 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899public class SoundPlayer { private Context context; private SoundPool soundPool; // 能同时播放的最大声音数 private static final int MAX_SREAMS = 5; // 单杀的声音 public static int SOUND_SINGLE_KILL; // 双杀的声音 public static int SOUND_DOUBLE_KILL; // 三杀的声音 public static int SOUND_TRIPLE_KILL; public SoundPlayer(Context context) { this(context, null); } public SoundPlayer(Context context, SoundPool.OnLoadCompleteListener onLoadCompleteListener) { this.context = context; soundPool = new SoundPool(MAX_SREAMS, AudioManager.STREAM_MUSIC,0); SOUND_SINGLE_KILL = soundPool.load(context, R.raw.sound_single_kill, 1); SOUND_DOUBLE_KILL = soundPool.load(context, R.raw.sound_double_kill, 1); SOUND_TRIPLE_KILL = soundPool.load(context, R.raw.sound_triple_kill, 1); soundPool.setOnLoadCompleteListener(onLoadCompleteListener); } // resId为放在raw文件夹下的音频文件 public void load(int resId) { soundPool.load(context, resId, 1); } // 参数为SoundPool.load()方法返回的soundID public void unload(int soundID) { soundPool.unload(soundID); } // 播放单杀的声音 public void playSingleKill() { play(SOUND_SINGLE_KILL); } // 播放双杀的声音 public void playDoubleKill() { play(SOUND_DOUBLE_KILL); } // 播放三杀的声音 public void playTripleKill() { play(SOUND_TRIPLE_KILL); } // 播放,soundID参数为SoundPool.load()方法返回的值 public int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate) { return soundPool.play(soundID, leftVolume, rightVolume, priority, loop, rate); } // 默认参数的播放 public int play(int soundID) { return play(soundID, 1, 1, 0, 0, 1); } // 循环播放 public int loopPlay(int soundID) { return play(soundID, 1, 1, 0, -1, 1); } // 停止播放 public void stop(int streamId) { soundPool.stop(streamId); } // 暂停播放 public void pausea(int streamId) { soundPool.pause(streamId); } // 继续播放 public void resume(int streamId) { soundPool.resume(streamId); } // 暂停所有播放 public void pauseAll() { soundPool.autoPause(); } // 继续所有播放 public void resumeAll() { soundPool.autoResume(); } // 释放 public void release() { if (soundPool != null) { soundPool.release(); soundPool = null; } }} 使用方法12345678910111213141516171819202122public void test(Context context) { // 实例化SoundPlayer SoundPlayer player = new SoundPlayer(context); // 播放单杀音频 player.playSingleKill(); // 暂停播放单杀音频 player.pausea(SoundPlayer.SOUND_SINGLE_KILL); // 继续播放单杀音频 player.resume(SoundPlayer.SOUND_SINGLE_KILL); // 停止播放单杀音频 player.stop(SoundPlayer.SOUND_SINGLE_KILL); // 播放双杀音频 player.playDoubleKill(); // 播放三杀音频 player.playTripleKill(); // 暂停播放所有音频 player.pauseAll(); // 继续播放所有音频 player.resumeAll(); // 释放 player.release();}]]></content>
</entry>
<entry>
<title><![CDATA[解决Hexo clean导致CNAME文件被删问题]]></title>
<url>%2F2019%2F07%2F22%2F%E8%A7%A3%E5%86%B3Hexo-clean%E5%AF%BC%E8%87%B4CNAME%E6%96%87%E4%BB%B6%E8%A2%AB%E5%88%A0%E9%97%AE%E9%A2%98%2F</url>
<content type="text"><![CDATA[问题描述在Hexo个人博客绑定域名这篇文章中我们介绍了Hexo如何绑定域名 最后需要在GitHub项目的根目录创建一个叫CNAME的文件,文件的内容为我们绑定的域名,详情见这篇文章,这里就不再赘述了 一开始我是把CNAME文件直接放在hexo/public目录下,但是每次执行hexo clean后就会被删除 后来改成在GitHub项目的Settings->Options->GitHub Pages的Custom domain中设置域名,如下图所示,但是这种方法和上面的其实没什么差别,也是会在GitHub项目根目录下(也就是Hexo的public目录下)生成一个CNAME文件,在执行hexo clean后该文件还是会被删除 解决方法这是因为整个public文件夹是执行hexo g后生成的,若执行hexo clean会把整个public连同CNAME文件夹删掉,在重新执行hexo g后生成的public文件夹里肯定没有我们手动添加的CNAME文件 解决方法是把CNAME文件放在source文件夹下,而不是public文件夹,在执行hexo g命令是,会把source文件夹下的CNAME文件原原本本地复制到public文件夹下,即使执行hexo clean后,下次重新编译还是会复制过去,这样就解决了问题。 其实还有很多其他的文件如果想编译后放在public文件夹下,都可以先放source文件夹下,在hexo g编译后就会复制到public文件夹下。]]></content>
</entry>
<entry>
<title><![CDATA[相对路径上一级及上上级目录的表示方法]]></title>
<url>%2F2019%2F07%2F22%2F%E7%9B%B8%E5%AF%B9%E8%B7%AF%E5%BE%84%E4%B8%8A%E4%B8%80%E7%BA%A7%E5%8F%8A%E4%B8%8A%E4%B8%8A%E7%BA%A7%E7%9B%AE%E5%BD%95%E7%9A%84%E8%A1%A8%E7%A4%BA%E6%96%B9%E6%B3%95%2F</url>
<content type="text"><![CDATA[问题引入,绝对路径的缺点在Android开发中我会把keystore签名文件放在项目根目录下,然后在build.gradle中配置release包的签名配置,这样每次可以一键打包,不要要再选择签名输入密码等,配置如下所示 12345678signingConfigs { release { keyAlias 'keyAlias' keyPassword 'keyPassword' storeFile file('D:/workspace/MyProject/mykeystore.jks') storePassword 'storePassword' }} 但是签名文件位置D:/workspace/MyProject/mykeystore.jks之前是用绝对路径写死的,因为签名文件是放在项目根路径的,如果项目移动到其他路径,相应的也要修改签名文件的路径配置。或者与团队协同开发,同个项目不同的人从SVN或者Git上check out出来项目存放在本地的路径可能不一样,那么也要修改这个绝对路径的值。 相对路径出场以上问题用相对路径就可以解决,因为该签名文件始终是存放在项目根目录下的,相对于module的builde.grale文件而言,是在builde.grale文件的上一级目录下,而上一级目录可以用../表示,所以可以用如下相对路径表示签名文件的位置,而且不管你把项目放在哪个路径下都不需要再修改该值。 1../mykeystore.jks' 所以修改后的配置如下 12345678signingConfigs { release { keyAlias 'xindeco' keyPassword 'xindeco' storeFile file('../mykeystore.jks') storePassword 'xindeco' }} 上上级路径的表示方法当然相对路径还可以表示上上级、上上上级、上上上上级目录等 上上级:../../ 上上上级:../../../ 上上上上级:../../../../ 相对路径也可以表示上级目录下的不同子目录和文件,例如: ../../subdir/subsubdir/test.txt:表示当前文件的上上级目录下的subdir目录下的subsubdir目录里面的test.txt文件 是不是绕晕了,其实认真理解下还是很清晰的]]></content>
</entry>
<entry>
<title><![CDATA[Hexo文章Scaffolds脚手架]]></title>
<url>%2F2019%2F07%2F21%2FHexo%E6%96%87%E7%AB%A0Scaffolds%E8%84%9A%E6%89%8B%E6%9E%B6%2F</url>
<content type="text"><![CDATA[简介在解决Hexo博客引用网络图片无法显示的问题这篇文章中我们介绍了解决加载网络图片失败的方法,只需在需要加载网络图片的文章头部添加一句XML即可。 但是如果每次写文章还要敲或者复制这句XML还是稍显麻烦,有没有什么办法在我们执行hexo new “post name”的时候就帮我们把这句话自动添加进来? 当然有,这时候Scaffolds脚手架登场了 脚手架在scaffolds文件夹下,里面默认有post.md、draft.md、page.md三个,分别为博文、草稿和page的脚手架 post.md基本大同小异,我们主要看post.md这个文件,内容如下 123title: {{ title }}date: {{ date }}tags: 我们在调用hexo new “post name”(或者hexo new post “post name”)的时候,就是基于post.md脚手架来创建文章的markdown文件的,默认生成的内容如下 123title: Hexo文章Scaffolds脚手架date: 2019-07-21 23:41:29tags: 所以在解决Hexo博客引用网络图片无法显示的问题这篇文章中的问题还有一种解决方法,就是在post.md脚手架文件直接添加 这句话,那么在hexo new创建的每一篇文章都会包含这一句话 post.md修改如下 12345title: {{ title }}date: {{ date }}tags:<meta name="referrer" content="no-referrer"/> 创建的文章头部默认包含这句话 12345title: Hexo文章Scaffolds脚手架date: 2019-07-21 23:41:29tags:<meta name="referrer" content="no-referrer"/> 自定义scaffold当然我们也可以自定义脚手架,比如上面提到的那个问题添加的那句XML添加到自定义scaffold更合适,因为这句话是为了解决网络图片加载不出来的问题的,所以可以在scaffolds文件夹下创建一个image_post.md文件,内容与post.md一样,多增加了这句话 12345title: {{ title }}date: {{ date }}tags:<meta name="referrer" content="no-referrer"/> 然后每次要创建带网络图片的博文可以执行hexo new image_post “post_name”来创建文章,结果如下 12345title: Hexo文章Scaffolds脚手架date: 2019-07-21 23:41:29tags:<meta name="referrer" content="no-referrer"/>]]></content>
</entry>
<entry>
<title><![CDATA[解决Hexo博客引用网络图片无法显示的问题]]></title>
<url>%2F2019%2F07%2F21%2F%E8%A7%A3%E5%86%B3Hexo%E5%8D%9A%E5%AE%A2%E5%BC%95%E7%94%A8%E7%BD%91%E7%BB%9C%E5%9B%BE%E7%89%87%E6%97%A0%E6%B3%95%E6%98%BE%E7%A4%BA%E7%9A%84%E9%97%AE%E9%A2%98%2F</url>
<content type="text"><![CDATA[问题我的博文首发是在CSDN,这样在Hexo博客发布时就可以直接引用CSDN的网络图片。但是在刚deploy上去的时候,预览都是OK的,但是过几天后就全都显示不了,如下图所示 用Chrome浏览器,按F12提示如下403错误 解决方法方法其实很简单,只需要文章的头部如下图所示位置添加这一句话就可以完美解决问题]]></content>
</entry>
<entry>
<title><![CDATA[ES文件浏览器ftp文件共享,通过电脑访问手机文件夹传输文件]]></title>
<url>%2F2019%2F07%2F20%2FES%E6%96%87%E4%BB%B6%E6%B5%8F%E8%A7%88%E5%99%A8ftp%E6%96%87%E4%BB%B6%E5%85%B1%E4%BA%AB%EF%BC%8C%E9%80%9A%E8%BF%87%E7%94%B5%E8%84%91%E8%AE%BF%E9%97%AE%E6%89%8B%E6%9C%BA%E6%96%87%E4%BB%B6%E5%A4%B9%E4%BC%A0%E8%BE%93%E6%96%87%E4%BB%B6%2F</url>
<content type="text"><![CDATA[在开发中遇到这样一个场景,Android设备在装修的时候内嵌到墙内,没有预留任何usb借口,这是后又要先把apk安装包先放到手机存储上才能够安装。当然解决方法有很多种比如手在手机上安装QQ,然后发送apk文件到QQ上再安装,再比如吧apk先存到网盘,再下载下来安装等等。但是这些方法都稍显麻烦,今天介绍的是通过ES浏览器的ftp文件共享方法。 1.下载安装ES文件浏览器在Android设备应用商店搜索ES文件浏览器下载安装,这里不再赘述 2.打开ftp文件共享 打开侧边栏,展开网络菜单项,点击从PC访问,如下图一所示 点击打开按钮,如下图二所示 打开成功后如图三所示,可以看到ftp://172.20.10.4:3721/这样的访问地址 3.电脑端访问访问前需保证电脑和Android自己在同一个局域网内 然后在电脑端的文件夹输入刚在Android端显示的ftp访问地址ftp://172.20.10.4:3721/,回车后就可以看到手机端的文件存储内容 在这里可以复制或者粘贴文件,我们可以把apk安装文件复制到这里,然后在Android端安装。]]></content>
</entry>
<entry>
<title><![CDATA[Win10 Digital License数字许可证永久激活]]></title>
<url>%2F2019%2F07%2F20%2FWin10-Digital-License%E6%95%B0%E5%AD%97%E8%AE%B8%E5%8F%AF%E8%AF%81%E6%B0%B8%E4%B9%85%E6%BF%80%E6%B4%BB%2F</url>
<content type="text"><![CDATA[在开始文章前先声明下,此文章仅供学习,请大家支持正版软件。 之前用的激活每隔一段时间就到期,还要手动激活,不然右下角会有一个水印提示你激活,看着很难受,今天给搭建介绍一个永久激活的方法,亲测可用。 1.下载下载地址 下载完成后解压“W10数字许可C#版v2.6.8”压缩包,找到DigitalLicense.exe双击运行 2.软件界面和使用方法软件界面 Readme详细介绍了软件的使用方法,动英文的小伙伴可以稍微了解下 Readme: Click the Activate button to activate digital authorization automatically. Click the Install-KEY button, only the KEY is installed and will not be activated. Check button Detects system information and SKU values. If the KEY text box is left blank, the built-in KEY corresponding to your system version will be automatically displayed after detection. Silent execution parameters is /Q. After the silent execution is complete, a log file will be generated. C:\W10D.log Add right-click menu to select product KEY. 3.激活虽然Readme写了一大堆使用用方法,但是其实该软件是一键激活的,只需直接点击左下角的ACTIVATE按钮就开始激活 除非遇到什么异常情况才需要Readme里介绍的其他使用方法 4.激活结果如下显示“成功地激活了产品”表示已激活成功 这是Progress details中显示的详细的激活过程的日志,如果出现什么异常导致激活失败,相关的说明也会在这里显示。 12345678910111213141516171819202122+++++++++++++++++++++++++++++++++++++++++++++Product: Professional [17134.1]11:58:21 AM Currently preparing...11:58:21 AM Installing key:VK7JG-NPHTM-C97JM-9MPGT-3V66T成功地安装了产品密钥 VK7JG-NPHTM-C97JM-9MPGT-3V66T。11:58:26 AM SKU: 4811:58:26 AM Adding registry key...11:58:26 AM Executing GatherOsState.exe...11:58:40 AM Deleting registry key...11:58:41 AM Applying GenuineTicket.xml...Done.Converted license Microsoft.Windows.48.X19-98841_8wekyb3d8bbwe and stored at C:\ProgramData\Microsoft\Windows\ClipSvc\Install\Migration\47f1754e-8d08-43cb-8aab-bd0206fec676.xml.Done.+++++++++++++++++++++++++++++++++++++++++++++11:58:41 AM Activating system...正在激活 Windows(R), Professional edition (4de7cb65-cdf1-4de9-8ae8-e3cce27b9f2c) ...成功地激活了产品。11:58:45 AM Deleting temporary files...11:58:45 AM Done. 5.验证cmd打开命令提示符,输入“slmgr.vbs -xpr”然后回车 若提示如下结果表示已永久激活成功]]></content>
</entry>
<entry>
<title><![CDATA[关闭Windows Defender实时保护解决下载激活软件报检测到病毒无法下载的问题]]></title>
<url>%2F2019%2F07%2F20%2F%E5%85%B3%E9%97%ADWindows-Defender%E5%AE%9E%E6%97%B6%E4%BF%9D%E6%8A%A4%E8%A7%A3%E5%86%B3%E4%B8%8B%E8%BD%BD%E6%BF%80%E6%B4%BB%E8%BD%AF%E4%BB%B6%E6%8A%A5%E6%A3%80%E6%B5%8B%E5%88%B0%E7%97%85%E6%AF%92%E6%97%A0%E6%B3%95%E4%B8%8B%E8%BD%BD%E7%9A%84%E9%97%AE%E9%A2%98%2F</url>
<content type="text"><![CDATA[使用Win10的小伙伴一定遇到过这个问题,浏览器下载激活软件一直失败,报检测到病毒问题,即使用其下载工具如迅雷下载成功后解压后里面的软件却被删除了,真的是变态恶心至极。这其实是Windows Defender的实时保护在搞鬼,我们可以先展示关闭实时保护,等下载完破解完后再重新开启实时保护。 声明本文章仅供参考,请大家支持正版软件 1.打开Windows Defender点击桌面右下角的Windows Defender图标(如下图第一个)打开Windows Defender安全中心 2.点击“病毒和威胁防护” 3.点击病毒和威胁防护设置 3.关闭实时保护 大功告成,接下来再去下破解激活软件就不会再报检查问题了。]]></content>
</entry>
<entry>
<title><![CDATA[如何对下载的文件进行MD5校验]]></title>
<url>%2F2019%2F07%2F20%2F%E5%A6%82%E4%BD%95%E5%AF%B9%E4%B8%8B%E8%BD%BD%E7%9A%84%E6%96%87%E4%BB%B6%E8%BF%9B%E8%A1%8CMD5%E6%A0%A1%E9%AA%8C%2F</url>
<content type="text"><![CDATA[对下载的文件,特别是经常下载的一些安装文件进行MD5校验,可以检验文件在传输过程中是否有损坏或者被篡改,如果通过MD5校验工具生成的MD5值与发送方提供的原MD5值不同,最好不要安装该文件,因为有可能被植入病毒或者恶意广告。 MD5校验工具很多,今天介绍的是好压压缩软件工具箱自带的MD5校验工具。 1.首先打开好压,点击工具箱,然后点击MD5校验 2.将需要MD5校验的文件或文件夹拖到如下方框中 3.对比结果这是我测试的文件MD5校验结果,好压MD5校验工具不仅计算MD5结果,还计算了SHA1和CRC32 这是软件作者提供的MD5值,如果两只一致表示该文件是安全的]]></content>
</entry>
<entry>
<title><![CDATA[Netlify搭建个人博客设置域名]]></title>
<url>%2F2019%2F07%2F18%2FNetlify%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E8%AE%BE%E7%BD%AE%E5%9F%9F%E5%90%8D%2F</url>
<content type="text"><![CDATA[前言在上一篇文章Netlify直接从GitHub导入项目部署个人博客中我们介绍了如何从Git仓库直接导入项目快速部署个人博客,如果不了解的可以先看上一篇文章,今天这篇文章主要介绍如何为Netlify博客设置域名 设置域名1.首先进入Netlify控制台,选择要设置域名的项目,我选择了angry-bose(愤怒的博塞,这默认生成的子域名很有趣) 2.然后点击第二个步骤”Set up a custom domain” 3.添加域名,这里我输入的是我在阿里云买的域名www.himmy.cn,然后点击Verify按钮继续 点击“Yes,add domain”按钮确认 4.域名解析 添加完后一开始显示是这样的,还需要到阿里云后台设置域名解析 配置域名解析,添加如下两条CNAME记录 然后再刷新下Netlify页面就可以看到解析成功了 大功告成,这时候通过https://himmy.cn/或者https://www.himmy.cn/就可以访问我的博客啦]]></content>
</entry>
<entry>
<title><![CDATA[Netlify直接从GitHub导入项目部署个人博客]]></title>
<url>%2F2019%2F07%2F18%2FNetlify%E7%9B%B4%E6%8E%A5%E4%BB%8EGitHub%E5%AF%BC%E5%85%A5%E9%A1%B9%E7%9B%AE%E9%83%A8%E7%BD%B2%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%2F</url>
<content type="text"><![CDATA[前言最近在搞个人博客,一开始用的是Hexo+GitHub Pages,但是后来发现用GitHub搭建的站点无法提交百度搜索,因为GitHub禁止了百度爬虫爬取,这样就无法通过百度搜索搜到我的博客了,然后就想着部署到Netlify,Netlify可以支持直接导入GitHub、GitLab等平台的静态博客直接部署。 准备访问Netlify,不需要梯子,若无账号先注册个账号。 部署我已经有创建一个从GitLab导入的项目了,这里为了演示再创建一个从GitHub导入的 1.首先点New site from Git按钮 2.然后选择要从哪个Git平台导入,目前支持GitHub、GitLab、Bitbucket 3.第一次用GitHub导入会提示授权 输入GitHub密码确认授权 4.选择要导入的仓库,这里我选择了之前创建的Hexo GitHub Pages博客项目ghxiaoxiao.github.io 5.部署,这里可以设置部署前的一些操作,比如要部署的仓库的分支、部署前的编译指令和编译后部署哪个文件夹 这我我选择默认,因为我GitHub仓库上传的已经是Hexo编译完后的public文件夹 接下来就慢慢等待,如下两图所示表示正在部署 如下图所示表示部署成功了,可以看到Netlify为我们分配了一个子域名https://angry-bose-0458e5.netlify.com,点击可以直接跳转博客查看效果 博客效果]]></content>
</entry>
<entry>
<title><![CDATA[史上最全的静态网站生成器Static Site Generators大集合(二)]]></title>
<url>%2F2019%2F07%2F15%2F%E5%8F%B2%E4%B8%8A%E6%9C%80%E5%85%A8%E7%9A%84%E9%9D%99%E6%80%81%E7%BD%91%E7%AB%99%E7%94%9F%E6%88%90%E5%99%A8Static-Site-Generators%E5%A4%A7%E9%9B%86%E5%90%88%EF%BC%88%E4%BA%8C%EF%BC%89%2F</url>
<content type="text"><![CDATA[在上一篇文章史上最全的静态网站生成器Static Site Generators大集合中我们介绍了一个集合了超过400个SSG的优秀的网站,站长真的是很用心。今天再介绍另一个网站,叫做StaticGen,是由虚拟主机提供商Netlify维护的,集合了超过250个的项目,也是非常全和用心了,而且可以按照开发语言、模板、开源协议来筛选项目,还未开始搭建自己静态网站或者静态博客的读者们,非常推荐这个网站,可以选择适合自己的网站生成器。 访问StaticGen]]></content>
</entry>
<entry>
<title><![CDATA[静态网站(博客)生成器Static Site Generators (SSGs)介绍]]></title>
<url>%2F2019%2F07%2F14%2F%E9%9D%99%E6%80%81%E7%BD%91%E7%AB%99-%E5%8D%9A%E5%AE%A2-%E7%94%9F%E6%88%90%E5%99%A8Static-Site-Generators-SSGs-%E4%BB%8B%E7%BB%8D%2F</url>
<content type="text"><![CDATA[最近在研究静态博客生成器,看到了GitLab上的一个相关的系列的3篇文章写得很不错,这里记录一下,也分享给想了解Static Site Generators的同学。 SSGs Part 1: Static vs Dynamic Websites第一篇文章对比了静态网站和动态网站的不同点,及各自的优缺点 SSGs Part 2: Modern Static Site Generators第二篇文章的目录如下,主要介绍了静态网站生成器的优点、结构、内置功能、静态博客生成、支持的内容、局限等 Benefits of Modern Static Site Generators Structure of SSGs Environment Template engine Markup language Preprocessors Directory structure SSGs built-in features Blog-Aware SSGs Supported content Limitations of SSGs Overcoming the limitations Conclusion Useful links SSGs Part 3: Build any SSG site with GitLab Pages第三篇文章的目录如下,介绍了一些不用编程语言实现的SSG的实例及GitLab CI配置 Build any SSG site with GitLab Pages SSGs examples Environment: Ruby Environment: Node JS Environment: Python Environment: Go Lang More Examples FAQ: which SSG should I get started with? Getting Help Conclusion]]></content>
</entry>
<entry>
<title><![CDATA[史上最全的静态网站生成器Static Site Generators大集合]]></title>
<url>%2F2019%2F07%2F14%2F%E5%8F%B2%E4%B8%8A%E6%9C%80%E5%85%A8%E7%9A%84%E9%9D%99%E6%80%81%E7%BD%91%E7%AB%99%E7%94%9F%E6%88%90%E5%99%A8Static-Site-Generators%E5%A4%A7%E9%9B%86%E5%90%88%2F</url>
<content type="text"><![CDATA[在以下两篇文章中我们介绍了一些常见的静态网站(博客)生成器,后来发现了一个国外的别人家的网址,整理的更加全面,可以号称是史上最全的Static Site Generators大集合,集合了超过463个项目,这里转载过来介绍给大家 静态网站生成器Static Site Generators(SSGs)推荐 静态网站(博客)生成器Static Site Generators(SSGs)大集合 以下的转载过来的内容,当然也可以直接访问Static Site Generators官网查看最新的内容 Static Site GeneratorsThe definitive listing of Static Site Generators — all 463 of them! Stars Name License Language Created Updated 119⭐️ ABlogABlog for blogging with Sphinx other Python 5 years ago 7 months ago 36⭐️ AceFlexible static sites generator suitable even for big and complex sites with dynamically generated pages such as pages for each tag etc (not on the fly obviously as it just produces HTML). mit Ruby 9 years ago 9 months ago 291⭐️ acrylamid(unmaintained) static blog generator in python with incremental rendering ⛺ BSD-2-Clause Python 8 years ago 3 months ago 20⭐️ AkashaCMSA content management system tool-suite to generate static websites, written in Node.js JavaScript 7 years ago 7 months ago 45⭐️ AkashicStatic Blog Generator——静态博客生成器 Common Lisp 7 years ago a year ago 459⭐️ antwarA static site generator built with React and Webpack. mit JavaScript 4 years ago 4 months ago ApperneticA Static Site Generator as a Service. Appernetic is a web service for managing and generating static content for GitHub Pages. Web 3 years ago 3673⭐️ assembleGet the rocks out of your socks! Assemble makes you fast at web development! Used by thousands of projects for rapid prototyping, themes, scaffolds, boilerplates, e-books, UI components, API documentation, blogs, building websites/static site generator, an alternative to Jekyll for gh-pages and more! Gulp- and grunt-friendly. mit JavaScript 7 years ago 3 months ago AutomadAutomad is a file-based CMS and template engine. It is designed to be fully portable without any dependencies on databases or fixed locations and doesn’t require any complicated setup process. MIT PHP 264⭐️ awestructA static site baking and deployment tool written in Ruby. other Ruby 9 years ago 5 months ago 57⭐️ AYM CMSA Django templating based static CMS. mit Python 11 years ago a year ago 57⭐️ bakerThe bash static site generator with real template engine gpl-2.0 Perl 6 years ago 4 months ago 23⭐️ BalloonGenerate static websites easily ISC JavaScript 5 years ago 9 months ago 14⭐️ BAMEasiest Static Web Generator on the Planet (beta) MIT JavaScript 7 years ago a year ago 817⭐️ bashblogA single Bash script to create blogs. Download, run, write, done! GPL-3.0 Shell 6 years ago 3 months ago 17⭐️ bazingain-progress free blogging tool especially for coders gpl-3.0 Perl 8 years ago 4 months ago 2⭐️ BeetleDYI static site generator mit Python 5 years ago 4 years ago 180⭐️ BenjenBlog engine WTFPL Python 6 years ago 5 months ago Bitbucket PagesHost your static websites on Bitbucket.com for free. 569⭐️ BlacksmithA generic static site generator built using flatiron, plates, and marked. mit JavaScript 8 years ago 4 months ago BlatterBlatter is a tiny tool for creating and publishing static web sites built from dynamic templates. MIT Python BlazeBlogger GPL-3.0 Perl bliperstatic html page generation in perl BSD-2-Clause Perl 9⭐️ BlodeA simple static site/blog generator like jekyll (made in node.js) JavaScript 8 years ago 3 years ago 107⭐️ blogcA blog compiler. bsd-3-clause C 4 years ago 3 months ago blogc++Just another static blog generator, written in C++17. WTFPL C++ 19⭐️ BlogenA local static blog site generator and previewer,that help you deploy blog on github pages gpl-3.0 Python 7 years ago a year ago 9⭐️ blogitA quick and simple static site generator based on markdown and jinja2. BSD-3-Clause Python 7 years ago 7 months ago 338⭐️ BlogofileA static website compiler and blog engine written in Python. MIT Python 10 years ago 5 months ago 21⭐️ blogpyA static blog generator in python gpl-3.0 Python 7 years ago 2 years ago blosxomBlosxom (pronounced blossom) is a lightweight yet feature-packed weblog application designed from the ground up with simplicity, usability, and interoperability in mind. MIT Perl 17 years ago 160⭐️ BlugBecause “I just blogged about it” is too difficult to say. MIT Python 7 years ago 3 months ago 8⭐️ BoltThe best way to build static websites through Ruby. mit Ruby 10 years ago 2 years ago 293⭐️ BonsaiA tiny static web site generator mit Ruby 10 years ago 5 months ago 7⭐️ Bramble MVCA Node.js, MVC based static site generator mit JavaScript 5 years ago 2 years ago 10⭐️ Breadfile based static website/blog generator :bread: mit JavaScript 8 years ago 3 years ago 111⭐️ Bricolage CMSContent management and publishing system (BSD-3-Clause AND LGPL-2.1 AND CC-BY-SA-2.5) Perl 10 years ago 8 months ago 96⭐️ Broccoli TacoStatic Sites Powered by Broccoli MIT JavaScript 5 years ago 7 months ago 358⭐️ brochureA Rack application for serving static sites mit Ruby 9 years ago 5 months ago Bryar Artistic-1.0-Perl Perl 9⭐️ BuntoA modern web application framework and static site generator based on Jekyll. mit Ruby 5 years ago a year ago 743⭐️ busterBrute force static site generator for Ghost mit Python 6 years ago 3 months ago 378⭐️ Cabin:evergreen_tree: Cabin is the best logger for JavaScript mit JavaScript 6 years ago 4 months ago 3273⭐️ CactusStatic site generator for designers. Uses Python and Django templates. other Python 8 years ago 3 months ago 136⭐️ CalepinPublish your markdown documents with the Dropbox API isc JavaScript 7 years ago 7 months ago 91⭐️ CarewA simple site generator mit PHP 7 years ago 8 months ago 130⭐️ catsupA lightweight static website generator which aims to be simple and elegant. mit Python 7 years ago 7 months ago 67⭐️ CecilYour content driven static site generator. mit PHP 6 years ago 3 months ago 27⭐️ ChiliDropbox powered static site generator mit Python 7 years ago 5 months ago 68⭐️ Chisela simple Python static blog generation utility mit Python 10 years ago a year ago 15⭐️ ChronicleChronicle is a simple blog compiler, written in Perl with minimal dependencies. (GPL-3.0 OR Artistic-1.0-Perl) Perl 5 years ago 4 months ago 6⭐️ CipherpressA static html based blog system in 2 parts, one for show and one for submission GPL-2.0 Python 8 years ago 2 years ago 8⭐️ cl-blog-generatorA blog site generator in common lisp bsd-2-clause Common Lisp 10 years ago 4 months ago Cloud CannonThe Cloud CMS for Jekyll. Build static or Jekyll websites, have your team and clients update inline with the CloudCannon GUI. 21⭐️ CMintSCMS and Static Site Generator created with the internationalization in mind mit JavaScript 2 years ago 4 months ago 717⭐️ cobalt.rsStatic site generator written in Rust mit Rust 5 years ago 3 months ago 65⭐️ CodexStatic site and documentation generator. Markdown for content. Jade/stylus for templates. MIT JavaScript 8 years ago 4 months ago 260⭐️ CoisasPure client-side CMS-like file editor and media manager for stuff hosted on GitHub. mit JavaScript 5 years ago 4 months ago 317⭐️ coleslawFlexible Lisp Blogware bsd-2-clause Common Lisp 8 years ago 3 months ago 43⭐️ ComposerStatic website generator, structure/syntax-agnostic. other Python 8 years ago 6 months ago ContentfulContentful is a content management developer platform with an API at its core. 60⭐️ CoryTiny generator for static sites mit JavaScript 3 years ago 3 months ago 3⭐️ cubCub is a template development environment and static site generator in PHP MIT PHP 9 years ago 5 years ago 37⭐️ cyraxStatic site generator isc Python 8 years ago 8 months ago 13⭐️ cytoplasmA static site generator written in python. mit Python 8 years ago 2 years ago 37⭐️ DapperA publishing tool for static websites. MIT Perl 5 years ago 5 months ago 4665⭐️ Daux.ioDaux.io is an documentation generator that uses a simple folder structure and Markdown files to create custom documentation on the fly. It helps you create great looking documentation in a developer friendly way. MIT 6 years ago 3 months ago 12⭐️ deplatedeplate is a tool for converting wiki-like markup to latex, docbook, html, or “html-slides”. It supports embedded LaTeX code, footnotes, citations, biblographies, automatic generation of an index etc. gpl-2.0 Ruby 10 years ago 2 years ago 3⭐️ DeplotDeplot is a lightweight and very extensible static web site generator written in ruby mit Ruby 8 years ago 6 years ago 14⭐️ DesiA blog platform written in JavaScript for developers, but with normal people in mind. apache-2.0 JavaScript 5 years ago 8 months ago 329⭐️ django-medusaA super simple “static site generator” Django app. (Unmaintained: see README for alternatives.) other Python 8 years ago 4 months ago 4⭐️ django-staticgenPush your django powered site to Amazon S3. bsd-3-clause Python 4 years ago 2 years ago 49⭐️ djangothisSimpleHTTPServer with Django steroids other Python 6 years ago a year ago 2984⭐️ DocPadEmpower your website frontends with layouts, meta-data, pre-processors (markdown, jade, coffeescript, etc.), partials, skeletons, file watching, querying, and an amazing plugin system. DocPad will streamline your web development process allowing you to craft powerful static sites quicker than ever before. MIT CoffeeScript 8 years ago 3 months ago 5⭐️ DoctaA new healing docs kit bsd-3-clause HTML 5 years ago 4 months ago 11180⭐️ DocusaurusDocusaurus makes it easy to build and maintain Open Source documentation websites. MIT JavaScript 2 years ago 3 months ago 206⭐️ DrapacheServe a website through your dropbox mit Python 7 years ago 7 months ago 9⭐️ drfredersonstatic website generator powered by PHP, YAML, Markdown and mustache mit PHP 6 years ago 5 months ago 8⭐️ Drop-a-SiteA Static-Site-Generator as simple as possible. mit PHP 7 years ago 4 years ago 6⭐️ dropbox-blogA static blog generator powered by Python and Dropbox Python 7 years ago 4 months ago 1599⭐️ DroppletsWelcome to an easier way to blog. - A minimalist markdown blogging platform. MIT PHP 6 years ago 3 months ago 3⭐️ DropPressYet another static blog generator JavaScript 7 years ago 5 years ago 38⭐️ DrupanDrupan is a flexible static site generator helping you to create blogs, single page applications or traditional websites BSD-2-Clause Python 8 years ago 6 months ago 11⭐️ DSSGA static site generator written in D mit D 2 years ago 7 months ago 7⭐️ DSWanother static web site generator in a shell script Shell 5 years ago a year ago 1⭐️ dwttoolA tool to manage websites based on Dynamic Web Templates mit D 2 years ago a year ago 45⭐️ dynamicmaticDynamic StaticMatic pages with Sinatra. mit Ruby 10 years ago 4 months ago 48⭐️ EasystaticEasystatic is a simple static site generator that uses Markdown for web pages, EJS and Postcss for layout, Browsersync for cross-device testing… apache-2.0 JavaScript 3 years ago 8 months ago 21⭐️ ecstaticecstatic creates static web pages and blog posts from Hiccup templates and Markdown. EPL-1.0 Clojure 6 years ago 2 years ago 2⭐️ electroStatic site generator Perl 6 years ago 3 years ago 2030⭐️ EleventyA simpler static site generator. An alternative to Jekyll. Transforms a directory of templates (of varying types) into HTML. mit JavaScript 2 years ago 3 months ago 3⭐️ ElyseA simple static website generator. mit Python 7 years ago 2 years ago 10⭐️ embelliha lightweight static website generator bsd-2-clause JavaScript 6 years ago a year ago 88⭐️ enfield[Not Maintained!] Jekyll-like static site generator for node.js mit CoffeeScript 7 years ago a year ago 22⭐️ EngineerA static website generator written in Python. mit Python 7 years ago 2 years ago 12⭐️ EquiproseA static website and blog generator in Node.js MIT JavaScript 6 years ago a year ago 6⭐️ FairytaleSinatra-like static site compiler. mit Ruby 7 years ago 2 years ago 11⭐️ FantasticWindmillA static web site generator for PHP programmers gpl-3.0 PHP 6 years ago 6 months ago fBlog Fortran Firedrop2The Blog Tool for Discerning Pythoneers BSD-3-Clause Python Firmant BSD-3-Clause Python 1⭐️ fjordA static blog generator. other Python 7 years ago 5 years ago 2⭐️ FlamelA static site generator written in F#. mit F# 5 years ago 2 years ago 70⭐️ FledermausBatman’s toolbelt for static site generation mit JavaScript 4 years ago 5 months ago 15⭐️ flimLightweight, static page generator mit JavaScript 6 years ago 10 months ago 12⭐️ FloydStatic website generator with MVC content system and built-in support for cloud hosting platforms bsd-2-clause Python 7 years ago 3 years ago FMPPFreeMarker-based text file PreProcessor Apache-2.0 Java 2⭐️ ford.py(Yet Another) Static Blog Generator in Python mit Python 6 years ago 5 years ago 423⭐️ FrankStatic Site Non-Framework mit Ruby 9 years ago 5 months ago 129⭐️ FranklinA static site framework for online books. MIT Ruby 5 years ago 4 months ago 628⭐️ FrogFrog is a static web site generator implemented in Racket, targeting Bootstrap and able to use Pygments. MIT Racket 6 years ago 3 months ago 583⭐️ Frozen-FlaskFreezes a Flask application into a set of static files. other Python 9 years ago 3 months ago 11⭐️ FunnelFunnel is a markdown to static html website generator. For Dropbox and gh-pages lightweight websites. Also has blogs! gpl-3.0 Python 7 years ago a year ago 9⭐️ GaroozisA .net blog and website generator using Razor templates to generate static html pages. Publish your site on Amazon S3. Also comes with its own dev server. other F# 8 years ago 2 years ago 33399⭐️ GatsbyBuild blazing fast, modern apps and websites with React mit JavaScript 4 years ago 3 months ago 11⭐️ GenA very simple static site generator using Twig. mit PHP 7 years ago 2 years ago 5⭐️ GenesisStatic site generator using Jade, Stylus and Grunt.js JavaScript 6 years ago 5 years ago 7⭐️ GerablogGerablog is a static blog generator. Write your posts in in Markdown, publish in HTML. Just two commands (create and generate) and seven templates (header, footer, categories page, categories block, main index, post and feed). mit Ruby 3 years ago 8 months ago 1⭐️ gettheshitdoneA simple website creating tool, package, framework whatever. mit JavaScript 6 years ago 5 years ago 178⭐️ ghost-renderRender static blog sites from Markdown using Ghost themes JavaScript 5 years ago 3 months ago GitHub PagesWebsites for you and your projects, hosted directly from your GitHub repository. Just edit, push, and your changes are live. GitLab PagesHost your static websites on GitLab.com for free, or on your own GitLab Enterprise Edition instance. 76⭐️ GlyphA Ruby-powered Rapid Document Authoring Framework other Ruby 10 years ago 2 years ago 23⭐️ Go-Static!A static site generator for Yeoman mit JavaScript 6 years ago 2 years ago 23⭐️ goblogA static blog generator implemented in Go. MIT Go 6 years ago a year ago 17⭐️ GoldbargA static blog generator written in Python gpl-3.0 Python 9 years ago 2 years ago 251⭐️ gollum-siteStatic Site Generator for Gollum Wikis MIT Ruby 9 years ago 5 months ago 581⭐️ gorGolang编写的静态博客引擎 BSD-3-Clause Go 7 years ago 4 months ago 347⭐️ gostaticFast static site generator isc Go 7 years ago 4 months ago 143⭐️ GrainGrain is a lightweight and powerful static website generator with custom themes to help create static, SEO-friendly websites or a blog in no time. (Apache-2.0 AND MIT) Groovy 6 years ago 4 months ago 10666⭐️ GravModern, Crazy Fast, Ridiculously Easy and Amazingly Powerful Flat-File CMS mit PHP 5 years ago 3 months ago 259⭐️ Grav Administration PanelGrav Admin Plugin mit CSS 4 years ago 4 months ago 106⭐️ GrazeStatic site generator using Razor. MIT C# 7 years ago 3 months ago 49⭐️ grenderA different take on a static site generator BSD-3-Clause Go 7 years ago a year ago 23⭐️ Griffinspeedy and simplistic static site generator. apache-2.0 Java 4 years ago 10 months ago 278⭐️ GrowA fresh, declarative, file-based microsite generator for rapid, high-quality website production. mit Python 6 years ago 3 months ago 63⭐️ Growlpython based, easy extendable, blog aware, static site generator GPL-2.0 Python 10 years ago a year ago 25⭐️ grunt-generatorsimple static site generator for grunt mit JavaScript 7 years ago 8 months ago 488⭐️ GSLiMatix GSL code generator gpl-3.0 C 9 years ago 4 months ago 34⭐️ guetzliA deliciously fast and simple Filesystem-Web-CMS (LGPL-3.0 OR GPL-3.0) Python 4 years ago a year ago 47⭐️ gulp-ssgStatic site generator plugin for gulp MIT JavaScript 5 years ago 2 years ago 8⭐️ Gustav BSD-3-Clause PHP 4 years ago 6 months ago 5⭐️ habitA simple static site generator based on Nunjucks, LESS, and Markdown. MIT JavaScript 5 years ago 4 months ago 10⭐️ haggisA static site generator with blogging/comments support other Haskell 6 years ago a year ago 1931⭐️ HakyllA static website compiler library in Haskell other Haskell 10 years ago 3 months ago 12⭐️ HalwaSingle file static site generator written in Python mit Python 6 years ago 2 years ago HammerSuper-charge your web development with Hammer. Still using PHP includes for HTML? You’re going to love hammer. 11⭐️ hanayonode.js static blog generator JavaScript 4 years ago 4 months ago handcrank Python 23⭐️ HandleA static site generator powered by PHP and the command line mit PHP 3 years ago a year ago 12⭐️ handroll:bento: A website generator for software artisans bsd-2-clause Python 5 years ago 6 months ago 84⭐️ happyplanUNMAINTAINED. I can only recommend you to checkout this brand new static website generator https://phenomic.io mit JavaScript 6 years ago 5 months ago 291⭐️ HarmonicThe next static site generator mit JavaScript 5 years ago 3 months ago 162⭐️ haroopressA static site generator built with Node.js, “Haroo” means “a day” Support Markdown presentation, Syntax Highlight, Themes MIT JavaScript 7 years ago 4 months ago 4772⭐️ HarpStatic Site Server/Generator with built-in preprocessing MIT JavaScript 7 years ago 3 months ago 150⭐️ Hastiea static site generator written in Go mit Go 7 years ago 4 months ago 344⭐️ HeckleJavaScript-based Jekyll clone other JavaScript 7 years ago 3 months ago 13⭐️ Helpful SiteBeautifully simple static site generator in Python unlicense Python 5 years ago 3 years ago 25922⭐️ HexoA fast, simple & powerful blog framework, powered by Node.js. mit JavaScript 7 years ago 3 months ago 27⭐️ HiDStatic website generation system Artistic-1.0-Perl Perl 7 years ago a year ago 2856⭐️ High VoltageEasily include static pages in your Rails app. mit Ruby 10 years ago 3 months ago 10⭐️ Hobixcommandline blogging & static pages & ruby. other Ruby 10 years ago 2 years ago 8⭐️ HSCHTML Sucks Completely, an HTML preprocessor gpl-2.0 C 8 years ago 3 months ago 25⭐️ hublohublo: the world needed another site generator isc Emacs Lisp 4 years ago a year ago 2845⭐️ HubPressA web application to build your blog on GitHub mit CSS 4 years ago 3 months ago 33973⭐️ HugoThe world’s fastest framework for building websites. apache-2.0 Go 6 years ago 3 months ago 1494⭐️ HydeA Python Static Website Generator mit Python 9 years ago 4 months ago 3⭐️ HydrasticA static site generator. Symfony Components, Twig, PHP >5.3. mit PHP 8 years ago 4 years ago 3⭐️ igapyonLightweight and turnkey static site/blog generator for developers Apache-2.0 Java 4 years ago 10 months ago 17⭐️ igorIgor is a static blog generator written in python mit Python 10 years ago 4 years ago Ikiwiki 897⭐️ inkAn elegant static blog generator Go 4 years ago 3 months ago 45⭐️ ipsum generaStatic blog generator written in Nim mit Nimrod 6 years ago 7 months ago 3⭐️ JAGSSJust another generator for static sites mit Python 7 years ago 4 years ago 752⭐️ JBakeJava based open source static site/blog generator for developers & designers. mit Java 6 years ago 3 months ago 121⭐️ JedieStatic site generator written in golang mit Go 6 years ago 7 months ago 6⭐️ JekxllA jekyll like static blog generator with Hoa PHP 7 years ago 3 years ago 53⭐️ JekydeStatic blog generater, server and writer. Write markdown with latex in desktop/mobile browsers. MIT JavaScript 6 years ago 7 months ago 37290⭐️ Jekyll:globe_with_meridians: Jekyll is a blog-aware static site generator in Ruby mit Ruby 11 years ago 3 months ago 1934⭐️ Jekyll AdminA Jekyll plugin that provides users with a traditional CMS-style graphical interface to author content and administer Jekyll sites. mit JavaScript 3 years ago 3 months ago 5862⭐️ Jekyll NowBuild a Jekyll blog in minutes, without touching the command line. mit CSS 5 years ago 3 months ago 1⭐️ JekytrumSimple static site generator Scala 5 years ago 4 years ago 3⭐️ jem-pressAn easy-to-use static site generator. GPL-3.0 CSS 5 years ago a year ago 4⭐️ Jen[defunct] node.js static site generator with a live updating development server - still in early stages - see rfunduk/blog for an example site that uses this mit CSS 8 years ago 2 years ago 1112⭐️ jigsawSimple static sites with Laravel’s Blade mit PHP 4 years ago 3 months ago 7⭐️ jinjetStatic generator for Jinja2 templates with localized site tree at / for each available localization. isc Python 7 years ago 6 years ago 135⭐️ jkl[DEPRECATED] a static site generator written in Go based on Jekyll mit Go 7 years ago 5 months ago 74⭐️ JottMinimum viable blog generator. Markup in Jade. Static HTML output. unlicense JavaScript 6 years ago 9 months ago 434⭐️ JournoA quick-and-dirty (literate) blogging engine MIT CoffeeScript 6 years ago 4 months ago 767⭐️ JrJr. the static, static javascript site generator (you should see this) MIT HTML 5 years ago 4 months ago 16⭐️ jssgJava static site generator MIT Java 6 years ago a year ago 2⭐️ jstaticoStatic site generation through file tree transformation mit JavaScript 5 years ago 5 months ago 30⭐️ kalastatic:electric_plug: Facilitate the front-end experience through Styleguides and Prototypes MIT JavaScript 5 years ago 4 months ago 15⭐️ KelA ridiculously simple markdown file-based website system built on Express, CoffeeScript and NodeJS MIT CoffeeScript 7 years ago 3 years ago 81⭐️ KerouacPoetic static site generator for Node.js. mit JavaScript 7 years ago 10 months ago 3⭐️ KicksterBoilerplate with Node-Sass, Grunt, Bower, Browser-Sync etc. MIT JavaScript 5 years ago a year ago 36⭐️ KirbyKirby’s demo site is the easiest way to get started with Kirby. PHP a year ago 3 months ago 11⭐️ kkrKukuruz - static site generator similar to Jekyll in Go. bsd-2-clause Go 6 years ago 9 months ago 73⭐️ KormaGit based blog? Ruby 10 years ago 6 months ago 73⭐️ Kulfon:japanese_ogre: :frog: JavaScript static site generator with Org Mode & Markdown support (α) :boom: other JavaScript 3 years ago 3 months ago 109⭐️ Lambda PadStatic site generator using Erlang. Yes, Erlang. apache-2.0 Erlang 5 years ago 8 months ago 15⭐️ LannisterStatic web generator CC-BY-SA-3.0 Go 8 years ago 2 years ago 10⭐️ LanyonStatic Site Generator bsd-3-clause Python 9 years ago 3 years ago 2⭐️ larassgStatic Site Generator for Laravel PHP 5 years ago 5 years ago Latemp Perl 4⭐️ lava[UNMAINTAINED] Please use Cobalt instead mit Rust 5 years ago 3 years ago 3⭐️ LazeA simple static site generator mit Ruby 9 years ago 2 years ago 138⭐️ lazyblorgBlogging with Org-mode for very lazy people gpl-3.0 Python 6 years ago 3 months ago 135⭐️ LeefletsBeautifully Simple Websites MIT PHP 7 years ago 7 months ago 3017⭐️ LektorThe lektor static file content management system other Python 4 years ago 3 months ago 96⭐️ LenscapLenscap is a static site generator for creating beautiful photo narratives bsd-2-clause Python 5 years ago 6 months ago Leo GPL-2.0 C++ 676⭐️ LetterpressA minimal, Markdown based blogging system written in Python. other Python 6 years ago 5 months ago 18⭐️ lettersmith[DEPRECATED] A simple, flexible static site generator based on plugins Lua 5 years ago a year ago 90⭐️ lightningstatic site generator MIT Python 7 years ago 6 months ago 328⭐️ liquidluckFelix Felicis (aka liquidluck) is a static blog generator in python Python 8 years ago 4 months ago 15⭐️ LogyaLogya is a static website generator written in Python designed to be easy to use and flexible. mit Python 8 years ago 3 months ago 150⭐️ Luapress:newspaper: Static site/blog generator written in Lua mit Lua 6 years ago 4 months ago 31⭐️ MachinedA static site generator and Rack server built using Sprockets 2.0 mit Ruby 8 years ago 5 months ago 41⭐️ MadokoOne-time import of https://madoko.codeplex.com apache-2.0 JavaScript 5 years ago 8 months ago 124⭐️ MagnetoMagneto is a static site generator. mit Ruby 7 years ago 6 months ago 64⭐️ makebakeryA static website generator built on GNU Make AGPL-3.0+ WITH Autoconf-exception-3.0 Makefile 8 years ago 6 months ago 10⭐️ makeblogMinimalistic blog generator with social media integration bsd-2-clause Python 7 years ago 6 years ago 18⭐️ Makefly[ABANDONNED] Makefly is a fast and lightweight command line alternative to Nanoblogger static weblog engine GPL-3.0 Lua 7 years ago 9 months ago 606⭐️ makesite.pySimple, lightweight, and magic-free static site/blog generator for Python coders mit Python a year ago 3 months ago 97⭐️ Markbox[DEPRECATED] A blogging engine for Dropbox based on Markdown Python 7 years ago a year ago 348⭐️ MarkdocA lightweight Markdown-based wiki system. Current status: abandoned. unlicense Python 9 years ago 5 months ago 1320⭐️ markdown-stylesMarkdown to static HTML generator and multiple CSS themes for Markdown BSD-3-Clause HTML 6 years ago 3 months ago 132⭐️ Markxmarkdown + code => html mit JavaScript 7 years ago 5 months ago 10⭐️ massimoMassimo is a static website builder. mit Ruby 10 years ago 2 years ago Maven Site PluginThe Site Plugin is used to generate a site for the project. The generated site also includes the project’s reports that were configured in the POM. Apache-2.0 Java 2711⭐️ mdwikiCMS/Wiki system using Javascript for 100% client side single page application using Markdown. AGPL-3.0 JavaScript 6 years ago 3 months ago 56⭐️ MechaStable version. gpl-3.0 PHP 5 years ago 6 months ago 4⭐️ meinhofstatic site generator in PHP 5.3 using symfony components mit PHP 7 years ago 4 years ago 7185⭐️ metalsmithAn extremely simple, pluggable static site generator. MIT JavaScript 5 years ago 3 months ago 6⭐️ MibloMiblo is simple blog-aware static site generator writen in PHP 5.3 and using Twig. BSD-2-Clause PHP 7 years ago 6 years ago 6431⭐️ MiddlemanHand-crafted frontend development mit Ruby 10 years ago 3 months ago 32⭐️ MinimalPersonal static website and blog generator for Go, Node.js and Python. No external dependencies and requires only a few hundred lines of code to run. mit JavaScript 3 years ago 4 months ago 196⭐️ MinoriWikiMinoriWiki is a static Wiki site Generator mit JavaScript 4 years ago 3 months ago 39⭐️ Misakai BakerBaker is a static site generator for C# / .Net people. apache-2.0 C# 5 years ago 5 months ago 330⭐️ misakiJekyll inspired static site generator in Clojure other Clojure 7 years ago 4 months ago 7421⭐️ MkDocsProject documentation with Markdown. bsd-2-clause Python 6 years ago 3 months ago 9⭐️ mksiteDeclarative static site generator other Awk 6 years ago 8 months ago 48⭐️ monkeymanSimple static site generator for Scala and middleman lovers gpl-2.0 Scala 7 years ago 6 months ago 36⭐️ MulderA simple static site generator written in C#. mit C# 7 years ago 4 months ago 12⭐️ MuleifyA zero configuration static site generator and asset preprocessor compiler. mpl-2.0 JavaScript 3 years ago 5 months ago 360⭐️ myntA static site generator. other Python 8 years ago 4 months ago NanoBlogger GPL-3.0 Shell 1723⭐️ NanocA powerful web publishing system Ruby 12 years ago 3 months ago 8⭐️ nanogenA very small static site generator written in Python. mit Python 5 years ago 6 months ago 899⭐️ NestaCMSA lightweight CMS, implemented in Sinatra. mit Ruby 11 years ago 4 months ago Netlify DropDrag and drop a folder with your site’s HTML, CSS, and JS files. We’ll publish them live and give you a link to share it. 2⭐️ NeverlandNeverland is a static blog generator with Clojure and Emacs Org mode. JavaScript 7 years ago 4 years ago Newcomen GPL-3.0 Perl 15⭐️ nibstatic site generator with content pipeline mit Python 7 years ago 5 months ago 221⭐️ NibbleblogEasy, fast and free CMS Blog. All you need is PHP to work. other PHP 7 years ago 5 months ago 281⭐️ Niconico is a front-end friendly static site generator, best for web developers like you and me. bsd-3-clause JavaScript 7 years ago 5 months ago 1668⭐️ NikolaA static website and blog generator mit Python 7 years ago 3 months ago 77⭐️ node-blogThis repo is the code behind the blog HowToNode.org JavaScript 9 years ago 10 months ago 1⭐️ node-qssgQabex Static Site Generator (qssg) is (yet another) static website build system. mit JavaScript 6 years ago 5 years ago 3⭐️ nodeacheA static-webpage-generator based on node.js using mustaches, markdown and JSON. mit JavaScript 6 years ago a year ago 212⭐️ noflo-jekyllFlow-based reimplementation of the Jekyll static site generator MIT CoffeeScript 7 years ago 4 months ago 19208⭐️ Nuxt.jsThe Vue.js Framework other JavaScript 3 years ago 3 months ago 252⭐️ o-blogStandalone orgmode blog exporter. WTFPL Emacs Lisp 8 years ago 5 months ago 9⭐️ OakStatic blog generator written in Python wtfpl Python 9 years ago 2 years ago 318⭐️ obeliskStatic Site Generator written in Elixir. mit Elixir 5 years ago 4 months ago 16⭐️ OcamRazor and Markdown static site generator mit C# 7 years ago 4 months ago 1695⭐️ OctopressOctopress 3.0 – Jekyll’s Ferrari mit Ruby 6 years ago 4 months ago 9⭐️ onessgThe Static Site Generator that does only one thing: compile your html and markdown. mit JavaScript 3 years ago 3 months ago 4⭐️ operator-DD3Custom Site Generator unlicense Lua 5 years ago 4 years ago 105⭐️ OpooPressOpooPress framework is a java based blog aware static site generator. apache-2.0 Java 6 years ago 5 months ago 1⭐️ Orcaa simple static site generator Python 5 years ago 4 years ago 175⭐️ OrchidA beautiful and truly unique documentation engine and static site generator. lgpl-3.0 Java a year ago 3 months ago 17⭐️ Pagegen Python 4 years ago a year ago 36⭐️ Pagenpagen.js is a simple and customizable website generator for node.js. mit JavaScript 6 years ago 5 months ago 234⭐️ Pancake.ioStackem Up! mit Ruby 10 years ago a year ago 56⭐️ pansiteThese are the plain text files used to produce my personal website with pandoc and a bash shell script called pansite. CC-BY-SA-3.0 HTML 7 years ago 6 months ago Papery Artistic-1.0-Perl Perl 8673⭐️ PelicanStatic site generator that supports Markdown and reST syntax. Powered by Python. GPL-3.0 Python 9 years ago 3 months ago 3⭐️ PenguinA simple static page generator written in Python mit Python 4 years ago 3 years ago 302⭐️ PerunProgrammable static site generator built with Clojure and Boot epl-1.0 Clojure 4 years ago 4 months ago 267⭐️ PetrifyA flexible static site generator for node.js mit JavaScript 9 years ago 5 months ago 3188⭐️ PhenomicWebsites & apps static generator. Works React, Webpack, ReasonML and whatever you want ⚡️ mit JavaScript 4 years ago 3 months ago 3⭐️ PhileasA static site generator, using javascript and simple text files written in txt2tags (markdown ultra) syntax MIT JavaScript 5 years ago 6 months ago 45⭐️ PhlyBlogZF2 module for creating a static blog BSD-2-Clause PHP 7 years ago 10 months ago 242⭐️ PieCrustA simple PHP website engine and static file generator. Apache-2.0 PHP 8 years ago 3 months ago 14⭐️ PilcrowAn abandoned static site generator. mit Python 10 years ago a year ago 38⭐️ Pitha static website generator mit Ruby 9 years ago 9 months ago 31⭐️ PlainSitePlainSite:A Truely Hackable Static Site Generator. mit Ruby 6 years ago a year ago 58⭐️ PMBlogPHP版静态化博客程序,支持自定义主题和插件 PHP 6 years ago 6 months ago 563⭐️ poetA node.js blog engine mit JavaScript 7 years ago 4 months ago 15⭐️ PointlessA Static Blog Generator mit PHP 7 years ago 6 months ago 41⭐️ PoloStatic site generator written in Go and “compatible” with Jekyll & Pelican content mit Go 6 years ago 5 months ago 32⭐️ pooleA damn simple static website generator. This is a mirror of the repository hosted at BitBucket. GPL-3.0 Python 8 years ago 7 months ago 66⭐️ popA static site and blog generator mit JavaScript 8 years ago 2 years ago 17⭐️ PowerSiteA static page generator in .Net that is PowerShell friendly C# 5 years ago 4 months ago PPWizardUses classic REXX or Regina interpreter REXX 567⭐️ PretzelA site generation tool (and then some) for .NET platforms ms-pl C# 7 years ago 4 months ago PrimocaStatic Website Hosting and CDN Web PrismicA hosted, API based and developer friendly CMS backend. We take care of upgrades, scalability and security. 16⭐️ PropellerStatic Site Generator in Assemble and Bootstrap 3 mit ApacheConf 6 years ago 2 years ago ProseProse is a content editor for GitHub designed for managing websites. 87⭐️ Prosopopeea static website generator to make beautiful customizable pictures galleries that tell a story gpl-3.0 Python 4 years ago 4 months ago Pulse CMSThe easiest flat-file CMS for designers and their clients. PHP 1197⭐️ PunchA fun and easy way to build modern websites mit JavaScript 7 years ago 4 months ago 62⭐️ PyBlosxomPyblosxom file-based blogging engine other Python 8 years ago 5 months ago 27⭐️ PyBlueStatic website generator for sciency people other Python 6 years ago 3 months ago 28⭐️ PyllA Python-Powered Static Site Generator bsd-3-clause Python 8 years ago 3 years ago 11⭐️ QgodaQgoda (pronounce: yagoda!) is an extensible static site generator with arbitrary taxonomies and cross-links and a strong focus on multilanguage facilities. other Perl 3 years ago 4 months ago Quietly Confident Artistic-1.0-Perl Perl 52⭐️ QuillQuill is a simple blog engine inspired by Jekyll. Quill runs on node and has an easy command line interface. Themeing is as simple as editing a single html page. Beerware JavaScript 7 years ago 5 months ago 1⭐️ RakeWeb Apache-2.0 Ruby 7 years ago 6 years ago 9⭐️ rantCLI driven, blog aware static site generator in Python other Python 8 years ago 2 years ago 5⭐️ RassmalogStatic blogging with YAML and eRuby isc Ruby 10 years ago a year ago 41⭐️ rawkrage against web frameworks - posix shell static site generator other Roff 8 years ago 5 months ago 24⭐️ reacatUse React to generate your awesome static website other JavaScript 5 years ago 4 months ago 27⭐️ react-static(not maintained) React static site generator framework for Node.js mit JavaScript 4 years ago 5 months ago 387⭐️ react-static-siteAn experiment in generating a static site from react JavaScript 5 years ago 3 months ago 12⭐️ reactivateA static site generator using isomorphic react MPL-2.0 CoffeeScript 4 years ago 2 years ago 5037⭐️ Read the Docs (RTD)The source code that powers readthedocs.org mit Python 9 years ago 3 months ago 1⭐️ Really StaticPort of http://wordpress.org/plugins/really-static/ gpl-3.0 PHP 5 years ago 5 years ago 3⭐️ RefrainRefrain from unnecessary building. MIT JavaScript 5 years ago 3 years ago regenerate Lisp 263⭐️ reptarstatic sites that roar mit JavaScript 4 years ago 4 months ago 19⭐️ Rijigit based simple blog tool Artistic-1.0-Perl Perl 6 years ago 2 years ago 24⭐️ RizzoA static site generator, written in Groovy. mit Groovy 8 years ago 2 years ago 160⭐️ romulusBuilding static empires with node.js. mit JavaScript 7 years ago a year ago 1477⭐️ Rootsa toolkit for rapid advanced front-end development other CoffeeScript 7 years ago 4 months ago 120⭐️ RosidJust-in-time development server and static site generator. mit JavaScript 4 years ago 4 months ago 203⭐️ rstblogNIH site generator other Python 9 years ago 4 months ago 14⭐️ RubyFrontierTextMate bundle for generating static Web sites MIT Ruby 8 years ago 2 years ago 636⭐️ Ruhoh MIT Ruby 7 years ago 8 months ago 10⭐️ s2genA simple static site generator written in Scala mit Scala 3 years ago 6 months ago 1296⭐️ SculpinSculpin — Static Site Generator mit PHP 8 years ago 3 months ago 837⭐️ Second CrackA static-file Markdown blogging engine. other PHP 8 years ago 4 months ago 117⭐️ serifSerif is a static site generator and blogging system powered by markdown files. mit Ruby 7 years ago 4 months ago 2⭐️ serious-chickenA static site generator mit Python 7 years ago 5 years ago 846⭐️ ServeServe is a small Rack-based web server and rapid prototyping framework for Web applications (specifically Rails apps). Serve is meant to be a lightweight version of the Views part of the Rails MVC. This makes Serve an ideal framework for prototyping Rails applications or creating simple websites. Serve has full support for Rails-style partials and layouts. other Ruby 11 years ago 5 months ago 4⭐️ SGStatic site generator in PHP other PHP 7 years ago 3 years ago 1⭐️ sg.pyA small, static website generator written in Python. GPL-3.0 Python 9 years ago 6 months ago 2⭐️ SGGA Static Site Generator Ruby 5 years ago 3 years ago 4⭐️ shelobStatic site generator extension for Smeagol other Ruby 7 years ago a year ago 3⭐️ ShireBlog aware static site generator similar to Jekyll Apache-2.0 Java 7 years ago 10 months ago 9⭐️ ShonkuWe remember his diary, a static blogging tool. gpl-3.0 Go 5 years ago 4 months ago 635⭐️ SilexSilex is a static website builder in the cloud. gpl-3.0 JavaScript 6 years ago 3 months ago 923⭐️ SimikiSimiki is a simple wiki framework, written in Python. mit Python 6 years ago 3 months ago 869⭐️ SimpleA static blog generator with a single static page mit JavaScript 5 years ago 4 months ago 29⭐️ simple-staticStatic site generator based on sw Awk 7 years ago 5 months ago 40⭐️ simple-websitesimple-website – A zero-configuration static site generator written in Go Go 5 years ago 6 months ago 9⭐️ simsalabashHypertext mechanics in Bash mit Shell 8 years ago 2 years ago 26⭐️ Site builderSlightly less crappy PHP static site generator mit PHP 8 years ago 10 months ago Site builder console mit C# 2 years ago 2 years ago Site44 Web 31⭐️ SiteGen (Dart)Sitegenerator for Dart (Apache-2.0 AND BSD-3-Clause) Dart 4 years ago 5 months ago 49⭐️ Sitegen (MoonScript)static site generator in MoonScript MIT Lua 8 years ago 5 months ago 39⭐️ sitioimperative static site generator powered by React and browserify mit JavaScript 3 years ago 3 months ago 13⭐️ Smallest BloggerWith Markdown and Guard mit CSS 5 years ago 4 years ago 7⭐️ snowshoeA (nother) static site generator in PHP PHP 8 years ago 8 months ago 36⭐️ SoapboxStatic site generator as an SBT plugin lgpl-2.1 Scala 6 years ago 6 months ago 34⭐️ Socratessocrates is a static site generator written in Python other Python 8 years ago 4 months ago 3⭐️ sortastaticAnother attempt at a sorta-static site generator bsd-2-clause Go 5 years ago 2 years ago 42⭐️ SpeechhubStatic blog engine gpl-3.0 Python 7 years ago 4 years ago 129⭐️ SpeltDelightfully simple static site generator written in Swift [NO LONGER MAINTAINED] mit Swift 3 years ago 4 months ago 27⭐️ SPGStatic Page Generator based on Nashorn, Java 8 and Mustache apache-2.0 JavaScript 5 years ago 6 months ago 2481⭐️ SphinxMain repository for the Sphinx documentation builder BSD-2-Clause Python 5 years ago 3 months ago 460⭐️ Spike:warning: UNMAINTAINED :warning: A modern static build tool, powered by webpack other JavaScript 3 years ago 4 months ago 1636⭐️ SpinaSpina CMS other HTML 4 years ago 3 months ago 3⭐️ SpineA framework for building sites that don’t change very often mit Java 5 years ago a year ago 359⭐️ SpressPHP Static site generator mit PHP 6 years ago 5 months ago 1⭐️ Spring BoardA lightweight static blog generator for Node.js MIT JavaScript 6 years ago 5 years ago 10⭐️ SquidA static blog site generator built on Flask Python 7 years ago a year ago 7⭐️ SquirrelAnother Static Site Generater Python 5 years ago 2 years ago 2⭐️ SSCA static site generator inspired by Jekyll, written in NodeJS. A learning exercise :D JavaScript 7 years ago 5 years ago 1054⭐️ StaceyCheap & easy content management mit PHP 10 years ago 4 months ago 1⭐️ StackticA Static Site Generator for Node.js mit 5 years ago 2 years ago 4⭐️ StadøSimple, single-file static site generator powered by python scripts MIT Python 6 years ago 4 years ago 39⭐️ StaGenStatic site generator from WizTools.org apache-2.0 Java 5 years ago 7 months ago 699⭐️ StasisStatic sites made powerful mit Ruby 8 years ago 5 months ago 237⭐️ Stasis.cljSome Clojure functions for creating static websites. EPL-1.0 Clojure 6 years ago 4 months ago StatamicThe friendliest CMS you’ll ever meet. Powered by flat files, your favorites content parsers, and the raw power of nature. PHP 15⭐️ StatiA static site generator written in PHP that can work with any (most) existing Jekyll site, but can be easily extended in your (and my) favorite programming language, PHP! mit PHP 2 years ago 5 months ago 175⭐️ staticStatic Site Generator EPL-1.0 Clojure 9 years ago 4 months ago 1037⭐️ Static Site BoilerplateA better workflow for building modern static websites. mit JavaScript 7 months ago 3 months ago 132⭐️ Static Website Starter KitStatic Website Starter Kit (static site generator) powered by Gulp, Jade, Bootstrap, LESS and BrowserSync. It can automatically deploy your website to GitHub Pages via Travis CI. apache-2.0 JavaScript 5 years ago 6 months ago 2⭐️ static-ioStatic Website Generator with Jade, Bourbon and Neat mit JavaScript 4 years ago 3 years ago 2⭐️ Static-weberStatic website generator (Python + Mako) mit Python 5 years ago 4 years ago 6⭐️ static2000The simple static site generator built on node.js mit JavaScript 5 years ago 7 months ago 186⭐️ staticjinjaMinimalist Python library for building static sites w/ Jinja2 mit Python 7 years ago 3 months ago 269⭐️ StaticMaticThe Lightweight Static Content framework mit Ruby 11 years ago a year ago 56⭐️ StaticMatic2The lightweight static content framework. mit Ruby 8 years ago 2 years ago 3⭐️ staticpressBlog-focused static site generator MIT Ruby 8 years ago 2 years ago 2⭐️ staticpyA python static site generator mit Python 6 years ago a year ago 1⭐️ staticsitepublishes a static site to s3 BSD-3-Clause Haskell 7 years ago 6 years ago Staticsmoothie MIT Go StaticVolt Artistic-1.0-Perl Perl 204⭐️ StatikMulti-purpose static web site generator aimed at developers. mit Python 3 years ago 4 months ago Statix Shell 64⭐️ StatoclesStatic website CMS Artistic-1.0-Perl Perl 5 years ago 5 months ago 61⭐️ StogXML documents and web site compiler. gpl-3.0 OCaml 8 years ago 10 months ago 43⭐️ StrangeCaseIt’s yet another static site generator. Have you seen jekyll? hyde? Yup. Like those. other Python 7 years ago 7 months ago 3⭐️ straticYet another static site generator. mit JavaScript 5 years ago 2 years ago 2⭐️ StrikerA Simple & Fast Static Site Generator mit Ruby 6 years ago 2 years ago SurgeShipping web projects should be fast, easy, and low risk. Surge is static web publishing for Front-End Developers, right from the CLI. 41⭐️ SusiSimplest. Static page generator. Ever. isc JavaScript 5 years ago 4 months ago 12⭐️ SWGStatic Website Generator gpl-3.0 Python 8 years ago 4 months ago 4⭐️ SWSGA tool to generate static HTML pages using templates and markup sources wtfpl Python 9 years ago 2 years ago 1⭐️ SzyslakGrunt-based static site generator with an attitude gpl-3.0 JavaScript 6 years ago 3 years ago Tacot MIT Python 188⭐️ TagsThe simplest static site generator mit Python 5 years ago 4 months ago 3⭐️ TagyFully customizable structure and any params MIT Python 5 years ago a year ago 8⭐️ TahcheeA simple static website build system other Python 10 years ago a year ago 18⭐️ TapestryPHP static site generator using the plates template system mit PHP 3 years ago 5 months ago 154⭐️ TarbellA Flask-based static site authoring tool. MIT Python 7 years ago 3 months ago tclogWeblog generator with a GUI editor. Uploads entries via FTP. Features: RSS 1.0, HTML templates, TrackBack, wiki-like formatting, Namazu index creation. BSD-3-Clause Tcl 16 years ago 39⭐️ TclssgA static site generator (being rebuilt) mit Tcl 5 years ago 4 months ago 244⭐️ TechyA flat file CMS based on Gulp and AbsurdJS mit JavaScript 5 years ago 3 months ago 53⭐️ TemplerA modular extensible static-site-generator written in perl. Artistic-1.0-Perl Perl 7 years ago 3 months ago Text AssemblerA static website generator written in C++ integrating the Google V8 Javascript engine JavaScript 6⭐️ ThotA Python-Powered Static Site Generator Python 8 years ago a year ago 282⭐️ TinkererPython blogging engine BSD-2-Clause-FreeBSD Python 6 years ago 4 months ago 1408⭐️ totothe 10 second blog-engine for hackers mit Ruby 10 years ago 4 months ago 41⭐️ TriboExtremely fast static site generator written in Objective-C other Objective-C 8 years ago 5 months ago 130⭐️ trofafSuper simple live static blog generator in Go. Vraiment trofaf. bsd-3-clause Go 6 years ago 5 months ago Ultra simple Site Maker GPL-3.0 Shell 170⭐️ UrubuA micro CMS for static websites, with a focus on good navigation practices. GPL-3.0 HTML 6 years ago 4 months ago 14⭐️ UttersonA static site generator written in Clojure in the style of Jekyll & Hyde. Clojure 10 years ago 2 years ago 32⭐️ Uttersona minimal static blog generator written using old-school unix tools (make, ksh, m4, awk, procmail and a pinch of elisp) agpl-3.0 Shell 10 years ago 4 months ago 33⭐️ veevee is a highly portable, zero configuration (but configurable), cli microblogging tool. wtfpl Shell 8 years ago 6 months ago 12⭐️ VegetablesStatic site generator, and more… mit JavaScript 4 years ago a year ago 3354⭐️ vimwikiPersonal Wiki for Vim MIT Vim script 7 years ago 3 months ago 45⭐️ VoldemortA simple static site generator using Jinja2 and Markdown templates. apache-2.0 Python 8 years ago 4 months ago 43⭐️ voltVersatile static website generator BSD-3-Clause Python 7 years ago 7 months ago VoodooPadMacOS/iOS application for editing notes. Has a static site publishing functionality extensible via JavaScript. App 6⭐️ wadooAn XML/XSLT static site generator written in PHP. other PHP 7 years ago 4 years ago 5⭐️ WallflowerSorry I can’t dance, I’m holding on to my friend’s purse Artistic-1.0-Perl Perl 7 years ago 10 months ago 6⭐️ WannaNOT MAINTAINED ANY MORE.Wanna is a blog-aware, static site generator in Node.js. JavaScript 7 years ago 4 years ago 3⭐️ WeaverA DSL for generating static sites mit JavaScript 4 years ago 4 months ago 273⭐️ WebbyNO LONGER UNDER DEVELOPMENT MIT Ruby 11 years ago 6 months ago 94⭐️ webgenwebgen is a fast, powerful and extensible static website generator GPL-3.0 Ruby 11 years ago 3 months ago 2⭐️ webgenSimple PHP CLI generator of static sites other HTML 7 years ago 4 months ago 652⭐️ WebhookWebhook Command Line Interface. mit JavaScript 5 years ago 4 months ago 4⭐️ WebsleydaleA narrow-minded but very fast static site generator mit Python 6 years ago a year ago 1361⭐️ WheatWheat is a blog engine for coders written in node.JS other JavaScript 9 years ago 5 months ago 17⭐️ WikismithFun static site generator for fans of gulp and bower MIT JavaScript 5 years ago 3 years ago 3452⭐️ WintersmithA flexible static site generator mit CoffeeScript 7 years ago 3 months ago 115⭐️ wokA static website generator - Toss some content, templates, and media in a pan and fry it up! other Python 8 years ago 5 months ago 115⭐️ WoodsNode.js file based CMS inspired by Kirby & Stacey. other JavaScript 6 years ago 4 months ago 1⭐️ wordsisterReally simple Web page editing MIT PHP 6 years ago 4 years ago 405⭐️ WP2StaticWordPress plugin allowing static site generation w/ security, performance and cost benefits unlicense PHP 3 years ago 3 months ago 2⭐️ WPWMM4Web Page With Make and M4 - static web page generator other Makefile, m4 2 years ago 6 months ago 1010⭐️ WyamA modular static content and static site generator. mit C# 5 years ago 4 months ago 12⭐️ Yanaa simple static site generator MIT Python 5 years ago 6 months ago 3⭐️ yassgyet another static site generator using express.js and node.js BSD-3-Clause JavaScript 7 years ago 5 years ago 273⭐️ YellowDatenstrom Yellow is for people who make websites gpl-2.0 PHP 6 years ago 3 months ago Yggdrasil GPL-2.0 Perl 17 years ago 16 years ago 22⭐️ YozuchreStructuredText based static blog generator bsd-3-clause Python 5 years ago 4 months ago 271⭐️ ystcreate static websites from YAML data and string templates gpl-2.0 Haskell 10 years ago 3 months ago 84⭐️ zasMost zen static website generator in Golang. agpl-3.0 Go 6 years ago 5 months ago 60⭐️ ZenWeb MIT Ruby 10 years ago 4 months ago 3⭐️ ZineYet another blog aware static site generator mit Ruby 2 years ago 8 months ago 168⭐️ zodiacA static website generator written in awk and sh. mit Awk 8 years ago 4 months ago 1721⭐️ zolaYour one-stop static site engine. Forget dependencies. Everything you need in one binary. Previously called Gutenberg until WordPress took the name. mit Rust 3 years ago 5 months ago Zucchini Artistic-1.0-Perl Perl]]></content>
</entry>
<entry>
<title><![CDATA[静态网站(博客)生成器Static Site Generators(SSGs)大集合]]></title>
<url>%2F2019%2F07%2F14%2F%E9%9D%99%E6%80%81%E7%BD%91%E7%AB%99-%E5%8D%9A%E5%AE%A2-%E7%94%9F%E6%88%90%E5%99%A8Static-Site-Generators%EF%BC%88SSGs%EF%BC%89%E5%A4%A7%E9%9B%86%E5%90%88%2F</url>
<content type="text"><![CDATA[这是一份静态网站生成器的推荐集合,按开发语言或者平台分类和排序,大家各取所需。如果大家有用什么新的静态网站生成器未在本文章中列出,麻烦在评论中说下,我会不断更新该集合。 ClojureCryogen - A simple, static, automated CMS shipped as a template on Leiningen, a Clojure build tool. misaki - A Jekyll-inspired blog built in Clojure. GitFugitive - A blog engine running on top of git using hooks to generate static html pages. GoGostatic - A static site generator written in Go that tracks changes and works fast. Hugo - Flexibly works with many formats and is ideal for blogs, docs, portfolios and more. HaskellHakyll - A Haskell library that creates fast and secure small-to-medium sites and personal blogs. yst - Generates static sites by filling string templates with data from YAML or CSV text files or SQLite 3 file-based databases. JavaJBake - Structure your content any way you see fit with a Java-based site/blog generator. LispColeslaw - Aims to be flexible blogware that replaces single-user static compilers like Jekyll. Node.jsBlacksmith - Uses weld, jsdom and marked to turn JSON, Markdown and HTML/CSS into “awesome” static sites. Codex - A simple tool built in Jade and themed in Stylus that uses Markdown documents. DocPad - Allows for content management via the file system, rendering via plugins, and static site generation for deployment anywhere. Harp - Harp serves Jade, Markdown, EJS, CoffeeScript, Sass, LESS and Stylus as HTML, CSS & JavaScript—no configuration necessary. Hexo - A Node.js powered generator that supports multi-thread generating, allowing it to process hundreds of files in seconds. Metalsmith - An extremely simple site generator with logic completely handled by plugins. Poet - A blog generator in node.js that renders “markdown/jade/whatever posts” fast. Punch - Create simple, intuitive sites with a publishing framework built for ease-of-use. Roots - A light, super fast and intuitive static build system made for front end development and web apps. Wintersmith - A flexible, multi-platform static site generator built on top of node.js. PerlBlosxom - A full-featured weblog application with a tiny footprint. Dapper - Distributed as a Perl module, Dapper is a flexible (and fast) tool for creating static websites. PHPDropplets - Write and publish content with Markdown, while cuttings all the clutter. Phrozn - Static site generator written and extensible in PHP, designed with extensibility in mind. PieCrust - A static site generator and lightweight CMS that’s all managed with text files. Second Crack - “Unsuitable and unnecessary for nearly everyone,” but Marco Arment wrote and uses it to power Marco.org. Sculpin - Converts Markdown, Twig templates or standard HTML into static HTML sites. Stacey - A lightweight content management system managed simply by creating folders and editing text files. Templeet - Can easily generate HTML, CSS, SVG pictures, SMIL and text files. Tempo - Uses PHP-based templates, test files and images to build static HTML sites. Pythonacrylamid - A static blog generator with incremental rendering. Blatter - Uses dynamic templates and copied-over static files to create and publish static sites. Blogofile - A simple blogging engine using Python that requires no database and no special hosting environment. Cactus - A powerful static site builder using Python and the Django template system. Chisel - An incredibly lightweight (less than 8kb) blog generation utility. Flask - A micro-framework for Python based on Werkzeug, Jija 2 and “good intentions.” Hyde - Generates static websites with Python and Django that provide instant refresh and tons of flexibility. ikiwiki - Converts wiki pages into HTML pages for simple publishing. Jinja - A widely used and BSD licensed template engine for Python. Letterpress - A minimal blogging system built for a better writing experience. Mako - A template library written in Python that’s used by Reddit to deliver “over a billion page views per month.” Mynt - Designed to give all the features of a CMS with none of the rigid implementations. Nikola - A themable, flexible static site and blog generator with a tidy codebase PyGreen - Put all the files in a server, then it invokes the Mako template engine on all HTML files. Pelican - Creates completely static output that requires no database or server-side logic. Poole - A Markdown-driven static site generator that creates simple sites with navigation menus. Radpress - “A simple blog application for Djangonauts.” Socrates - A simple static site generator geared towards blogging. Sphinx - Makes it easy to create “intelligent and beautiful documentation.” Static Ninja - A library for easily deploying static sites using the jinja2 templating language. Wok - Similar to Jekyll, Hyde and nanoc, but with a restricted focus of features. RubyAssemble - The static site generator for Grunt.js, Yeoman and Node.js. :awestruct - A framework for creating static HTML files, inspired by Jekyll. Bonsai - Creates dynamic websites that focus on well-defined hierarchies without the need of a database or admin interface. Frank - Builds static sites using Tilt, with support for Haml & Sass, LESS, Builder, ERB, and Liquid. Hobix - A Ruby-based site generator for users who are “able to do a triple front-flip without using your legs at all.” Or else “you will go to Federal prison.” Jekyll - A “blog-aware” tool that quickly transforms your plain text (or Markdown) into static websites. Middleman - Uses all the shortcuts and tools in modern web development to generate static sites. MObtvse - A blogging platform for people who want a clean web interface for creating Markdown posts. Nanoc - Creates static sites of all kind with support for free-form metadata, various markup/templating languages and more. NestaCMS - Simple, easy code for developers and designers that is easily extended using the Sinatra web framework. Octopress - A “blog aware” static site generator based on mojombo/jekyll. Ruhoh - Similar to Jekyll and Nanoc, while giving you the tools to “publish websites like a BOSS.” Stasis - A powerful static website generator using Ruby. Toto - “The 10 second blog-engine for hackers.” Webby - Uses your favorite markup language to combine the contents of a page with a layout to produce HTML Webgen - Lets you concentrate on writing content with support for any markup and built-in helper tools. ScalaMonkeyman - A static site generator similar to Middleman that’s built in Scala. Not static, but in the same universeKirby - A PHP-built CMS that’s “easy to setup, easy to use, flexible as hell.” Statamic - A flat-file CMS built on PHP with a beautiful and responsive control panel designed with clients in mind. 参考 Chris Hall’s “The updated big list of static website generators for your site, blog or wiki“ Klint Finley’s ”5 Minimalist Static Blog Generators to Check Out” on Silicon Angle Thorpe Obazee’s ”Complete List of Static Site Generators for Python” on gist.pages BitBalloon’s static-site leaderboard, titled ”Top Static Site Generators Comparison” The metric ton of comments and tweets we got from the last post. Thanks so much for helping us make this list great.]]></content>
</entry>
<entry>
<title><![CDATA[静态网站生成器Static Site Generators(SSGs)推荐]]></title>
<url>%2F2019%2F07%2F14%2F%E9%9D%99%E6%80%81%E7%BD%91%E7%AB%99%E7%94%9F%E6%88%90%E5%99%A8Static-Site-Generators%EF%BC%88SSGs%EF%BC%89%E6%8E%A8%E8%8D%90%2F</url>
<content type="text"><![CDATA[简介Static Site Generators(SSGs)可以用来生成静态网站、博客,可以部署到GitHub Pages、GitTee Pages、Gitee Pages等免费空间上。 SSG:项目官网 Wbsite URL:示例网站 Project URL:项目在GitLab上的主页 Configuration:部署到GitLab Pages的GitLab CI配置文件 Environment: Ruby SSG Website URL Project URL Configuration Jekyll Default Theme Source on GitLab Building Jekyll 3.1.2 with Bundler Middleman Default Theme Source on GitLab Default + Bundler ENV=PRODUCTION Nanoc Default Theme Source on GitLab Default Octopress Default Theme Source on GitLab Default Environment: Node JS SSG Website URL Project URL Configuration Hexo Hueman Theme Source on GitLab Default + test job Brunch Default Skeleton Source on GitLab Default Harp Default Theme Source on GitLab Default Metalsmith Default Theme Source on GitLab Default GitBook Default Theme Source on GitLab Default Environment: Python SSG Website URL Project URL Configuration Pelican Default Theme Source on GitLab Default Lektor Default Theme Source on GitLab Default Hyde Default Theme Source on GitLab Default + test job Nikola Default Theme Source on GitLab Default Environment: Go Lang SSG Website URL Project URL Configuration Hugo Lanyon Theme (Default) Source on GitLab Default 参考 SSGs Part 3: Build any SSG site with GitLab Pages]]></content>
</entry>
<entry>
<title><![CDATA[解决 GitLab: You are not allowed to force push code to a protected branch on this project问题]]></title>
<url>%2F2019%2F07%2F13%2F%E8%A7%A3%E5%86%B3-GitLab-You-are-not-allowed-to-force-push-code-to-a-protected-branch-on-this-project%E9%97%AE%E9%A2%98%2F</url>
<content type="text"><![CDATA[将Hexo博客部署到GitLab的时候报如下错误 1234567891011remote: GitLab: You are not allowed to force push code to a protected branch on this project.To gitlab.com:himmy/himmy.gitlab.io.git ! [remote rejected] HEAD -> master (pre-receive hook declined)error: failed to push some refs to 'git@gitlab.com:himmy/himmy.gitlab.io.git'FATAL Something's wrong. Maybe you can find the solution here: http://hexo.io/docs/troubleshooting.htmlError: Spawn failed at ChildProcess.<anonymous> (D:\hexo\blog\node_modules\hexo-util\lib\spawn.js:52:19) at emitTwo (events.js:126:13) at ChildProcess.emit (events.js:214:7) at ChildProcess.cp.emit (D:\hexo\blog\node_modules\cross-spawn\lib\enoent.js:40:29) at Process.ChildProcess._handle.onexit (internal/child_process.js:198:12) 这是因为项目分支master设置了保护所致,是为了防止别人乱提交代码设置的 可以到项目的Settings页面下找到Protected Branches,有如下两种解决方法 1.可以直接点该分支旁的Unprotect按钮,解除保护,但是这种方法不推荐 2.第二种方法是在Allowed to push下选择允许那些角色或具体那些用户可以提交,在这里可以选择你自己 设置完毕后再重新提交就成功了。]]></content>
</entry>
<entry>
<title><![CDATA[Hexo博客部署到Gitee码云]]></title>
<url>%2F2019%2F07%2F13%2FHexo%E5%8D%9A%E5%AE%A2%E9%83%A8%E7%BD%B2%E5%88%B0Gitee%E7%A0%81%E4%BA%91%2F</url>
<content type="text"><![CDATA[注册账号首先你得有个Gitee码云账号,没有的话就注册一个,这里就不再详细说明了 创建项目创建一个与你的Gitee控件地址同名的空项目,比如我的Gitee空间地址为https://gitee.com/himmi,那么创建的项目名为himmi, 最终博客部署后的访问地址为https://himmi.gitee.io 修改配置复制上一步创建的项目的地址,比如我的项目地址是git@gitee.com:himmi/himmi.git 然后在Hexo配置文件_config.yml中找到deploy配置项,添加如下配置 123456# Deployment## Docs: https://hexo.io/docs/deployment.htmldeploy: type: git repo: git@gitee.com:himmi/himmi.git branch: master 添加SSH在Gitee添加自己电脑的ssh key,这样每次更新代码到Gitee就不用再输入密码了 获取电脑的SSH可以参考这篇文章GitHub添加SSH key 然后在Gitee的设置页面找到安全设置–>SSH公钥,用上一步获取的SSH创建一个新的公钥 部署hexo d -g部署 开启Gitee Pages服务在项目主页找到服务,点击后可以看到Gitee Pages,点击跳转Gitee Pages服务页面,点击启动按钮开启服务 然后就可以在浏览器输入https://himmi.gitee.io访问啦 参考码云Pages]]></content>
</entry>
<entry>
<title><![CDATA[Hexo博客NexT主题添加百度分享]]></title>
<url>%2F2019%2F07%2F07%2FHexo%E5%8D%9A%E5%AE%A2NexT%E4%B8%BB%E9%A2%98%E6%B7%BB%E5%8A%A0%E7%99%BE%E5%BA%A6%E5%88%86%E4%BA%AB%2F</url>
<content type="text"><![CDATA[1.配置NexT中已经集成好了百度分享,我们只需在配置中开启即可 打开\themes\next_config.yml配置文件,搜索baidushare光健字找到以下配置,将baidushare及type前的注释去掉 type有两种类型,button和slide可以根据自己喜欢的配置 12345# Baidu Share# Available values: button | slide# Warning: Baidu Share does not support https.baidushare: type: slide 2.支持https上面一步其实就已经OK了,hexo g编译一下,hexo s就可以在本地看到效果了,但是我部署到GitHub上却死活看不到效果,以为是浏览器缓存的问题,强制刷新也还是看不到效果,后面看到配置文件里baidushare配置项上有一条注释# Warning: Baidu Share does not support https,就是说百度分享不支持https,而GitHub的url地址都是https的,其实我们只需要把百度分享需要的一些文件下载下来放在本地static文件夹下即可。 首先到GitHub上下载baiduShare,然后解压后将static整个文件夹放在\themes\next\source路径下 然后打开\themes\next\layout_partials\share\baidushare.swig文件,拉到最下面找到如下配置 123<script> with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='//bdimg.share.baidu.com/static/api/js/share.js?cdnversion='+~(-new Date()/36e5)];</script> 将上面的配置替换成如下配置 123<script> with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='/static/api/js/share.js?cdnversion='+~(-new Date()/36e5)];</script> 其实也就是吧static前的//bdimg.share.baidu.com去掉就完事了 最终效果最后再重新hexo d -g编译部署下就可以看到如下效果了 左侧有一个分享的按钮 点击后展开,可以选择分享到哪里]]></content>
</entry>
<entry>
<title><![CDATA[Hexo博客NexT主题添加theme-next-canvas-nest几何动效]]></title>
<url>%2F2019%2F07%2F07%2FHexo%E5%8D%9A%E5%AE%A2NexT%E4%B8%BB%E9%A2%98%E6%B7%BB%E5%8A%A0theme-next-canvas-nest%E5%87%A0%E4%BD%95%E5%8A%A8%E6%95%88%2F</url>
<content type="text"><![CDATA[步骤一 进入NexT文件目录1$ cd themes/next 步骤二 下载canvas-nest我们把canvas-nest下载到next目录下的ource/lib目录里 1$ git clone https://github.com/theme-next/theme-next-canvas-nest source/lib/canvas-nest 步骤三 配置在NexT配置文件_config.yml中配置开启canvas_nest 在配置文件中找到canvas_nest,将enable设置成true,还可以自定义颜色、透明度、数量等。 1234567canvas_nest: enable: true onmobile: true # display on mobile or not color: '0,0,255' # RGB values, use ',' to separate opacity: 0.5 # the opacity of line: 0~1 zIndex: -1 # z-index property of the background count: 99 # the number of lines 更新12$ cd themes/next/source/lib/canvas-nest$ git pull 效果$ hexo d -g重新编译部署下就可以看到效果了,可以到我的个人博客野猿新一看下效果 canvas_nest GitHub主页详细说明可以到canvas_nest主页theme-next/theme-next-canvas-nest查看]]></content>
</entry>
<entry>
<title><![CDATA[Hexo博客NexT主题下添加文章边框阴影效果]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E5%8D%9A%E5%AE%A2NexT%E4%B8%BB%E9%A2%98%E4%B8%8B%E6%B7%BB%E5%8A%A0%E6%96%87%E7%AB%A0%E8%BE%B9%E6%A1%86%E9%98%B4%E5%BD%B1%E6%95%88%E6%9E%9C%2F</url>
<content type="text"><![CDATA[1.打开themes/next/source/css/_custom/custom.styl文件 2.在custom.styl文件中添加如下配置 1234567.post { margin-top: 60px; margin-bottom: 60px; padding: 25px; -webkit-box-shadow: 0 0 5px rgba(202, 203, 203, .5); -moz-box-shadow: 0 0 5px rgba(202, 203, 204, .5);} 3.重新hexo d -g即可看到效果]]></content>
</entry>
<entry>
<title><![CDATA[Hexo博客NexT主题右上角添加fork me on github入口]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E5%8D%9A%E5%AE%A2NexT%E4%B8%BB%E9%A2%98%E5%8F%B3%E4%B8%8A%E8%A7%92%E6%B7%BB%E5%8A%A0fork-me-on-github%E5%85%A5%E5%8F%A3%2F</url>
<content type="text"><![CDATA[先看下实现效果,右上角有个GitHub的小图标,点击后可以自定义跳转其他页面,我这里设置的是我的GitHub主页 1.首先到GitHub Corners或者GitHub Ribbons选择自己喜欢的图标,然后copy相应的代码 2.然后将刚才复制的代码粘贴到themes/next/layout/_layout.swig文件中下面一行 3.把代码中的href后面的值替换成你要跳转的地址,比如你的GitHub主页 以下是上面的效果采用的代码 1<a href="https://your-url" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>]]></content>
</entry>
<entry>
<title><![CDATA[Hexo博客添加live2d卡通人物]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E5%8D%9A%E5%AE%A2%E6%B7%BB%E5%8A%A0live2d%E5%8D%A1%E9%80%9A%E4%BA%BA%E7%89%A9%2F</url>
<content type="text"><![CDATA[实现效果 先看一下实现效果,右下角的小可爱就是添加的live2d卡通人物,而且她还会眨眼睛,头会随着鼠标的移动而转动 1.安装hexo-helper-live2d 1$ npm install --save hexo-helper-live2d 2.安装live2d 其中替换成想要的,比如我安装的的是live2d-widget-model-wanko 当然,还有很多的model可供选择,参考 xiazeyu/live2d-widget-models hexo live2d插件 2.0 ! 1$ npm install <live2d-widget-model> 安装live2d-widget-model-wanko 1$ npm install live2d-widget-model-wanko 3.配置 在Hexo站点配置文件_config.yml,或者主题配置文件_config.yml中添加如下配置 至于每个配置项的作用看名字就很清楚,也可以修改值然后部署看下效果 更多详细的说明和配置,详见官网EYHN/hexo-helper-live2d 123456789101112131415161718live2d: enable: true scriptFrom: local pluginRootPath: live2dw/ pluginJsPath: lib/ pluginModelPath: assets/ tagMode: false log: false model: use: live2d-widget-model-wanko display: position: right width: 150 height: 300 mobile: show: true react: opacity: 0.7 重新hexo d -g即可看到效果]]></content>
</entry>
<entry>
<title><![CDATA[Hexo博客NexT主题下添加字数统计和阅读时长]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E5%8D%9A%E5%AE%A2NexT%E4%B8%BB%E9%A2%98%E4%B8%8B%E6%B7%BB%E5%8A%A0%E5%AD%97%E6%95%B0%E7%BB%9F%E8%AE%A1%E5%92%8C%E9%98%85%E8%AF%BB%E6%97%B6%E9%95%BF%2F</url>
<content type="text"><![CDATA[安装hexo-symbols-count-time 1$ npm install hexo-symbols-count-time --save 如果安装完报如下提醒,还需安装eslint 12D:\hexo\blog>npm install hexo-symbols-count-time --savenpm WARN babel-eslint@10.0.1 requires a peer of eslint@>= 4.12.1 but none is installed. You must install peer dependencies yourself. 安装eslint 1$ npm install eslint --save 在站点配置文件添加如下配置 123456symbols_count_time: symbols: true # 文章字数统计 time: true # 文章阅读时长 total_symbols: true # 站点总字数统计 total_time: true # 站点总阅读时长 exclude_codeblock: false # 排除代码字数统计 在NexT主题配置文件添加如下配置(NexT主题已支持该插件,有的话无需再添加) 123456789# Post wordcount display settings# Dependencies: https://github.com/theme-next/hexo-symbols-count-timesymbols_count_time: separated_meta: true # 是否另起一行(true的话不和发表时间等同一行) item_text_post: true # 首页文章统计数量前是否显示文字描述(本文字数、阅读时长) item_text_total: false # 页面底部统计数量前是否显示文字描述(站点总字数、站点阅读时长) awl: 4 # Average Word Length wpm: 275 # Words Per Minute(每分钟阅读词数) suffix: mins. 效果如下: 站点统计 文章统计 参考theme-next/hexo-symbols-count-time]]></content>
</entry>
<entry>
<title><![CDATA[Hexo博客NexT主题下添加分类、标签、关于菜单项]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E5%8D%9A%E5%AE%A2NexT%E4%B8%BB%E9%A2%98%E4%B8%8B%E6%B7%BB%E5%8A%A0%E5%88%86%E7%B1%BB%E3%80%81%E6%A0%87%E7%AD%BE%E3%80%81%E5%85%B3%E4%BA%8E%E8%8F%9C%E5%8D%95%E9%A1%B9%2F</url>
<content type="text"><![CDATA[Hexo NexT主题下默认有首页和归档两个菜单,我们还可以开启其他菜单项,比如分类、标签、关于 首先打开主题下的配置文件_config.yml,然后搜索menu找到如下配置项,将about、tags、categories前的#号去掉,就开启了关于、标签和分类标签,当然还有其他菜单项也可以开启 12345678910111213141516171819# ---------------------------------------------------------------# Menu Settings# ---------------------------------------------------------------# When running the site in a subdirectory (e.g. domain.tld/blog), remove the leading slash from link value (/archives -> archives).# Usage: `Key: /link/ || icon`# Key is the name of menu item. If the translation for this item is available, the translated text will be loaded, otherwise the Key name will be used. Key is case-senstive.# Value before `||` delimiter is the target link.# Value after `||` delimiter is the name of FontAwesome icon. If icon (with or without delimiter) is not specified, question icon will be loaded.# External url should start with http:// or https://menu: home: / || home about: /about/ || user tags: /tags/ || tags categories: /categories/ || th archives: /archives/ || archive #schedule: /schedule/ || calendar #sitemap: /sitemap.xml || sitemap #commonweal: /404/ || heartbeat 重新生成部署后,可以看到新增的菜单项,但是单击后会报如下错误 123Cannot GET /about/Cannot GET /tags/Cannot GET /categories/ 这是因为你还需运行如下命令新建相关页面 123hexo new page "about"hexo new page "tags"hexo new page "categories" 运行结果如下,会再source文件下创建about、tags、categories文件夹,每个文件夹下还会创建一个index.md文件表示关于、标签页分类页面,编辑这三个MarkDown文件可以自定义这三个页面的内容 12345678D:\hexo\blog>hexo new page "about"INFO Created: D:\hexo\blog\source\about\index.mdD:\hexo\blog>hexo new page "tags"INFO Created: D:\hexo\blog\source\tags\index.mdD:\hexo\blog>hexo new page "categories"INFO Created: D:\hexo\blog\source\categories\index.md 还差最后一步,打开各页面对应的index.md文件,编辑如下内容,title和date是默认生成的,增加type即可 1234567891011121314151617---title: aboutdate: 2019-06-25 19:16:17type: "about"------title: aboutdate: 2019-06-25 19:16:17type: "tags"------title: aboutdate: 2019-06-25 19:16:17type: "categories"--- 重新生成和部署即可看到效果]]></content>
</entry>
<entry>
<title><![CDATA[Hexo博客新建草稿]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E5%8D%9A%E5%AE%A2%E6%96%B0%E5%BB%BA%E8%8D%89%E7%A8%BF%2F</url>
<content type="text"><![CDATA[输入如下命令新建一个草稿文件,其中替换成草稿标题 1$ hexo new draft <title> 实际运行可以知道新建的草稿文件存放在/source/_drafts路径下 12D:\hexo\blog>hexo new draft "我的草稿文章"INFO Created: D:\hexo\blog\source\_drafts\我的草稿文章.md 既然是草稿的话,肯定是不会发布的,可以重新生成发布下,在首页确实看不到草稿文章 如果说草稿写完成了要发布,可以输入如下命令将草稿转成正式文章,该命令会把/source/_drafts下的文章移到/source/_posts下 1$ hexo publish <title> 12D:\hexo\blog>hexo publish "我的草稿文章"INFO Published: D:\hexo\blog\source\_posts\2019-06-24-我的草稿文章.md]]></content>
</entry>
<entry>
<title><![CDATA[Hexo博客删除文章]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E5%8D%9A%E5%AE%A2%E5%88%A0%E9%99%A4%E6%96%87%E7%AB%A0%2F</url>
<content type="text"><![CDATA[目前未找到删除文章的指令,可以到目录/source/_posts下删除相应的文章,然后重新生成部署即可 到目录/source/_posts下删除相应的文章 $ hexo g $ hexo d]]></content>
</entry>
<entry>
<title><![CDATA[Hexo博客新建文章]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E5%8D%9A%E5%AE%A2%E6%96%B0%E5%BB%BA%E6%96%87%E7%AB%A0%2F</url>
<content type="text"><![CDATA[命令行创建一篇新的文章,输入如下命令,其中替换成你的文章标题 1$ hexo new <title> 或者 1$ hexo new post <title> 实际运行可以看到创建的文件在/source/_posts路径下 12345D:\hexo\blog>hexo new 我的第一篇博文INFO Created: D:\hexo\blog\source\_posts\2019-06-24-我的第一篇博文.mdD:\hexo\blog>hexo new post "My Second Blog"INFO Created: D:\hexo\blog\source\_posts\2019-06-24-My-Second-Blog.md 打开创建的MarkDown文件,默认内容如下,我们可以在新建的文件里用MarkDown语法编辑文章 12345---title: 我的第一篇博文date: 2019-06-24 20:05:55tags:---]]></content>
</entry>
<entry>
<title><![CDATA[Hexo博客百度收录]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E5%8D%9A%E5%AE%A2%E7%99%BE%E5%BA%A6%E6%94%B6%E5%BD%95%2F</url>
<content type="text"><![CDATA[链接提交给百度在百度中搜索自己博客的域名,如www.himmy.cn,或者site:www.himmy.cn,如果百度找不到该博客的相关信息就说明你的博客地址还未被百度收录,会有如下提示,按提示点击提交网站到相关页面就可以提交我们的博客地址 没有找到该URL。您可以直接访问 www.himmy.cn,还可提交网址给我们。 添加网站及验证所有权登录百度搜索资源平台,然后进入站点管理页面,点击添加网站按钮添加我们的博客 第一步:输入网站地址,如https://www.himmy.cn 第二步:选择站点属性,最多可选三项,如影视动漫、信息技术等 第三步:验证网站,就是验证网站的所有权,说明该网站是我们的,这是重点,下面详细说明 验证网站有三种方式:文件验证、HTML标签验证、CNAME验证 文件验证 请点击 下载验证文件 获取验证文件(当前最新:baidu_verify_aZtyNXPrcV.html) 将验证文件放置于您所配置域名(https://www.himmy.com)的根目录下 点击这里确认验证文件可以正常访问 请点击“完成验证”按钮 为保持验证通过的状态,成功验证后请不要删除HTML文件 HTML标签验证 将以下代码添加到您的网站首页HTML代码的标签与标签之间,完成操作后请点击“验证”按钮。 注意:content这个值每个网站都是不一样的,要替换成你自己网站的值 示例 123456789<html> <head> <meta name="baidu-site-verification" content="aZtyNXPrcV" /> <title>My title</title> </head> <body> page contents </body></html> 为保持验证通过的状态,成功验证后请不要删除该标签 CNAME验证 请将 aZtyNXPrcV.himmy.com 使用CNAME解析到ziyuan.baidu.com 完成操作后请点击“完成验证”按钮。 为保持验证通过的状态,成功验证后请不要删除该DNS记录 注意aZtyNXPrcV这个值是百度随机生成分配的,替换成你自己网站的值 CNME验证的方法适用于博客已绑定域名的情况下 这种方法比较简单,本人用的就是该方法 我用的阿里云,登录阿里云后台,进入域名解析页面,添加一条CNAME记录,主机记录设置为aZtyNXPrcV.himmy.com,记录值设置为ziyuan.baidu.com,设置完成后返回百度验证页面点击完成验证按钮即可。 链接提交完成网站所有权验证后就可以向百度提交链接了,找到网站支持->数据引入->连接提交进入提交页 使用说明 \1. 链接提交工具是网站主动向百度搜索推送数据的工具,本工具可缩短爬虫发现网站链接时间,网站时效性内容建议使用链接提交工具,实时向搜索推送数据。本工具可加快爬虫抓取速度,无法解决网站内容是否收录问题 \2. 百度搜索资源平台为站长提供链接提交通道,您可以提交想被百度收录的链接,百度搜索引擎会按照标准处理,但不保证一定能够收录您提交的链接。 链接提交分成自动提交和手动提交两种方式 手动提交就是手动编辑链接提交 自动提交又分三种: 主动推送:通过主动调用百度提供的接口提交链接 自动推送:在页面被访问时,页面URL将立即被推送给百度 sitemap:sitemap文件里包含站点的所有页面地址,提供给搜索引擎的爬虫爬取 下面详细介绍下自动推送和sitemap这两种方式在Hexo中的使用 链接提交:自动推送这里以NexT主题为例,进入主题配置文件,开启baidu_push 12# Enable baidu push so that the blog will push the url to baidu automatically which is very helpful for SEO.baidu_push: true 然后进入 站点根目录\themes\next\layout_scripts目录,修改baidu_push.swig文件为以下内容,若无该文件直接创建一个 12345678910111213141516{% if theme.baidu_push %}<script>(function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s);})();</script>{% endif %} 重新编译生成部署即可,这样每次访问一个页面都会主动把这个页面的地址提交给百度 访问博客任一页面,然后按F12,切换到Elements,如果看到如下代码说明以上js代码插入成功 链接提交:sitemap安装sitemap生成器插件 12$ npm install hexo-generator-sitemap --save$ npm install hexo-generator-baidu-sitemap --save 然后修改站点配置文件_config.yml,将url改成我们博客的地址 123456# URL## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/'url: https://www.himmy.cnroot: /permalink: :year/:month/:day/:title/permalink_defaults: 重新generate,会在public目录下生成sitemap.xml、baidusitemap.xml两个文件 sitemap.xml文件内容,包含我博客里两篇文章的地址 1234567891011<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <loc>https://www.himmy.cn/2019/06/19/My_First_BLog/</loc> <lastmod>2019-06-19T13:27:31.916Z</lastmod> </url> <url> <loc>https://www.himmy.cn/2019/06/16/hello-world/</loc> <lastmod>2019-06-16T13:47:45.959Z</lastmod> </url> </urlset> 重新生成部署一下 最后在自动提交->sitemap页面填入sitemap的地址,以我的博客为例: 12www.himmy.cn/sitemap.xmlwww.himmy.cn/baidusitemap.xml 点击提交按钮提交]]></content>
</entry>
<entry>
<title><![CDATA[Hexo博客NexT主题下显示摘要和阅读原文按钮]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E5%8D%9A%E5%AE%A2NexT%E4%B8%BB%E9%A2%98%E4%B8%8B%E6%98%BE%E7%A4%BA%E6%91%98%E8%A6%81%E5%92%8C%E9%98%85%E8%AF%BB%E5%8E%9F%E6%96%87%E6%8C%89%E9%92%AE%2F</url>
<content type="text"><![CDATA[NexT主题下的首页默认是显示每一篇文章的全文的,如果文章很长就要往下拉很远才能看到下一篇文章 我们要设置成每一篇文章只显示150个字的摘要,然后底部显示一个阅读原文的按钮,点击可以进入阅读全文 打开NexT主题的配置文件,站点根目录\themes\next_config.yml 搜索auto_excerpt,然后配置如下,开启摘要,设置摘要长度为150个字 12345# Automatically Excerpt (Not recommend).# Use <!-- more --> in the post to control excerpt accurately.auto_excerpt: enable: true length: 150 重新生成及部署即可看到效果]]></content>
</entry>
<entry>
<title><![CDATA[Hexo个人博客NexT主题添加Local Search本地搜索]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2NexT%E4%B8%BB%E9%A2%98%E6%B7%BB%E5%8A%A0Local-Search%E6%9C%AC%E5%9C%B0%E6%90%9C%E7%B4%A2%2F</url>
<content type="text"><![CDATA[添加本地自定义站点内容搜索,可以通过文字标题或文字内容关键字搜索出相应文章 安装 hexo-generator-searchdb,在站点的根目录下执行以下命令: 1$ npm install hexo-generator-searchdb --save 编辑站点配置文件,新增以下内容到任意位置: 12345search: path: search.xml field: post format: html limit: 10000 编辑主题配置文件,启用本地搜索功能: 123# Local searchlocal_search: enable: true 效果]]></content>
</entry>
<entry>
<title><![CDATA[Hexo 修改博客标题乱码的问题]]></title>
<url>%2F2019%2F07%2F06%2FHexo-%E4%BF%AE%E6%94%B9%E5%8D%9A%E5%AE%A2%E6%A0%87%E9%A2%98%E4%B9%B1%E7%A0%81%E7%9A%84%E9%97%AE%E9%A2%98%2F</url>
<content type="text"><![CDATA[在文章Hexo 修改博客站点标题中我们介绍了如何配置博客的标题,但是发现如果设置成中文会有乱码的问题。 站点配置文件_config.yml原来的编码为ANSI,不支持中文,只需将配置文件_config.yml保存为UTF-8编码格式即可。 可以用EditPlus双击底部编码,在弹出的对话框中选择UTF-8编码]]></content>
</entry>
<entry>
<title><![CDATA[Hexo 修改博客站点标题]]></title>
<url>%2F2019%2F07%2F06%2FHexo-%E4%BF%AE%E6%94%B9%E5%8D%9A%E5%AE%A2%E7%AB%99%E7%82%B9%E6%A0%87%E9%A2%98%2F</url>
<content type="text"><![CDATA[Site Setting Description title The title of your website subtitle The subtitle of your website description The description of your website author Your name language The language of your website. Use a 2-lettter ISO-639-1 code. Default is en. timezone The timezone of your website. Hexo uses the setting on your computer by default. You can find the list of available timezones here. Some examples are America/New_York, Japan, and UTC. 网站 参数 描述 title 网站标题 subtitle 网站副标题 description 网站描述 author 您的名字 language 网站使用的语言 timezone 网站时区。Hexo 默认使用您电脑的时区。时区列表。比如说:America/New_York, Japan, 和 UTC 。 Hexo支持配置如上站点参数,其中title属性就是博客的标题 打开站点配置文件_config.yml,找到如下配置项,配置成你想要的值 12345678# Sitetitle: 野猿新一subtitle: 无description: 野猿新一的博客keywords: Android Pythonauthor: himmylanguage: zh-CNtimezone: America/New_York 重新打包部署即可生效]]></content>
</entry>
<entry>
<title><![CDATA[Hexo NexT主题更改语言]]></title>
<url>%2F2019%2F07%2F06%2FHexo-NexT%E4%B8%BB%E9%A2%98%E6%9B%B4%E6%94%B9%E8%AF%AD%E8%A8%80%2F</url>
<content type="text"><![CDATA[打开站点配置文件:站点根目录/_config.yml 然后搜索找到language属性,属性值配置成zh-Hans,表示中文 1language: zh-Hans 然后重新打包部署,却发现未生效,可是网上看的一篇教程明明说这这样配置的,果然还是要自己验证下 到 Hexo站点根目录\themes\next\languages文件夹下看有支持哪些语言,发现中文有如下三种,就是没有zh-Hans zh-CN.yml zh-HK.yml zh-TW.yml 那么有两种解决方法 1.是将language属性配置成zh-CN 1language: zh-Hans 2.将zh-CN.yml文件名改成zh-Hans NexT目前支持的多语言到NexT官网https://theme-next.org/docs/getting-started/可以看到目前NexT支持的多语言,也就是\themes\next\languages目录下的所有语言,当然我们也可以模仿\themes\next\languages下语言文件的格式,自己添加其他语言的支持 Language Example Code 🇨🇳 Chinese (Simplified) 简体中文 zh-CN 🇹🇼 Chinese (Traditional) 繁體中文 zh-TW 🇭🇰 Chinese (Hong Kong) 繁體中文-香港 zh-HK 🇧🇶 Dutch Niederländisch nl 🇺🇸 English English en 🇹🇫 French Français fr 🇩🇪 German Deutsch de 🇮🇩 Indonesian Indonesia id 🇮🇹 Italian Italiano it 🇯🇵 Japanese 日本語 ja 🇰🇷 Korean 한국어 ko 🇮🇷 Persian فارسی fa 🇵🇹 Portuguese Português pt 🇧🇷 Portuguese (Brazilian) Português (Brazilian) pt-BR 🇷🇺 Russian Русский ru 🇪🇸 Spanish Español es 🇹🇷 Turkish Türk tr 🇺🇦 Ukrainian Український uk 🇻🇳 Vietnamese Tiếng Việt vi 重新打包部署就可以看到更改后的语言了]]></content>
</entry>
<entry>
<title><![CDATA[Hexo NexT主题添加不算子统计]]></title>
<url>%2F2019%2F07%2F06%2FHexo-NexT%E4%B8%BB%E9%A2%98%E6%B7%BB%E5%8A%A0%E4%B8%8D%E7%AE%97%E5%AD%90%E7%BB%9F%E8%AE%A1%2F</url>
<content type="text"><![CDATA[不算子官网不算子官网有不算子详细使用的官方文档,但是如果你仅仅是想要在NexT中开启不算子统计,可以跳过直接往下看 开启不算子统计NexT中已经为我们集成了不算子统计的代码,我们只需在NexT配置文件中开启即可 进入Hexo站点目录\themes\next_config.yml,打开配置文件 搜索busuanzi_count关键字找到不算子统计的相关配置,配置如下: 只需将enable设置成true即可 total_visitors: 站点总访问量 total_visitors_icon: 站点总访问量的小图标 total_views: 总浏览量(所有页面的总浏览量) total_views_icon: 总浏览量的小图标 post_views: 文章浏览量 post_views_icon: 文浏览量的小图标 12345678busuanzi_count: enable: true total_visitors: true total_visitors_icon: user total_views: true total_views_icon: eye post_views: true post_views_icon: eye 查看效果Hexo重新编译部署下 然后重新打开博客 拉到页面最底部可以看到如下站点总访问量和总浏览量统计 进入到某一篇文章,在文章标题底下可以看到文章浏览量统计]]></content>
</entry>
<entry>
<title><![CDATA[Hexo NexT主题添加百度统计]]></title>
<url>%2F2019%2F07%2F06%2FHexo-NexT%E4%B8%BB%E9%A2%98%E6%B7%BB%E5%8A%A0%E7%99%BE%E5%BA%A6%E7%BB%9F%E8%AE%A1%2F</url>
<content type="text"><![CDATA[注册百度统计账号如果没有百度统计账号的先到https://tongji.baidu.com注册一个账号 添加博客域名然后在管理页面https://tongji.baidu.com/sc-web/28191016/home/site中新增网站,添加我们的博客域名 获取Baidu Analytics ID然后进入代码管理->代码获取https://tongji.baidu.com/sc-web/28191016/home/site/getjs页面 可以看到如下一段javascript代码,这段代码本来是要手动嵌入到网页中的,但是HexT已经帮我们嵌入了,所以我们自诩配置Baidu Analytics ID即可,这个ID就是下面代码的hm.js?后面那一串,先复制一下 123456789<script>var _hmt = _hmt || [];(function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?5b6ae75148041557ddd693925322myid"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s);})();</script> 配置Baidu Analytics ID打开hexo站点根目录\themes\next_config.yml路径下的next配置文件,然后搜索Baidu Analytics ID,找到如下配置项: 去掉baidu_analytics前面的#号开启百度统计,值设置成刚在百度统计后台复制的Baidu Analytics ID 12# Baidu Analytics IDbaidu_analytics: 5b6ae75148041557ddd69392532288de 重新打包部署就OK啦 12$ hexo g$ hexo d 验证在百度统计后台的代码管理->代码安装检查页面查看是否需安装成功,如果成功会显示页面代码安装状态:代码安装正确 刚安装一般要等20分钟左右才会显示成功。可以先参考这篇文章《手动检查代码的方法》手动检查,这是马上生效的]]></content>
</entry>
<entry>
<title><![CDATA[Hexo个人博客NexT主题设置Scheme外观]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2NexT%E4%B8%BB%E9%A2%98%E8%AE%BE%E7%BD%AEScheme%E5%A4%96%E8%A7%82%2F</url>
<content type="text"><![CDATA[Scheme简介Scheme 是 NexT 提供的一种特性,借助于 Scheme,NexT 为你提供多种不同的外观。同时,几乎所有的配置都可以 在 Scheme 之间共用。目前 NexT 支持三种 Scheme,他们是: Muse - 默认 Scheme,这是 NexT 最初的版本,黑白主调,大量留白 Mist - Muse 的紧凑版本,整洁有序的单栏外观 Pisces - 双栏 Scheme,小家碧玉似的清新 配置Scheme 的切换通过更改主题配置文件,搜索 scheme 关键字。 你会看到有三行 scheme 的配置,将你需用启用的 scheme 前面注释 # 去除即可。 配置文件在hexo项目根目录下的themes/next/_config.yml 例如选择 Pisces Scheme 12345678# ---------------------------------------------------------------# Scheme Settings# ---------------------------------------------------------------# Schemes#scheme: Muse#scheme: Mistscheme: Pisces 预览设置完保存,重新generate和deploy就可以看到更改后的外观 scheme: Muse scheme: Mist scheme: Pisces]]></content>
</entry>
<entry>
<title><![CDATA[Hexo个人博客绑定域名]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E7%BB%91%E5%AE%9A%E5%9F%9F%E5%90%8D%2F</url>
<content type="text"><![CDATA[注册域名我是在阿里云上注册的,很便宜,首年29块钱 首先到阿里云域名注册页输入你想要的域名查询https://wanwang.aliyun.com/domain/ 如果没被人抢注就可以加入清单直接购买 结算的时候需选择个人或者企业,且还要学者一个通过邮箱真实性验证的模板,如果没有需创建一个 然后就可以提交订单支付购买了 若未实名认证还需提交身份证照片审核,说是两三个工作日内会审核,但是我十分钟内就审核通过了,速度很快 设置域名解析进入阿里云后台->侧边栏选择域名->点击刚注册的域名->侧边栏再选择域名解析,进入到该域名的域名解析设置页 先用ping yourusername.github.io查询你的guihub主页对应的ip地址 然后在域名解析设置里添加两条记录: CNAME记录的记录值设置成域名,也就是你的github主页yourusername.github.io A记录的记录值设置成IP地址,也就是我们刚才ping查询出来的IP地址 主机记录就是域名的前缀,区别如下: 我的域名最终配置如下 CNAME文件最后在yourusername.github.io仓库根目录下新增一个文件命名为CNAME(注意不要后缀名),内容设置为申请的域名www.himmy.cn 也可以在hexo个目录/public目录下新郑CNAME文件,然后再重新部署上传到GitHub 访问大功告成 himmy.cn和www.himmy.cn都是可以访问的 欢迎大家访问我的个人博客]]></content>
</entry>
<entry>
<title><![CDATA[Hexo部署个人博客到GitHub]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E9%83%A8%E7%BD%B2%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E5%88%B0GitHub%2F</url>
<content type="text"><![CDATA[准备工作 Hexo搭建个人博客 GitHub添加SSH key 创建GitHub仓库先在GitHub创建一个名字为username.github.io的项目,其中username替换成你自己的用户名 修改配置文件打开_config.yml配置文件,找到deploy,修改成如下配置,其中username替换成你自己的用户名 123456# Deployment## Docs: https://hexo.io/docs/deployment.htmldeploy: type: git repo: git@github.com:username/username.github.io branch: master 安装Deployer部署前还需要安装Deployer部署器,否则会报如下错误 1ERROR Deployer not found: git 输入如下命令安装hexo-deployer-git 1$ npm install hexo-deployer-git --save 安装成功 123456npm WARN babel-eslint@10.0.1 requires a peer of eslint@>= 4.12.1 but none is installed. You must install peer dependencies yourself.npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.9 (node_modules\fsevents):npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.9: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})+ hexo-deployer-git@1.0.0added 59 packages in 15.847s Git设置user.email和user.name本来以为准备工作已做完,直接运行$ hexo deploy部署,结果报如下错误 大致意思就是说要设置user.email和user.name 1234567891011121314151617181920*** Please tell me who you are.Run git config --global user.email "you@example.com" git config --global user.name "Your Name"to set your account's default identity.Omit --global to set the identity only in this repository.fatal: unable to auto-detect email address (got 'Him@hongxiaoxin.(none)')error: src refspec HEAD does not match any.error: failed to push some refs to 'git@github.com:ghxiaoxiao/ghxiaoxiao.github.io'FATAL Something's wrong. Maybe you can find the solution here: http://hexo.io/docs/troubleshooting.htmlError: Spawn failed at ChildProcess.<anonymous> (d:\hexo\blog\node_modules\hexo-util\lib\spawn.js:52:19) at emitTwo (events.js:126:13) at ChildProcess.emit (events.js:214:7) at ChildProcess.cp.emit (d:\hexo\blog\node_modules\cross-spawn\lib\enoent.js:40:29) at Process.ChildProcess._handle.onexit (internal/child_process.js:198:12) 那就设置一下呗,其中email是你的GitHub绑定的邮箱,用户名是你的GitHub用户名 123$ git config --global user.email "youremail@demo.com"$ git config --global user.name "whatisyourname" Deploy部署首先进入hexo博客的根目录 1$ cd /d/hexo/blog 然后运行 1$ hexo deploy 或者 1$ hexo d 然后就可以看到类似如下的输出,开始部署上传 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157INFO Deploying: gitINFO Clearing .deploy_git folder...INFO Copying files from public folder...INFO Copying files from extend dirs...warning: LF will be replaced by CRLF in 2019/06/16/hello-world/index.html.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in archives/2019/06/index.html.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in archives/2019/index.html.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in archives/index.html.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in css/main.css.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in css/style.css.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-buttons.css.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-buttons.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-media.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-thumbs.css.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in fancybox/helpers/jquery.fancybox-thumbs.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in fancybox/jquery.fancybox.css.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in fancybox/jquery.fancybox.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in fancybox/jquery.fancybox.pack.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in js/affix.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in js/algolia-search.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in js/exturl.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in js/js.cookie.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in js/motion.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in js/next-boot.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in js/post-details.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in js/schemes/muse.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in js/schemes/pisces.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in js/script.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in js/scroll-cookie.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in js/scrollspy.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in js/utils.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in lib/font-awesome/css/font-awesome.css.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in lib/font-awesome/css/font-awesome.min.css.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in lib/jquery/index.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in lib/velocity/velocity.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in lib/velocity/velocity.min.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in lib/velocity/velocity.ui.js.The file will have its original line endings in your working directorywarning: LF will be replaced by CRLF in lib/velocity/velocity.ui.min.js.The file will have its original line endings in your working directory[master (root-commit) a25c173] Site updated: 2019-06-17 19:14:40 72 files changed, 20010 insertions(+) create mode 100644 2019/06/16/hello-world/index.html create mode 100644 archives/2019/06/index.html create mode 100644 archives/2019/index.html create mode 100644 archives/index.html create mode 100644 css/fonts/FontAwesome.otf create mode 100644 css/fonts/fontawesome-webfont.eot create mode 100644 css/fonts/fontawesome-webfont.svg create mode 100644 css/fonts/fontawesome-webfont.ttf create mode 100644 css/fonts/fontawesome-webfont.woff create mode 100644 css/images/banner.jpg create mode 100644 css/main.css create mode 100644 css/style.css create mode 100644 fancybox/blank.gif create mode 100644 fancybox/fancybox_loading.gif create mode 100644 fancybox/fancybox_loading@2x.gif create mode 100644 fancybox/fancybox_overlay.png create mode 100644 fancybox/fancybox_sprite.png create mode 100644 fancybox/fancybox_sprite@2x.png create mode 100644 fancybox/helpers/fancybox_buttons.png create mode 100644 fancybox/helpers/jquery.fancybox-buttons.css create mode 100644 fancybox/helpers/jquery.fancybox-buttons.js create mode 100644 fancybox/helpers/jquery.fancybox-media.js create mode 100644 fancybox/helpers/jquery.fancybox-thumbs.css create mode 100644 fancybox/helpers/jquery.fancybox-thumbs.js create mode 100644 fancybox/jquery.fancybox.css create mode 100644 fancybox/jquery.fancybox.js create mode 100644 fancybox/jquery.fancybox.pack.js create mode 100644 images/algolia_logo.svg create mode 100644 images/apple-touch-icon-next.png create mode 100644 images/avatar.gif create mode 100644 images/cc-by-nc-nd.svg create mode 100644 images/cc-by-nc-sa.svg create mode 100644 images/cc-by-nc.svg create mode 100644 images/cc-by-nd.svg create mode 100644 images/cc-by-sa.svg create mode 100644 images/cc-by.svg create mode 100644 images/cc-zero.svg create mode 100644 images/favicon-16x16-next.png create mode 100644 images/favicon-32x32-next.png create mode 100644 images/loading.gif create mode 100644 images/logo.svg create mode 100644 images/placeholder.gif create mode 100644 images/quote-l.svg create mode 100644 images/quote-r.svg create mode 100644 images/searchicon.png create mode 100644 index.html create mode 100644 js/affix.js create mode 100644 js/algolia-search.js create mode 100644 js/exturl.js create mode 100644 js/js.cookie.js create mode 100644 js/motion.js create mode 100644 js/next-boot.js create mode 100644 js/post-details.js create mode 100644 js/schemes/muse.js create mode 100644 js/schemes/pisces.js create mode 100644 js/script.js create mode 100644 js/scroll-cookie.js create mode 100644 js/scrollspy.js create mode 100644 js/utils.js create mode 100644 lib/font-awesome/HELP-US-OUT.txt create mode 100644 lib/font-awesome/bower.json create mode 100644 lib/font-awesome/css/font-awesome.css create mode 100644 lib/font-awesome/css/font-awesome.css.map create mode 100644 lib/font-awesome/css/font-awesome.min.css create mode 100644 lib/font-awesome/fonts/fontawesome-webfont.eot create mode 100644 lib/font-awesome/fonts/fontawesome-webfont.woff create mode 100644 lib/font-awesome/fonts/fontawesome-webfont.woff2 create mode 100644 lib/jquery/index.js create mode 100644 lib/velocity/velocity.js create mode 100644 lib/velocity/velocity.min.js create mode 100644 lib/velocity/velocity.ui.js create mode 100644 lib/velocity/velocity.ui.min.jsEnumerating objects: 94, done.Counting objects: 100% (94/94), done.Delta compression using up to 4 threadsCompressing objects: 100% (85/85), done.Writing objects: 100% (94/94), 941.35 KiB | 4.32 MiB/s, done.Total 94 (delta 9), reused 0 (delta 0)remote: Resolving deltas: 100% (9/9), done.To github.com:ghxiaoxiao/ghxiaoxiao.github.io + 7e2ebc3...a25c173 HEAD -> master (forced update)Branch 'master' set up to track remote branch 'master' from 'git@github.com:ghxiaoxiao/ghxiaoxiao.github.io'.[32mINFO [39m Deploy done: [35mgit[39m 浏览博客大功告成,一切顺利的话可以直接在浏览器输入https://username.github.io/浏览博客 我的博客地址是https://ghxiaoxiao.github.io/,欢迎参观 参考 Hexo Deployment]]></content>
</entry>
<entry>
<title><![CDATA[GitHub添加SSH key]]></title>
<url>%2F2019%2F07%2F06%2FGitHub%E6%B7%BB%E5%8A%A0SSH-key%2F</url>
<content type="text"><![CDATA[1.打开Git Bash命令行窗口2.检查是否已有SSH如果没有,会返回如下信息,继续第三步创建SSH 12$ cd ~/.sshbash: cd: /c/Users/Him/.ssh: No such file or directory 如果本地已经有创建SSH,会返回如下信息,表示本地已经有创建过SSH了,跳过第三步,直接看第四步 1234$ cd ~/.ssh$ lsid_rsa id_rsa.pub known_hosts 3.创建SSH key最后一个参数替换成你自己的GitHub注册邮箱 12$ ssh-keygen -t rsa -C "your_email@example.com"Generating public/private rsa key pair. 接下来会提示你输入生成的key存放的路径,不设置直接回车的话会默认创建在C:/Users/你的用户账号/.ssh文件夹下 12Enter file in which to save the key (/c/Users/Him/.ssh/id_rsa):Created directory '/c/Users/userpath/.ssh'. 再接下来会提示你输入密码,这个密码是用来每次提交的时候输入确认,可以不设置,直接回车两次 12Enter passphrase (empty for no passphrase):Enter same passphrase again: 最后成功后会看到类似如下的输出,表示成功生成SSH key了,可以到C:/Users/你的用户账号/.ssh文件夹下看下 12345678910111213141516Your identification has been saved in /c/Users/Him/.ssh/id_rsa.Your public key has been saved in /c/Users/Him/.ssh/id_rsa.pub.The key fingerprint is:SHA256:RwvBINgH8CEt2KniltmykeyDsOseUYcwMzehFeyT86s 1225723686@qq.comThe key's randomart image is:+---[RSA 2048]----+| o+%OO+o. ||..=+%*+ .. || ..+o+o.. . ||o. o=. o . ||o oolalala S o || +.+.. . . ||. .o . || . . . || . E. |+----[SHA256]-----+ 4.添加SSH key到GitHub首先复制.ssh文件夹下id_rsa.pub文件的内容,可以直接用文本编辑器打开复制,也可以用如下命令行复制 1$ clip < ~/.ssh/id_rsa.pub 然后进入https://github.com/settings/keys设置,如果没有登录要先登录 或者登录后依次点击右上角Settings,然后再点击SSH and GPG keys 然后点击New SSH key按钮,然后输入Title和我们刚才复制的Key,Title的话表示这个key来自哪里,比如说可以叫“家里的笔记本” 最后点击Add SSH key按钮保存 5.测试SSH连接输入如下命令 1$ ssh -T git@github.com 会得到如下输出,询问是否确认连接,输入yes回车确认 123The authenticity of host 'github.com (13.229.188.59)' can't be established.RSA key fingerprint is SHA256:nThbg6kXUpJWGl7mykeyCspRomTxdCARLviKw6E5SY8.Are you sure you want to continue connecting (yes/no)? yes 最后连接成功会看到如下输出 12Warning: Permanently added 'github.com,13.229.188.59' (RSA) to the list of known hosts.Hi ghxiaoxiao! You've successfully authenticated, but GitHub does not provide shell access. 参考 Connecting to GitHub with SSH]]></content>
</entry>
<entry>
<title><![CDATA[Hexo博客更换主题]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E5%8D%9A%E5%AE%A2%E6%9B%B4%E6%8D%A2%E4%B8%BB%E9%A2%98%2F</url>
<content type="text"><![CDATA[在文章Hexo搭建个人博客中我们介绍了如何利用Hexo搭建个人博客,刚搭建默认的主题是landscape,我们也可以更换自己喜欢的主题,这里以更换NexT主题为例 NexT主题GitHub首页:https://github.com/theme-next/hexo-theme-next 安装主题首先进入博客项目根目录 123d:\>cd hexo/blogd:\hexo\blog> 然后安装NexT主题 命令:git clone https://github.com/theme-next/hexo-theme-next themes/next 12345678D:\hexo\blog>git clone https://github.com/theme-next/hexo-theme-next themes/nextCloning into 'themes/next'...remote: Enumerating objects: 8, done.remote: Counting objects: 100% (8/8), done.remote: Compressing objects: 100% (8/8), done.remote: Total 7195 (delta 0), reused 1 (delta 0), pack-reused 7187Receiving objects: 100% (7195/7195), 5.57 MiB | 652.00 KiB/s, done.Resolving deltas: 100% (4640/4640), done. 主题下载成功后存放在如下目录:博客根目录/themes目录下 配置主题在项目根目录下打开_config.yml文件,找到theme属性,改成 1theme: next 重启服务重启服务后即可生效 12345678910111213D:\hexo\blog>hexo gINFO Start processingINFO Files loaded in 883 msINFO Generated: archives/2019/index.htmlINFO Generated: 2019/06/16/hello-world/index.htmlINFO Generated: archives/index.htmlINFO Generated: archives/2019/06/index.htmlINFO Generated: index.htmlINFO 5 files generated in 731 msD:\hexo\blog>hexo sINFO Start processingINFO Hexo is running at http://localhost:4000 . Press Ctrl+C to stop. 更新NexT首先进入NexT主题所在路径 1D:\>cd D:/hexo/blog/themes/next 然后执行命令:git pull 12D:\hexo\blog\themes\next>git pullAlready up to date.]]></content>
</entry>
<entry>
<title><![CDATA[Hexo搭建个人博客]]></title>
<url>%2F2019%2F07%2F06%2FHexo%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%2F</url>
<content type="text"><![CDATA[准备工作 安装Node.js 安装Git 安装hexo命令:npm install hexo-cli -g 12345678D:\>cd D:/hexoD:\hexo>npm install hexo-cli -gC:\Users\Him\AppData\Roaming\npm\hexo -> C:\Users\Him\AppData\Roaming\npm\node_modules\hexo-cli\bin\hexonpm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.9 (node_modules\hexo-cli\node_modules\fsevents):npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.9: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})+ hexo-cli@2.0.0added 189 packages in 48.578s 创建博客项目命令:hexo init blog 命令最后的blog为项目名,也可以设置自己喜欢的名字 12345678910111213141516171819202122232425D:\hexo>hexo init blogINFO Cloning hexo-starter https://github.com/hexojs/hexo-starter.gitCloning into 'D:\hexo\blog'...remote: Enumerating objects: 6, done.remote: Counting objects: 100% (6/6), done.remote: Compressing objects: 100% (4/4), done.remote: Total 74 (delta 2), reused 4 (delta 2), pack-reused 68Unpacking objects: 100% (74/74), done.Submodule 'themes/landscape' (https://github.com/hexojs/hexo-theme-landscape.git) registered for path 'themes/landscape'Cloning into 'D:/hexo/blog/themes/landscape'...remote: Enumerating objects: 21, done.remote: Counting objects: 100% (21/21), done.remote: Compressing objects: 100% (20/20), done.remote: Total 917 (delta 4), reused 10 (delta 0), pack-reused 896Receiving objects: 100% (917/917), 2.56 MiB | 682.00 KiB/s, done.Resolving deltas: 100% (484/484), done.Submodule path 'themes/landscape': checked out '73a23c51f8487cfcd7c6deec96ccc7543960d350'[32mINFO [39m Install dependenciesnpm WARN deprecated core-js@1.2.7: core-js@<2.6.8 is no longer maintained. Please, upgrade to core-js@3 or at least to actual version of core-js@2.npm notice created a lockfile as package-lock.json. You should commit this file.npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.9 (node_modules\fsevents):npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.9: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})added 420 packages in 43.278sINFO Start blogging with Hexo! 安装博客项目以上命令执行成功后会创建一个blog文件夹,进入该文件夹,执行install安装命令 1234567D:\hexo>cd blogD:\hexo\blog>npm installnpm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.9 (node_modules\fsevents):npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.9: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})up to date in 5.243s 打包生成命令:hexo g 或者命令:hexo generate 会打包生成静态文件 1234567891011121314151617181920212223242526272829303132D:\hexo\blog>hexo gINFO Start processingINFO Files loaded in 540 msINFO Generated: index.htmlINFO Generated: fancybox/jquery.fancybox.cssINFO Generated: fancybox/fancybox_overlay.pngINFO Generated: archives/index.htmlINFO Generated: archives/2019/06/index.htmlINFO Generated: fancybox/blank.gifINFO Generated: fancybox/fancybox_loading@2x.gifINFO Generated: fancybox/fancybox_loading.gifINFO Generated: fancybox/fancybox_sprite.pngINFO Generated: fancybox/fancybox_sprite@2x.pngINFO Generated: fancybox/jquery.fancybox.pack.jsINFO Generated: fancybox/jquery.fancybox.jsINFO Generated: archives/2019/index.htmlINFO Generated: fancybox/helpers/fancybox_buttons.pngINFO Generated: css/fonts/FontAwesome.otfINFO Generated: css/fonts/fontawesome-webfont.woffINFO Generated: fancybox/helpers/jquery.fancybox-buttons.cssINFO Generated: fancybox/helpers/jquery.fancybox-buttons.jsINFO Generated: fancybox/helpers/jquery.fancybox-thumbs.jsINFO Generated: css/fonts/fontawesome-webfont.eotINFO Generated: fancybox/helpers/jquery.fancybox-media.jsINFO Generated: js/script.jsINFO Generated: fancybox/helpers/jquery.fancybox-thumbs.cssINFO Generated: css/style.cssINFO Generated: css/fonts/fontawesome-webfont.ttfINFO Generated: css/images/banner.jpgINFO Generated: css/fonts/fontawesome-webfont.svgINFO Generated: 2019/06/16/hello-world/index.htmlINFO 28 files generated in 907 ms 开启服务命令:hexo s 或者命令:hexo server 123D:\hexo\blog>hexo sINFO Start processingINFO Hexo is running at http://localhost:4000 . Press Ctrl+C to stop. 发开博客服务成功后,在浏览器输入http://localhost:4000回车,就可以在本地浏览博客了 关闭服务在命令行中输入命令:Ctrl+C 再输入Y,回车确认退出服务 1234INFO Have a nice dayTerminate batch job (Y/N)? YD:\hexo\blog>]]></content>
</entry>
<entry>
<title><![CDATA[Android Dagger2 @Component的dependencies属性用法]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Dagger2-Component%E7%9A%84dependencies%E5%B1%9E%E6%80%A7%E7%94%A8%E6%B3%95%2F</url>
<content type="text"><![CDATA[一个Component可以通过dependencies依赖另一个Component,可以获取到另一个Component提供的依赖 具体代码如下: 1234567891011121314151617public interface Person { String saySomething();}public class Student implements Person { public String name; public Student() { this.name = "野猿新一"; } @Override public String saySomething() { return String.format("我的名字叫%s啦", name); }} 1234567@Modulepublic class PersonModule { @Provides public Person providePerson() { return new Student(); }} 注意这里为Person单独创建了一个Component然后依赖PersonModule,而不是直接把PersonModule设置给MainActivityModule 而且里面的getPerson()方法单纯提供Person对象,不像MainActivityModule中的inject()方法用于注入 1234@Component(modules = PersonModule.class)public interface PersonComponent { Person getPerson();} 这里有别于依赖于Module的写法@Component(modules = PersonModule.class),而是直接依赖PersonComponent 1234@Component(dependencies = PersonComponent.class)public interface MainActivityComponent { void inject(MainActivity activity);} 这里需要注意的是personComponent()方法必须调用且传递一个PersonComponent对象进入,否则在运行时会报如下错误 实现代码1234Caused by: java.lang.IllegalStateException: com.him.hisapp.PersonComponent must be set at com.him.hisapp.DaggerMainActivityComponent$Builder.build(DaggerMainActivityComponent.java:45) at com.him.hisapp.MainActivity.onCreate(MainActivity.java:20) at android.app.Activity.performCreate(Activity.java:6041) 123456789101112131415161718public class MainActivity extends AppCompatActivity { @Inject Person person; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.builder() .personComponent(DaggerPersonComponent.create()) .build() .inject(this); TextView textView = findViewById(R.id.text); textView.setText(person.saySomething()); }} DaggerMainActivityComponent源码解析我们主要看下Dagger为我们生成的DaggerMainActivityComponent的源码,里面依赖了PersonComponent 在Builder.build()方法调用是会判断personComponent是否为空,空的话会抛异常,至于为什么不在为空的时候直接new一个DaggerPersonComponent出来目前我还不知道原因,还请有知道的读者评论说明下 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849public final class DaggerMainActivityComponent implements MainActivityComponent { private PersonComponent personComponent; private DaggerMainActivityComponent(Builder builder) { initialize(builder); } public static Builder builder() { return new Builder(); } @SuppressWarnings("unchecked") private void initialize(final Builder builder) { this.personComponent = builder.personComponent; } @Override public void inject(MainActivity activity) { injectMainActivity(activity); } @CanIgnoreReturnValue private MainActivity injectMainActivity(MainActivity instance) { MainActivity_MembersInjector.injectPerson( instance, Preconditions.checkNotNull( personComponent.getPerson(), "Cannot return null from a non-@Nullable component method")); return instance; } public static final class Builder { private PersonComponent personComponent; private Builder() {} public MainActivityComponent build() { if (personComponent == null) { throw new IllegalStateException(PersonComponent.class.getCanonicalName() + " must be set"); } return new DaggerMainActivityComponent(this); } public Builder personComponent(PersonComponent personComponent) { this.personComponent = Preconditions.checkNotNull(personComponent); return this; } }} 注入的流程我们再看下Dagger为我们生成的代码再注入的整个过程中做了哪些工作 首先从MainActivity中调用DaggerMainActivityComponent.inject()开始,记得必须调用personComponent设置依赖的Component 1234DaggerMainActivityComponent.builder() .personComponent(DaggerPersonComponent.create()) .build() .inject(this); 看下DaggerMainActivityComponent中的Builder代码,卡夏是如何设置personComponent的和在build()的时候如果personComponent为空会抛出异常 1234567891011121314151617public static final class Builder { private PersonComponent personComponent; private Builder() {} public MainActivityComponent build() { if (personComponent == null) { throw new IllegalStateException(PersonComponent.class.getCanonicalName() + " must be set"); } return new DaggerMainActivityComponent(this); } public Builder personComponent(PersonComponent personComponent) { this.personComponent = Preconditions.checkNotNull(personComponent); return this; } } DaggerMainActivityComponent的inject()方法内部调用了injectMainActivity()方法 1234@Overridepublic void inject(MainActivity activity) { injectMainActivity(activity);} DaggerMainActivityComponent的injectMainActivity()方法内部调用了MainActivity_MembersInjector.injectPerson()方法来完成Person的依赖注入,传入MainActivity对象和一个Person实例,而这个Person实例就是由PersonComponent的gerPerson()方法提供的 123456789@CanIgnoreReturnValueprivate MainActivity injectMainActivity(MainActivity instance) { MainActivity_MembersInjector.injectPerson( instance, Preconditions.checkNotNull( personComponent.getPerson(), "Cannot return null from a non-@Nullable component method")); return instance;} 我们在看下DaggerPersonComponent的getPerson()方法,里面调用了PersonModule_ProvidePersonFactory.proxyProvidePerson() 1234@Overridepublic Person getPerson() { return PersonModule_ProvidePersonFactory.proxyProvidePerson(personModule);} 看下PersonModule_ProvidePersonFactory.proxyProvidePerson()方法内部就明白了Persong对象最终是由PersonModule提供的 1234public static Person proxyProvidePerson(PersonModule instance) { return Preconditions.checkNotNull( instance.providePerson(), "Cannot return null from a non-@Nullable @Provides method");} 最终再看下MainActivity_MembersInjector.injectPerson()方法,里面把生成的person对象赋值给MainActivity的person变量,最终完成依赖的注入 123public static void injectPerson(MainActivity instance, Person person) { instance.person = person;} 总结以上的注入流程就是 MainActivityComponent依赖PersonComponent PersonComponent依赖PersonModule 所以最终的Person对象是由Module提供的]]></content>
</entry>
<entry>
<title><![CDATA[Android Dagger2 自定义@Qualifier]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Dagger2-%E8%87%AA%E5%AE%9A%E4%B9%89-Qualifier%2F</url>
<content type="text"><![CDATA[先定义两个Qualifier,待会在Module中用于区分两个Person 1234567@Qualifier@Retention(RetentionPolicy.RUNTIME)public @interface QualifierStudent {}@Qualifier@Retention(RetentionPolicy.RUNTIME)public @interface QualifierTeacher {} 12345678910111213141516171819202122232425262728293031public interface Person { String saySomething();}public class Student implements Person { public String name; public Student() { this.name = "野猿新一"; } @Override public String saySomething() { return String.format("我的名字叫%s啦", name); }}public class Teacher implements Person { public String name; public Teacher() { this.name = "苍老湿"; } @Override public String saySomething() { return String.format("我的名字叫%s啦", name); }} 这里是重点,用上面定义的两个Qualifier @QualifierStudent和@QualifierTeacher来区分两个放回类型都为Person的方法 123456789101112131415@Modulepublic class MainModule { @Provides @QualifierStudent public Person provideStudent() { return new Student(); } @Provides @QualifierTeacher public Person provideTeacher() { return new Teacher(); }} 1234@Component(modules = MainModule.class)public interface MainActivityComponent { void inject(MainActivity activity);} 这里是另一个重点,被注入的变量也要用Qualifier标注,表示要用那个方法来生成对象 12345678910111213141516public class MainActivity extends AppCompatActivity { @Inject @QualifierTeacher Person person; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.create().inject(this); TextView textView = findViewById(R.id.text); textView.setText(person.saySomething()); }}]]></content>
</entry>
<entry>
<title><![CDATA[Android Dagger2 @Named用法]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Dagger2-Named%E7%94%A8%E6%B3%95%2F</url>
<content type="text"><![CDATA[如下示例代码,在Module中同时提供了两个Person的实例,如果不加以区分,就会报如下错误 这时候我们可以在用@Name来加以区分 12345678error: [Dagger/DuplicateBindings] com.him.hisapp.Person is bound multiple times:@Provides com.him.hisapp.Person com.him.hisapp.MainModule.provideStudent()@Provides com.him.hisapp.Person com.him.hisapp.MainModule.provideTeacher()com.him.hisapp.Person is injected atcom.him.hisapp.MainActivity.personcom.him.hisapp.MainActivity is injected atcom.him.hisapp.MainActivityComponent.inject(com.him.hisapp.MainActivity) 1234567891011121314151617181920212223242526272829303132333435363738394041public interface Person { String saySomething();}public class Student implements Person { public String name; public Student() { this.name = "野猿新一"; } @Override public String toString() { return String.format("我的名字叫%s啦", name); } @Override public String saySomething() { return toString(); }}public class Teacher implements Person { public String name; public Teacher() { this.name = "苍老湿"; } @Override public String toString() { return String.format("我的名字叫%s啦", name); } @Override public String saySomething() { return toString(); }} 重点在这里,用@Named来区分相同类的不同实例 123456789101112131415@Modulepublic class MainModule { @Provides @Named("student") public Person provideStudent() { return new Student(); } @Provides @Named("teacher") public Person provideTeacher() { return new Teacher(); }} 1234@Component(modules = MainModule.class)public interface MainActivityComponent { void inject(MainActivity activity);} 在具体要注入的地方也要通过@Named指定要注入哪一个 12345678910111213141516public class MainActivity extends AppCompatActivity { @Inject @Named("student") Person person; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.create().inject(this); TextView textView = findViewById(R.id.text); textView.setText(person.saySomething()); }}]]></content>
</entry>
<entry>
<title><![CDATA[Android Dagger2 @Module includes属性用法]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Dagger2-Module-includes%E5%B1%9E%E6%80%A7%E7%94%A8%E6%B3%95%2F</url>
<content type="text"><![CDATA[1234567@Modulepublic class TeacherModule { @Provides public Teacher provideTeacher() { return new Teacher(); }} MainModule通过includes包含TeacherModule 1234567@Module(includes = TeacherModule.class)public class MainModule { @Provides public Person providePerson() { return new Student(); }} 这样MainActiviyComponent最后其实是引用了MainModule和TeacherModele两个Module 1234@Component(modules = MainModule.class)public interface MainActivityComponent { void inject(MainActivity activity);} 以上其实和以下写法效果是一样的 1234567@Component(modules = { MainModule.class, TeacherModule.class})public interface MainActivityComponent { void inject(MainActivity activity);} 当然Module的includes可以多层依赖的,比如TeacherModule还可以在includes其他Module,而且includes支持包含多个 12345678910@Module(includes = { FatherModule.class, MotherModule.class})public class TeacherModule { @Provides public Teacher provideTeacher() { return new Teacher(); }}]]></content>
</entry>
<entry>
<title><![CDATA[Android Dagger2 @Component依赖多个Module]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Dagger2-Component%E4%BE%9D%E8%B5%96%E5%A4%9A%E4%B8%AAModule%2F</url>
<content type="text"><![CDATA[Dagger2中Component可以依赖单个Module,写法如下 12345678910@Component(modules = MainModule.class)public interface MainActivityComponent { void inject(MainActivity activity);}// 或者@Component(modules = {MainModule.class})public interface MainActivityComponent { void inject(MainActivity activity);} 也可以依赖多个Module,如下所示 1234567@Component(modules = { MainModule.class, TeacherModule.class})public interface MainActivityComponent { void inject(MainActivity activity);}]]></content>
</entry>
<entry>
<title><![CDATA[android Dagger2 @Module和@Provides 依赖注入]]></title>
<url>%2F2019%2F07%2F06%2Fandroid-Dagger2-Module%E5%92%8C-Provides-%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%2F</url>
<content type="text"><![CDATA[通过@Inject提供对象来依赖注入有以下两个缺点 如果要注入的对象属于某个第三方库中的类,那么我们是无法在在这个类的构造方法上添加@Inject注解的 如果要注入的对象是个abstract抽象类或者Interface接口,那么这个类似没有构造方法的,也无法添加@Inject注解 要解决以上问题,这时候就需要@Module和@Provides这对好基友上场了 二话不说,直接上代码 123public interface Person { String saySomething();} 12345678910111213141516171819public class Student implements Person { public String name; // 这边不需要再用@Inject了 public Student() { this.name = "野猿新一"; } @Override public String toString() { return String.format("我的名字叫%s啦", name); } @Override public String saySomething() { return toString(); }} 123456789@Modulepublic class MainModule { // 注意返回类型要明确是Person,而不能是子类Student // 这里我们就能够提供了一个接口的实例了 @Provides public Person providePerson() { return new Student(); }} 12345// 这里记得加载module@Component(modules = MainModule.class)public interface MainActivityComponent { void inject(MainActivity activity);} 12345678910111213141516public class MainActivity extends AppCompatActivity { // 这里声明的是一个接口 @Inject Person person; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.create().inject(this); TextView textView = findViewById(R.id.text); textView.setText(person.saySomething()); }} 运行结果,成功注入了Person对象 生成代码解析Dagger为我们生成了很多代码,我们看下注入的流程 DaggerMainActivityComponent和MainActivity_MemberInjector在前面的文章中已经有介绍就不黏贴了 我们这次主要看下跟@Module和@Provides有关的MainModule_ProvidePersonFactory的代码,代码很简单,通过module来提供注入的对象 12345678910111213141516171819202122232425public final class MainModule_ProvidePersonFactory implements Factory<Person> { private final MainModule module; public MainModule_ProvidePersonFactory(MainModule module) { this.module = module; } @Override public Person get() { return provideInstance(module); } public static Person provideInstance(MainModule module) { return proxyProvidePerson(module); } public static MainModule_ProvidePersonFactory create(MainModule module) { return new MainModule_ProvidePersonFactory(module); } public static Person proxyProvidePerson(MainModule instance) { return Preconditions.checkNotNull( instance.providePerson(), "Cannot return null from a non-@Nullable @Provides method"); }} 我们再看下整个的注入流程的代码 1DaggerMainActivityComponent.create().inject(this); DaggerMainActivityComponent中的相关代码 DaggerMainActivityComponent内部的建造器,Module在这里生成 1234567891011121314151617public static final class Builder { private MainModule mainModule; private Builder() {} public MainActivityComponent build() { if (mainModule == null) { this.mainModule = new MainModule(); } return new DaggerMainActivityComponent(this); } public Builder mainModule(MainModule mainModule) { this.mainModule = Preconditions.checkNotNull(mainModule); return this; }} 1234567891011@Overridepublic void inject(MainActivity activity) { injectMainActivity(activity);}@CanIgnoreReturnValueprivate MainActivity injectMainActivity(MainActivity instance) { MainActivity_MembersInjector.injectPerson( instance, MainModule_ProvidePersonFactory.proxyProvidePerson(mainModule)); return instance;} MainModule_ProvidePersonFactory中的proxyProvidePerson()方法,返回由module提供的Person对象 1234public static Person proxyProvidePerson(MainModule instance) { return Preconditions.checkNotNull( instance.providePerson(), "Cannot return null from a non-@Nullable @Provides method");} MainActivity_MembersInjector中的injectPerson()方法,将提供的Person对象赋值给Activity的person成员变量,完成注入 123public static void injectPerson(MainActivity instance, Person person) { instance.person = person;}]]></content>
</entry>
<entry>
<title><![CDATA[Android Dagger2 @Inject必须具体到某个类,不支持注入子类赋值给父类]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Dagger2-Inject%E5%BF%85%E9%A1%BB%E5%85%B7%E4%BD%93%E5%88%B0%E6%9F%90%E4%B8%AA%E7%B1%BB%EF%BC%8C%E4%B8%8D%E6%94%AF%E6%8C%81%E6%B3%A8%E5%85%A5%E5%AD%90%E7%B1%BB%E8%B5%8B%E5%80%BC%E7%BB%99%E7%88%B6%E7%B1%BB%2F</url>
<content type="text"><![CDATA[我们知道java的多态支持声明一个父类对象,然后实例化一个子类对象赋值给它 但是Dagger并不支持这种多态,我们可以写个例子验证下,直接上代码 123public interface Person { String saySomething();} 1234567891011121314151617181920public class Student implements Person { public String name; // 注入的是子类 @Inject public Student() { this.name = "野猿新一"; } @Override public String toString() { return String.format("我的名字叫%s啦", name); } @Override public String saySomething() { return toString(); }} 1234@Componentpublic interface MainActivityComponent { void inject(MainActivity activity);} 12345678910111213141516public class MainActivity extends AppCompatActivity { // 这里声明的是父类 @Inject Person student; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.create().inject(this); TextView textView = findViewById(R.id.text); textView.setText(student.saySomething()); }} 直接运行下,结果无法编译通过,报如下错误 12345error: [Dagger/MissingBinding] com.him.hisapp.Person cannot be provided without an @Provides-annotated method.com.him.hisapp.Person is injected atcom.him.hisapp.MainActivity.studentcom.him.hisapp.MainActivity is injected atcom.him.hisapp.MainActivityComponent.inject(com.him.hisapp.MainActivity) 计时用Modele也是没用的 1234567@Modulepublic class MainModule { @Provides public Student providePerson() { return new Student(); }} 除非我们将Modele中providePerson方法返回类型明确声明为父类Person 1234567@Modulepublic class MainModule { @Provides public Person providePerson() { return new Student(); }} 其实很容易知道为什么Dagger依赖注入不支持多态的原因,我们假设需要注入一个Object的对象,而项目中如果有很多@Inject标识的构造方法,这些构造方法生成的对象都是Object的子类对象,那么Dagger就不知道要注入哪一个,就无法编译通过。]]></content>
</entry>
<entry>
<title><![CDATA[Android Dagger2 @Inject标识在方法上注入]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Dagger2-Inject%E6%A0%87%E8%AF%86%E5%9C%A8%E6%96%B9%E6%B3%95%E4%B8%8A%E6%B3%A8%E5%85%A5%2F</url>
<content type="text"><![CDATA[Dagger除了可以在属性上表示@Inject注入对象外,还可以在方法上标识,如下所示 然后调用DaggerMainActivityComponent.create().inject(this)就可以注入对象了 123456789101112131415161718192021public class MainActivity extends AppCompatActivity { // 这里不用标识 Student student; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.create().inject(this); TextView textView = findViewById(R.id.text); textView.setText(student.toString()); } // 在方法上标识 @Inject void setStudent(Student student) { this.student = student; }} Dagger帮我们生成了很多代码,我们可以看下注入的流程: DaggerMainActivityComponent中inject()方法内部再调用injectMainActivity()方法 injectMainActivity()方法内部调用了MainActivity_MembersInjector.injectSetStudent() 12345678910@Overridepublic void inject(MainActivity activity) { injectMainActivity(activity);}@CanIgnoreReturnValueprivate MainActivity injectMainActivity(MainActivity instance) { MainActivity_MembersInjector.injectSetStudent(instance, new Student()); return instance;} 我们再看下MainActivity_MembersInjector.injectSetStudent()方法内部,直接调用Activity中定义的setStudent方法注入student对象 123public static void injectSetStudent(MainActivity instance, Student student) { instance.setStudent(student);} 我们可以对比下以上injectSetStudent()方法和用@Inject标识成员变量时生成的injectSetStudent()的区别 一个是直接赋值,一个是调用set方法赋值,基本上大同小异 123public static void injectStudent(MainActivity instance, Student student) { instance.student = student;}]]></content>
</entry>
<entry>
<title><![CDATA[Android Dagger2 Component获取某一对象实例]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Dagger2-Component%E8%8E%B7%E5%8F%96%E6%9F%90%E4%B8%80%E5%AF%B9%E8%B1%A1%E5%AE%9E%E4%BE%8B%2F</url>
<content type="text"><![CDATA[Component常见的方法定义如下所示 1234@Componentpublic interface MainActivityComponent { public Student getStudent();} 然后通过DaggerMainActivityComponent.create().inject(this)就可以注入MainActivity的所有被@Inject标识的对象 今天我们介绍Component中的另一种方法定义,可以只注入某个对象 定义如下 1234@Componentpublic interface MainActivityComponent { Student getStudent();} 然后在被注入对象中使用方法如下 被注入对象不用@Inject标识,在需要初始化的地方用getStudent()获取 其实严格来说这应该不叫依赖注入,就是单纯的获取一格对象的实例,然后赋值给MainActivity的成员变量 1234567891011121314public class MainActivity extends AppCompatActivity { Student student; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); student = DaggerMainActivityComponent.create().getStudent(); TextView textView = findViewById(R.id.text); textView.setText(student.toString()); }} 再看下Dagger为我们生成的DaggerMainActivityComponent中的代码 很简单,主要看getStudent()方法,直接new Student()返回 123456789101112131415161718192021222324252627// Generated by Dagger (https://google.github.io/dagger).package com.him.hisapp;public final class DaggerMainActivityComponent implements MainActivityComponent { private DaggerMainActivityComponent(Builder builder) {} public static Builder builder() { return new Builder(); } public static MainActivityComponent create() { return new Builder().build(); } @Override public Student getStudent() { return new Student(); } public static final class Builder { private Builder() {} public MainActivityComponent build() { return new DaggerMainActivityComponent(this); } }}]]></content>
</entry>
<entry>
<title><![CDATA[android Dagger2 多层注入]]></title>
<url>%2F2019%2F07%2F06%2Fandroid-Dagger2-%E5%A4%9A%E5%B1%82%E6%B3%A8%E5%85%A5%2F</url>
<content type="text"><![CDATA[简单来说我们要实现的就是在MainActivity中注入一个Student对象,在Student对象中又注入一个Teacher对象 实现代码二话不说,直接上代码 Student 1234567891011121314151617181920public class Student { public String name; @Inject public Teacher teacher; @Inject public Student() { this.name = "野猿新一"; } public Student(String name) { this.name = name; } @Override public String toString() { return String.format("我的名字叫%s啦,我们老师的名字叫%s", name, teacher.name); }} Teacher 123456789101112131415161718public class Teacher { public String name; @Inject public Teacher() { this.name = "苍老湿"; } public Teacher(String name) { this.name = name; } @Override public String toString() { return String.format("我的名字叫%s啦", name); }} MainActivityComponent 1234@Componentpublic interface MainActivityComponent { void inject(MainActivity activity);} MainActivity 123456789101112131415public class MainActivity extends AppCompatActivity { @Inject Student student; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.create().inject(this); TextView textView = findViewById(R.id.text); textView.setText(student.toString()); }} 运行结果 可以看到Student对象成功注入到了Activity中,而Student对象也成功注入到Student中 背后的原理Dagger通过我们的注解自动生成了很多的代码,如下这些类都是Dagger帮我们生成的 里面的代码其实很简单,我就不一一黏贴上来了,自己可以建个工程跑一下就会生成这些文件,自己点进入看一下 我们主要看下注入的流程,从MainActivity中的注入开始看: MainActivity的onCreate方法中开启注入 1DaggerMainActivityComponent.create().inject(this); DaggerMainActivityComponent中的代码,直接看很清晰 injectMainActivity完成的动作是把Student对象注入到MainActivity,通过MainActivity_MembersInjector.injectStudent()方法来完成 injectStudent完成的动作是把Teacher对象注入到Student中,通过Student_MembersInjector.injectTeacher()方法来完成 1234567891011121314151617181920@Overridepublic void inject(MainActivity activity) { injectMainActivity(activity);}@CanIgnoreReturnValueprivate MainActivity injectMainActivity(MainActivity instance) { MainActivity_MembersInjector.injectStudent(instance, getStudent()); return instance;}private Student getStudent() { return injectStudent(Student_Factory.newStudent());}@CanIgnoreReturnValueprivate Student injectStudent(Student instance) { Student_MembersInjector.injectTeacher(instance, new Teacher()); return instance;} 最后看下以下两个Injector的注入实现,很简单,直接把注入对象赋值给被注入对象内的成员变量,大功告成 MainActivity_MembersInjector.injectStudent()方法: 123public static void injectStudent(MainActivity instance, Student student) { instance.student = student;} Student_MembersInjector.injectTeacher()方法: 123public static void injectTeacher(Student instance, Teacher teacher) { instance.teacher = teacher;}]]></content>
</entry>
<entry>
<title><![CDATA[Android Dagger2 构造方法参数注入]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Dagger2-%E6%9E%84%E9%80%A0%E6%96%B9%E6%B3%95%E5%8F%82%E6%95%B0%E6%B3%A8%E5%85%A5%2F</url>
<content type="text"><![CDATA[在上一篇文章Dagger2最简单的入门我们写了一个最简单的demo来介绍Dagger2,我们通过@Inject注解构无参造器来注入对象 那么如果构造器有参数,且这个参数也需要注入,又该怎么写呢? 本篇基于上一篇文章Dagger2最简单的入门,代码片段也是以上一篇文章的demo修改的,所以看本篇之前先看上一篇 我们先增加一个Teacher类,其中的无参构造方法用@Inject标识 123456789101112131415161718public class Teacher { public String name; @Inject public Teacher() { this.name = "苍老湿"; } public Teacher(String name) { this.name = name; } @Override public String toString() { return String.format("我的名字叫%s啦", name); }} 然后修改Student类原来的无参构造方法,增加一个Teacher参数,在toString()中增加显示老师的信息 1234567891011121314151617181920public class Student { public String name; public Teacher teacher; @Inject public Student(Teacher teacher) { this.name = "野猿新一"; this.teacher = teacher; } public Student(String name) { this.name = name; } @Override public String toString() { return String.format("我的名字叫%s啦,我们老师的名字叫%s", name, teacher.name); }} ok,大工告成,就上面两步,直接运行看看 可以看到,注入Student对象的同时,也成功注入了Teacher对象 我们还是看下Dagger生成代码的注入流程 在MainActivity中调用如下代码 1DaggerMainActivityComponent.create().inject(this); DaggerMainActivityComponent中innect()方法再调用injectMainActivity()方法 injectMainActivity()方法内部调用了MainActivity_MembersInjector.injectStudent(),传入了activity对象和通过getStudent()方法获取的Student对象 我们看到getStudent()方法中直接new了一个Teacher对象,然后作为参数再new一个Student 1234567891011121314@Overridepublic void inject(MainActivity activity) { injectMainActivity(activity);}@CanIgnoreReturnValueprivate MainActivity injectMainActivity(MainActivity instance) { MainActivity_MembersInjector.injectStudent(instance, getStudent()); return instance;}private Student getStudent() { return new Student(new Teacher());} 最后看下MainActivity_MembersInjector的injectStudent()方法,直接将传入student赋值给activity的student成员变量,至此就成功把Student注入到MainActivity中 123public static void injectStudent(MainActivity instance, Student student) { instance.student = student;}]]></content>
</entry>
<entry>
<title><![CDATA[Dagger2最简单的入门]]></title>
<url>%2F2019%2F07%2F06%2FDagger2%E6%9C%80%E7%AE%80%E5%8D%95%E7%9A%84%E5%85%A5%E9%97%A8%2F</url>
<content type="text"><![CDATA[啰嗦几句这几天看Dagger看得晕头转向的,我觉得还不如动手敲几行代码,跑一跑遛一遛,边敲边理解,一口吃不成胖子,先从最简单的开始。 既然Dagger的最主要功能就是依赖注入,我就来一个炒鸡简单的demo,在一个Activity中注入一个对象,然后显示这个对象的信息,就这样。一下子讲太多的东西反而会让你望而却步。 dependencies1234dependencies { annotationProcessor "com.google.dagger:dagger-compiler:2.16" compile "com.google.dagger:dagger:2.16"} Student用@Inject注解构造方法 123456789101112131415161718public class Student { public String name; @Inject public Student() { this.name = "野猿新一"; } public Student(String name) { this.name = name; } @Override public String toString() { return String.format("我的名字叫%s啦", name); }} Component创建一个Component类,用@Component注解,这是一个注入类,先依样画葫芦照着写就是了,inject方法的参数是我们要在里面注入对象的MainActivity 1234@Componentpublic interface MainActivityComponent { void inject(MainActivity activity);} MainActivity在MainActivity里写一个全局变量student,用@Inject注解,注意被注入的对象不能为private 然后在onCreate里调用DaggerMainActivityComponent.create().inject(this)就可以成功注入student了 接下来就可以使用这个对象了 如果报找不到DaggerMainActivityComponent可以先菜单Build->Make Project编译一下 123456789101112131415public class MainActivity extends AppCompatActivity { @Inject Student student; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.create().inject(this); TextView textView = findViewById(R.id.text); textView.setText(student.toString()); }} 运行大功告成,直接运行,看下结果 是不是很神奇,我们只是声明了一个Student对象并没有实例化,却可以直接使用,这就是Dagger依赖注入的神奇之处 背后的原理你可能会有疑问我们创建的是MainActivityComponent,那DaggerMainActivityComponent是哪来的呢? 是Dagger根据MainActivityComponent为我们创建,不仅如此,还在以下路径创建了以下类 我们分别看下生成的这三个类的代码 Student_Factory 一个Seudent的工厂类,代码很简单 12345678910111213141516171819202122232425// Generated by Dagger (https://google.github.io/dagger).package com.him.hisapp;import dagger.internal.Factory;public final class Student_Factory implements Factory<Student> { private static final Student_Factory INSTANCE = new Student_Factory(); @Override public Student get() { return provideInstance(); } public static Student provideInstance() { return new Student(); } public static Student_Factory create() { return INSTANCE; } public static Student newStudent() { return new Student(); }} MainActivity_MembersInjector 看名字就知道这是个注入器,Component就是调用这个注入器来把变量注入Activity的 1234567891011121314151617181920212223242526// Generated by Dagger (https://google.github.io/dagger).package com.him.hisapp;import dagger.MembersInjector;import javax.inject.Provider;public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> { private final Provider<Student> studentProvider; public MainActivity_MembersInjector(Provider<Student> studentProvider) { this.studentProvider = studentProvider; } public static MembersInjector<MainActivity> create(Provider<Student> studentProvider) { return new MainActivity_MembersInjector(studentProvider); } @Override public void injectMembers(MainActivity instance) { injectStudent(instance, studentProvider.get()); } public static void injectStudent(MainActivity instance, Student student) { instance.student = student; }} DaggerMainActivityComponent 代码也很简单 1234567891011121314151617181920212223242526272829303132333435// Generated by Dagger (https://google.github.io/dagger).package com.him.hisapp;import com.google.errorprone.annotations.CanIgnoreReturnValue;public final class DaggerMainActivityComponent implements MainActivityComponent { private DaggerMainActivityComponent(Builder builder) {} public static Builder builder() { return new Builder(); } public static MainActivityComponent create() { return new Builder().build(); } @Override public void inject(MainActivity activity) { injectMainActivity(activity); } @CanIgnoreReturnValue private MainActivity injectMainActivity(MainActivity instance) { MainActivity_MembersInjector.injectStudent(instance, new Student()); return instance; } public static final class Builder { private Builder() {} public MainActivityComponent build() { return new DaggerMainActivityComponent(this); } }} 我们从MainActivity里DaggerMainActivityComponent.create().inject(this)的调用开始一层层进入就可以知道整个的注入流程 12// MainActivity的onCreate()中调用DaggerMainActivityComponent.create().inject(this); DaggerMainActivityComponent的inject()方法里再调用injectMainActivity(),在这里就创建了Student对象,然后和activity对象一起传入MainActivity_MembersInjector.injectStudent()方法 12345678910@Overridepublic void inject(MainActivity activity) { injectMainActivity(activity);}@CanIgnoreReturnValueprivate MainActivity injectMainActivity(MainActivity instance) { MainActivity_MembersInjector.injectStudent(instance, new Student()); return instance;} 在MainActivity_MembersInjector的injectStudent方法里可看到,把student实例对象赋给了MainActivity的student成员变量 123public static void injectStudent(MainActivity instance, Student student) { instance.student = student;} 看完整个流程是不是就明白了注入的过程 Dagger帮我们生成代码,做了很多工作,我们只需要简单写一些注解就可以完成对象的依赖注入。]]></content>
</entry>
<entry>
<title><![CDATA[Android 官方架构示例android-architecture之todo-mvp深入解析]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-%E5%AE%98%E6%96%B9%E6%9E%B6%E6%9E%84%E7%A4%BA%E4%BE%8Bandroid-architecture%E4%B9%8Btodo-mvp%E6%B7%B1%E5%85%A5%E8%A7%A3%E6%9E%90%2F</url>
<content type="text"><![CDATA[google在GitHub上开源了android-architecture项目,包含了MVP、MVVM等架构的示例项目,今天我们从todo‑mvp开始入手,研究里面代码的具体实现 项目地址todo-mvp项目地址 应用功能介绍了解一个项目的主要功能最快的方法就是直接安装,然后运行,就可以知道主要有哪些页面,有哪些功能 todo示例项目是一个代办事项的简单App,有四个页面 待办列表页 待办详情页 待办编辑页 待办统计页 项目结构我们主要研究MVP代码的实现,androidTest、androidTestMock、mock、test下是测试的代码,不在我们本次的讨论范围内,下次会另开一篇文章讨论。 项目分包主要以业务模块来划分 tasks包:待办列表模块 taskdetail包:待办详情模块 addedittask包:新建/编辑待办模块 statistics包:待办统计模块 data包:跟MVP中V有关的模块 util包:工具模块 BaseView接口:MVP中V的基础接口 BasePresenter包接口:MVP中P的基础接口 我们先看下BaseView的定义,每一个具体的业务模块的View都要实现该接口,通过setPresenter方法持有Presenter的对象引用,然后通过Presenter对象调用具体的业务逻辑 123public interface BaseView<T> { void setPresenter(T presenter);} 我们再看下BasePresenter的定义,每一个具体业务模块的Presenter都需要实现该接口,需实现start()方法做一些初始化的业务,而该方法一般是在Fragment的onResume中调用(单不是绝对的,视具体业务而定) 123public interface BasePresenter { void start();} MVP架构概览如下图所示,我们具体展开每一个业务模块,可以发现每个模块的设计都是一样的,主要有四个类型的类 Contract:合同类,这是一个接口,里面又定义了两个子接口View和Presenter Activity:只是一个Fragment的容器,具体的View实现交个Fragment Fragment:实现Contract中定义的View接口,承担View的角色,负责页面的显示刷新交互,持有Presenter的引用,调用Presenter的相关业务实现 Presenter:实现Contract中定义的Presenter接口,承担Presenter角色,负责具体的业务逻辑,在Presenter中会可能会调用Model对数据进行操作,然后通过持有的View对象的引用回调View,操作更新页面 V和P具体代码实现由于每一个具体业务模块的实现代码都大同小异,我们选取待办编辑这一业务模块来看看具体的实现代码 待办列表模块的业务功能比较多,代码量也最多,为了快速入门,我们选择待办编辑代码量比较适中,也不会像待办统计也代码量太少。 待办编辑模块主要有两个功能,一个是新建待办然后编辑保存,一个是编辑已有的待办然后保存 待办编辑模块主要有如下四个类,我们按顺序讲解。 AddEditTaskContract AddEditTaskActivity AddEditTaskFragment AddEditTaskPresenter AddEditTaskContract Contract定义了View和Presenter之间的一组协议 View继承BaseView,负责UI的操作更新 Presenter继承BasePresenter,负责具体的业务逻辑,有可能会调用Model的功能 1234567891011121314151617181920212223public interface AddEditTaskContract { interface View extends BaseView<Presenter> { // 保存待办时如果是空任务的情况的显示提醒 void showEmptyTaskError(); // 保存成功后返回待办列表页面 void showTasksList(); // 设置待办标题 void setTitle(String title); // 设置待办说明 void setDescription(String description); // 当前View是否已销毁,一般在Presenter中更新UI前都要调用该方法判断 boolean isActive(); } interface Presenter extends BasePresenter { // 保存待办 void saveTask(String title, String description); // 查询已有的待办事项,在初始进入的时候且是已有待办的情况下调用 void populateTask(); // 返回一个标志字段判断是否需要重新加载数据 boolean isDataMissing(); }} AddEditTaskActivity Activity的功能比较简单,主要是负责加载Fragment和创建Presenter对象 不承担View和Presenter的功能 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879public class AddEditTaskActivity extends AppCompatActivity { public static final int REQUEST_ADD_TASK = 1; public static final String SHOULD_LOAD_DATA_FROM_REPO_KEY = "SHOULD_LOAD_DATA_FROM_REPO_KEY"; private AddEditTaskPresenter mAddEditTaskPresenter; private ActionBar mActionBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.addtask_act); // 一些ToolBar初始化设置 Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); mActionBar = getSupportActionBar(); mActionBar.setDisplayHomeAsUpEnabled(true); mActionBar.setDisplayShowHomeEnabled(true); // 如果是从待办列表页面点击某个具体待办跳转过来,会传过来待办的id String taskId = getIntent().getStringExtra(AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID); // 设置标题,如果是新建待办显示“New TO-DO”,如果是编辑已有待办,显示“Edit TO-DO” setToolbarTitle(taskId); AddEditTaskFragment addEditTaskFragment = (AddEditTaskFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (addEditTaskFragment == null) { addEditTaskFragment = AddEditTaskFragment.newInstance(); // 编辑已有任务需将待办id传递给Fragment if (getIntent().hasExtra(AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID)) { Bundle bundle = new Bundle(); bundle.putString(AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID, taskId); addEditTaskFragment.setArguments(bundle); } // 将Fragment添加到Activity中 ActivityUtils.addFragmentToActivity(getSupportFragmentManager(), addEditTaskFragment, R.id.contentFrame); } boolean shouldLoadDataFromRepo = true; // 防止设置更改(如横竖屏切换)情况下又重复请求一次数据 if (savedInstanceState != null) { // Data might not have loaded when the config change happen, so we saved the state. shouldLoadDataFromRepo = savedInstanceState.getBoolean(SHOULD_LOAD_DATA_FROM_REPO_KEY); } // 实例化Presenter,传入addEditTaskFragment持有View对象 mAddEditTaskPresenter = new AddEditTaskPresenter( taskId, Injection.provideTasksRepository(getApplicationContext()), addEditTaskFragment, shouldLoadDataFromRepo); } private void setToolbarTitle(@Nullable String taskId) { if(taskId == null) { mActionBar.setTitle(R.string.add_task); } else { mActionBar.setTitle(R.string.edit_task); } } @Override protected void onSaveInstanceState(Bundle outState) { // Save the state so that next time we know if we need to refresh data. outState.putBoolean(SHOULD_LOAD_DATA_FROM_REPO_KEY, mAddEditTaskPresenter.isDataMissing()); super.onSaveInstanceState(outState); } @Override public boolean onSupportNavigateUp() { onBackPressed(); return true; } @VisibleForTesting public IdlingResource getCountingIdlingResource() { return EspressoIdlingResource.getIdlingResource(); }} AddEditTaskFragment AddEditTaskFragment功能很简单,就是编辑待办事项,可以是编辑新建的待办,也可以是编辑已有的待办,然后最后保存返回待办列表页面。 AddEditTaskFragment通过setPresenter持有AddEditTaskPresenter对象示例 在onResume的地方调用Presenter请求已有的任务(如果是已有任务的情况下) 在右下角完成按钮点击的时候调用Presenter的saveTask()方法保存任务 View的主要功能就是: 事件触发后调用Presenter处理具体的业务 实现Contract中定义的方法操作显示UI(等待Presenter业务处理完的回调) 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View { public static final String ARGUMENT_EDIT_TASK_ID = "EDIT_TASK_ID"; private AddEditTaskContract.Presenter mPresenter; private TextView mTitle; private TextView mDescription; public static AddEditTaskFragment newInstance() { return new AddEditTaskFragment(); } public AddEditTaskFragment() { // Required empty public constructor } @Override public void onResume() { super.onResume(); // Presenter.start的具体实现中,如果是已有的待办事项,会先查询 mPresenter.start(); } // 设置Presenter,持有该对象的引用 @Override public void setPresenter(@NonNull AddEditTaskContract.Presenter presenter) { mPresenter = checkNotNull(presenter); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // 右下角的完成编辑保存按钮 FloatingActionButton fab = getActivity().findViewById(R.id.fab_edit_task_done); fab.setImageResource(R.drawable.ic_done); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 调用Presenter实现具体保存业务逻辑 mPresenter.saveTask(mTitle.getText().toString(), mDescription.getText().toString()); } }); } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.addtask_frag, container, false); mTitle = root.findViewById(R.id.add_task_title); mDescription = root.findViewById(R.id.add_task_description); setHasOptionsMenu(true); return root; } // 保存待办时如果是空任务的情况的显示提醒 @Override public void showEmptyTaskError() { Snackbar.make(mTitle, getString(R.string.empty_task_message), Snackbar.LENGTH_LONG).show(); } // 保存成功后返回待办列表页面 @Override public void showTasksList() { getActivity().setResult(Activity.RESULT_OK); getActivity().finish(); } // 设置待办标题 @Override public void setTitle(String title) { mTitle.setText(title); } // 设置待办说明 @Override public void setDescription(String description) { mDescription.setText(description); } // 当前View是否已销毁,一般在Presenter中更新UI前都要调用该方法判断 @Override public boolean isActive() { return isAdded(); }} AddEditTaskPresenter AddEditTaskPresenter实现了AddEditTaskContract.Presenter中定义的接口 在View中触发事件,然后为了View跟Model层的解耦,甩锅给Presenter让Presenter调用Model执行一些增删改查等操作(本地数据库操作或者网络请求操作),最后再通过Presenter回调View层处理更新UI。整个过程View跟Model是解耦的。 所以Presenter就是View和Model之间的桥梁,是个跑腿的,举个栗子: View说:喂,Presenter,去Model那边帮我买一瓶酱油回来 Presenter:屁颠屁颠的跑去Model那边买了瓶酱油,然后再跑回去拿给View View:拿到酱油后炒了一盘炒饭发到朋友圈显示出来 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112/** * Listens to user actions from the UI ({@link AddEditTaskFragment}), retrieves the data and updates * the UI as required. */public class AddEditTaskPresenter implements AddEditTaskContract.Presenter, TasksDataSource.GetTaskCallback { @NonNull private final TasksDataSource mTasksRepository; @NonNull private final AddEditTaskContract.View mAddTaskView; @Nullable private String mTaskId; private boolean mIsDataMissing; /** * Creates a presenter for the add/edit view. * * @param taskId ID of the task to edit or null for a new task * @param tasksRepository a repository of data for tasks * @param addTaskView the add/edit view * @param shouldLoadDataFromRepo whether data needs to be loaded or not (for config changes) */ public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository, @NonNull AddEditTaskContract.View addTaskView, boolean shouldLoadDataFromRepo) { mTaskId = taskId; mTasksRepository = checkNotNull(tasksRepository); mAddTaskView = checkNotNull(addTaskView); mIsDataMissing = shouldLoadDataFromRepo; mAddTaskView.setPresenter(this); } // 如果是已有的待办,查询该待办的信息 @Override public void start() { if (!isNewTask() && mIsDataMissing) { populateTask(); } } // 完成编辑 @Override public void saveTask(String title, String description) { if (isNewTask()) { createTask(title, description); } else { updateTask(title, description); } } @Override public void populateTask() { if (isNewTask()) { throw new RuntimeException("populateTask() was called but task is new."); } // 通过Repository数据仓库根据taskId查询该待办信息,然后在callback中回调UI显示待办信息 mTasksRepository.getTask(mTaskId, this); } @Override public void onTaskLoaded(Task task) { // 查询成功后回调UI显示,显示前先判断View是否已销毁 // The view may not be able to handle UI updates anymore if (mAddTaskView.isActive()) { mAddTaskView.setTitle(task.getTitle()); mAddTaskView.setDescription(task.getDescription()); } mIsDataMissing = false; } @Override public void onDataNotAvailable() { // 查询失败后回调UI显示,显示前先判断View是否已销毁 // The view may not be able to handle UI updates anymore if (mAddTaskView.isActive()) { mAddTaskView.showEmptyTaskError(); } } @Override public boolean isDataMissing() { return mIsDataMissing; } // 判断是已有待办或者是新建待办 private boolean isNewTask() { return mTaskId == null; } // 通过Model保存新创建的待办,然后返回待办列表页面 private void createTask(String title, String description) { Task newTask = new Task(title, description); if (newTask.isEmpty()) { mAddTaskView.showEmptyTaskError(); } else { mTasksRepository.saveTask(newTask); mAddTaskView.showTasksList(); } } // 通过Model保存已有待办的更新,然后返回待办列表页面 private void updateTask(String title, String description) { if (isNewTask()) { throw new RuntimeException("updateTask() was called but task is new."); } mTasksRepository.saveTask(new Task(title, description, mTaskId)); mAddTaskView.showTasksList(); // After an edit, go back to the list. }} Model设计本示例中Mode层在data包下,又分为local和remote两个包。 local表示本地数据库数据 remote表示远程服务端数据,但是本示例并未真正实现服务端接口的请求,只是用一个LinkedHashMap增删改查待办任务,然后用Handler.postDelay来模拟异步请求 TaskDatasource定义了数据源的接口,包括获取所有待办事项、获取单个待办事项、创建待办事项、完成待办事项等 TaskLocalDataSource实现了TaskDatasource,实现了本地的数据操作 TaskRemoteDataSource实现了TaskDataSource,模拟了网络异步的数据操作 TaskRepository代表的就是View层,Presenter中持有的View对象就是TaskRepository,TaskRepository负责提供数据和对数据进行操作,然后把结果返回给Presenter,Presenter再回调View更新视图。 TaskRepository也实现了TaskDatasource接口,内部同时持有TaskLocalDataSource和TaskRemoteDataSource两种数据源,同时还有一个Map内存缓存,对待办数据进行增加、删除、修改等操作是会同时对内存缓存、TaskLocalDataSource、TaskRemoteDataSource三种数据源进行操作。 总结盗用一张百科的图片,如有侵权请联系删除 View和Model彻底解耦 Presenter是View和Model之间的桥梁 一个完整流程包含以下四个步骤: View中事件触发 Presenter调用Model请求处理数据 Model处理完数据返回给Presenter Presenter回调View更新UI]]></content>
</entry>
<entry>
<title><![CDATA[解决Could not find manifest-merger.jar问题]]></title>
<url>%2F2019%2F07%2F06%2F%E8%A7%A3%E5%86%B3Could-not-find-manifest-merger-jar%E9%97%AE%E9%A2%98%2F</url>
<content type="text"><![CDATA[Android Studio编译的时候报如下错误 123ERROR: Could not find manifest-merger.jar (com.android.tools.build:manifest-merger:26.0.0).Searched in the following locations: https://jcenter.bintray.com/com/android/tools/build/manifest-merger/26.0.0/manifest-merger-26.0.0.jar 解决办法是在项目的build.gradle文件中将google()换到jcenter()前即可,如下所示 12345678910111213141516buildscript { repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.3.0' }}allprojects { repositories { google() jcenter() }}]]></content>
</entry>
<entry>
<title><![CDATA[解决Unable to resolve dependency for ':app@debugAndroidTest/compileClasspath'问题]]></title>
<url>%2F2019%2F07%2F06%2F%E8%A7%A3%E5%86%B3Unable-to-resolve-dependency-for-app-debugAndroidTest-compileClasspath-%E9%97%AE%E9%A2%98%2F</url>
<content type="text"><![CDATA[Android Studio有时候会抽风出现各种问题,有时候项目会报各种找不到类,但是编译运行按钮却可以按,而且可以成功编译安装应用,或者是报类似如下的问题 1Unable to resolve dependency for ':app@debugAndroidTest/compileClasspath' 一般都是Build->Clen Project可以解决大部分的问题 若还是不行可以尝试File -> Invalidate Caches / Restart -> Invalidate and Restart,该方法会使缓存无效并重启Android Studio,一般重启后那些讨厌的报红就会消失了。]]></content>
</entry>
<entry>
<title><![CDATA[java import static 用法]]></title>
<url>%2F2019%2F07%2F06%2Fjava-import-static-%E7%94%A8%E6%B3%95%2F</url>
<content type="text"><![CDATA[在项目中有时候在同个Java文件中会多次用到某个工具类的static静态方法,这时候我们可以用import static导入该方法,然后可以直接可以直接使用该方法就像是改类中的方法一样,如下代码所示 12345678910import static android.text.TextUtils.isEmpty;public class MyClass { public void myFun() { if (isEmpty("野猿新一")) { System.out.println("空空空"); } else { System.out.println("不空不空"); } }} 如果看谷歌官方的很多demo会发现import static经常用到,比如经常见到的Guava包中的checkNotNull方法,该方法可以用来判空,如果参数为空会抛出NullPointerException异常 12345import static com.google.common.base.Preconditions.checkNotNull;public void fun(Object obj) { checkNotNull(obj);}]]></content>
</entry>
<entry>
<title><![CDATA[解决Unknown host 'jcenter.bintray.com'. You may need to adjust the proxy setting]]></title>
<url>%2F2019%2F07%2F06%2F%E8%A7%A3%E5%86%B3Unknown-host-jcenter-bintray-com-You-may-need-to-adjust-the-proxy-setting%2F</url>
<content type="text"><![CDATA[有时候从GitHub上下的项目加载老半天依赖下不下来,主要报以下错误 123Unknown host 'jcenter.bintray.com'. You may need to adjust the proxy settings in Gradle.Enable Gradle 'offline mode' and sync projectLearn about configuring HTTP proxies in Gradle 或者如下错误 123ERROR: Failed to resolve: com.google.guava:guava:27.0.0-androidShow in Project Structure dialogAffected Modules: app 解决办法只要在项目项目build.gradle文件中添加maven { url”https://jitpack.io"}即可 123456789101112131415161718buildscript { repositories { google() jcenter() maven {url"https://jitpack.io"} } dependencies { classpath 'com.android.tools.build:gradle:3.3.0' }}allprojects { repositories { google() jcenter() maven {url"https://jitpack.io"} }}]]></content>
</entry>
<entry>
<title><![CDATA[Android Retrofit+RxJava 取消网络请求]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Retrofit-RxJava-%E5%8F%96%E6%B6%88%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82%2F</url>
<content type="text"><![CDATA[可以通过Observer监听的onSubscribe回调方法中取得Disposable,然后通过Disposable对象来取消请求。 实例代码如下 123456789101112131415161718192021222324252627282930313233343536373839404142private Disposable loginDisposable;public void request() { Retrofit retrofit = new Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl("http://www.yeyuanxinyi.com/YeYuanXinYi") .build(); HttpService service = retrofit.create(HttpService.class); Observable<Account> observable = service.login("yeyuanxinyi", "123456"); observable.subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Account>() { @Override public void onSubscribe(Disposable d) { // 在这里保存Disposable对象 loginDisposable = d; } @Override public void onNext(Account t) { } @Override public void onError(Throwable e) { } @Override public void onComplete() { } });}// 取消请求private void cancelLogin() { if (loginDisposable != null && !loginDisposable.isDisposed()) { loginDisposable.dispose(); }}]]></content>
</entry>
<entry>
<title><![CDATA[解决java.lang.IllegalArgumentException: Unable to create call adapter for io.reactivex.Observable问题]]></title>
<url>%2F2019%2F07%2F06%2F%E8%A7%A3%E5%86%B3java-lang-IllegalArgumentException-Unable-to-create-call-adapter-for-io-reactivex-Observable%E9%97%AE%E9%A2%98%2F</url>
<content type="text"><![CDATA[使用Retrofit2结合RxJava2的网络请求方式,请求的时候报如下错误,原因是adapter-rxjava包引用错了,由于用的RxJava2,所以用的依赖的adapter库也应该是RxJava2的,报错的原因就是依赖城RxJava1的adapter了 1234567891011121314151617181920212223242526272829303132333406-01 16:20:12.373 25257-25257/com.him.hisapp E/AndroidRuntime: FATAL EXCEPTION: main Process: com.him.hisapp, PID: 25257 java.lang.IllegalArgumentException: Unable to create call adapter for io.reactivex.Observable<com.him.hisapp.http.Resp.RespBase<java.util.List<com.him.hisapp.http.Resp.Account>>> for method HttpService.login2 at retrofit2.Utils.methodError(Utils.java:52) at retrofit2.HttpServiceMethod.createCallAdapter(HttpServiceMethod.java:60) at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:34) at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:36) at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:168) at retrofit2.Retrofit$1.invoke(Retrofit.java:147) at java.lang.reflect.Proxy.invoke(Proxy.java:397) at $Proxy0.login2(Unknown Source) at com.him.hisapp.http.HttpServiceImpl.login2(HttpServiceImpl.java:147) at com.him.hisapp.MainActivity.login(MainActivity.java:31) at com.him.hisapp.MainActivity.access$000(MainActivity.java:15) at com.him.hisapp.MainActivity$1.onClick(MainActivity.java:25) at android.view.View.performClick(View.java:4785) at android.view.View$PerformClick.run(View.java:19888) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5276) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:911) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:706) Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for io.reactivex.Observable<com.him.hisapp.http.Resp.RespBase<java.util.List<com.him.hisapp.http.Resp.Account>>>. Tried: * retrofit2.adapter.rxjava.RxJavaCallAdapterFactory * retrofit2.ExecutorCallAdapterFactory at retrofit2.Retrofit.nextCallAdapter(Retrofit.java:239) at retrofit2.Retrofit.callAdapter(Retrofit.java:203) at retrofit2.HttpServiceMethod.createCallAdapter(HttpServiceMethod.java:58) ... 20 more 错误的写法1234567891011dependencies { implementation 'com.squareup.retrofit2:adapter-rxjava:2.5.0'}public Retrofit getRetrofit() { retuan new Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .baseUrl(getBaseUrl()) .build();} 正确的写法12345678910111213dependencies { // 改成adapter-rxjava2 implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'}public Retrofit getRetrofit() { retuan new Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) // 改成RxJava2CallAdapterFactory .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl(getBaseUrl()) .build();} 总结 依赖com.squareup.retrofit2:adapter-rxjava:2.5.0改成com.squareup.retrofit2:adapter-rxjava2:2.5.0 adapter RxJavaCallAdapterFactory改成RxJava2CallAdapterFactory]]></content>
</entry>
<entry>
<title><![CDATA[Android Retrofit OkHttp添加Interceptor拦截器设置POST请求公共参数]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Retrofit-OkHttp%E6%B7%BB%E5%8A%A0Interceptor%E6%8B%A6%E6%88%AA%E5%99%A8%E8%AE%BE%E7%BD%AEPOST%E8%AF%B7%E6%B1%82%E5%85%AC%E5%85%B1%E5%8F%82%E6%95%B0%2F</url>
<content type="text"><![CDATA[12345678910111213141516171819202122232425262728293031323334353637private HttpServiceImpl() { OkHttpClient httpClient = new OkHttpClient.Builder() .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS) // 对请求进行拦截处理,添加统一的请求头及打印请求报文和返回报文 .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); if ("POST".equals(request.method())) { if (request.body() instanceof FormBody) { FormBody.Builder bodyBuilder = new FormBody.Builder(); FormBody formBody = (FormBody) request.body(); // 先复制原来的参数 for (int i = 0; i < formBody.size(); i++) { bodyBuilder.addEncoded(formBody.encodedName(i), formBody.encodedValue(i)); } // 添加公共参数 formBody = bodyBuilder .addEncoded("version", "1.0.0") .addEncoded("appkey", "yeyuanxinyi") .addEncoded("timestamp", String.valueOf(System.currentTimeMillis())) .build(); request = request.newBuilder().post(formBody).build(); } } return chain.proceed(request); } }) .build(); return new Retrofit.Builder() .client(httpClient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl(getBaseUrl()) .build();}]]></content>
</entry>
<entry>
<title><![CDATA[Android Retrofit通过OkHttp添加Interceptor拦截器设置Get请求公共参数]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Retrofit%E9%80%9A%E8%BF%87OkHttp%E6%B7%BB%E5%8A%A0Interceptor%E6%8B%A6%E6%88%AA%E5%99%A8%E8%AE%BE%E7%BD%AEGet%E8%AF%B7%E6%B1%82%E5%85%AC%E5%85%B1%E5%8F%82%E6%95%B0%2F</url>
<content type="text"><![CDATA[12345678910111213141516171819202122232425pubic Retrofit getRetrofit() { OkHttpClient httpClient = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); if ("GET".equals(request.method())) { HttpUrl httpUrl = request.url().newBuilder() .addQueryParameter("version", "1.0.0") .addQueryParameter("appkey","yeyuanxinyi") .addQueryParameter("timestamp", String.valueOf(System.currentTimeMillis())) .build(); request = request.newBuilder().url(httpUrl).build(); } return chain.proceed(request); } }) .build(); return new Retrofit.Builder() .client(httpClient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl(getBaseUrl()) .build();}]]></content>
</entry>
<entry>
<title><![CDATA[Android Retrofit通过OkHttp设置Interceptor拦截器统一打印请求报文及返回报文]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Retrofit%E9%80%9A%E8%BF%87OkHttp%E8%AE%BE%E7%BD%AEInterceptor%E6%8B%A6%E6%88%AA%E5%99%A8%E7%BB%9F%E4%B8%80%E6%89%93%E5%8D%B0%E8%AF%B7%E6%B1%82%E6%8A%A5%E6%96%87%E5%8F%8A%E8%BF%94%E5%9B%9E%E6%8A%A5%E6%96%87%2F</url>
<content type="text"><![CDATA[我们先定义一个打印报文的拦截器,继承Interceptor 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152public class LogInterceptor implements Interceptor { private static final String TAG = LogInterceptor.class.getSimpleName(); @Override public Response intercept(Chain chain) throws IOException { Charset UTF8 = Charset.forName("UTF-8"); // 打印请求报文 Request request = chain.request(); RequestBody requestBody = request.body(); String reqBody = null; if(requestBody != null) { Buffer buffer = new Buffer(); requestBody.writeTo(buffer); Charset charset = UTF8; MediaType contentType = requestBody.contentType(); if (contentType != null) { charset = contentType.charset(UTF8); } reqBody = buffer.readString(charset); } Log.d(TAG, String.format("发送请求\nmethod:%s\nurl:%s\nheaders: %s\nbody:%s", request.method(), request.url(), request.headers(), reqBody)); // 打印返回报文 // 先执行请求,才能够获取报文 Response response = chain.proceed(request); ResponseBody responseBody = response.body(); String respBody = null; if(responseBody != null) { BufferedSource source = responseBody.source(); source.request(Long.MAX_VALUE); Buffer buffer = source.buffer(); Charset charset = UTF8; MediaType contentType = responseBody.contentType(); if (contentType != null) { try { charset = contentType.charset(UTF8); } catch (UnsupportedCharsetException e) { e.printStackTrace(); } } respBody = buffer.clone().readString(charset); } Log.d(TAG, String.format("收到响应\n%s %s\n请求url:%s\n请求body:%s\n响应body:%s", response.code(), response.message(), response.request().url(), reqBody, respBody)); return response; }} 然后通过OkHttp设置拦截器 1234567891011private Retrofit getRetrofit() { OkHttpClient httpClient = new OkHttpClient.Builder() .addInterceptor(new LogInterceptor()) .build(); return new Retrofit.Builder() .client(httpClient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl(getBaseUrl()) .build();} 打印出来的报文如下所示 1234567891011121306-01 14:36:35.223 16547-16593/com.him.hisapp D/HttpServiceImpl: 发送请求 method:POST url:http://www.yeyuanxinyi.com:6666/api/YeYuanXinYi headers: header1: headerValue1 header2: headerValue2 header3: headerValue3 body:{"user_name":"yeyuanxinyi","age":"28","class":"3"}06-01 14:36:39.003 16547-16593/com.him.hisapp D/HttpServiceImpl: 收到响应 200 OK 请求url:http://110.86.9.67:8401/api/VipSelfCashRegister/GetSysUser 请求body:{"customerCode":"VIP","passWord":"666666","username":"00088"} 响应body:{"success":true,"message":null,"code":null,"data":{"user_name":"yeyuanxinyi","age":"28","class":"3"}}]]></content>
</entry>
<entry>
<title><![CDATA[Android Retrofit Interceptor拦截器统一设置请求头Header]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Retrofit-Interceptor%E6%8B%A6%E6%88%AA%E5%99%A8%E7%BB%9F%E4%B8%80%E8%AE%BE%E7%BD%AE%E8%AF%B7%E6%B1%82%E5%A4%B4Header%2F</url>
<content type="text"><![CDATA[我们知道Retrofit可以通过@Headers和@Header为每个接口设置请求头,但是有些情况下项目中的所有接口都需设置某几个相同的请求头,如果还一个个设置显得过于麻烦,我们可以通过Interceptor拦截所有请求报文,然后在请求对象中统一设置请求头。 123456789101112131415161718192021222324private Retrofit createRetrofit() { OkHttpClient httpClient = new OkHttpClient.Builder() // 设置拦截器,添加统一的请求头 .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { // 以拦截到的请求为基础创建一个新的请求对象,然后插入Header Request request = chain.request().newBuilder() .addHeader("app_key", "myAppKey") .addHeader("sign", "12345678") .addHeader("times_tamp", "2019-06-01 13:47:45") .build(); // 开始请求 return chain.proceed(request); } }) .build(); return new Retrofit.Builder() .client(httpClient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl("http://www.yeyuanxinnyi.com/retrofit") .build();}]]></content>
</entry>
<entry>
<title><![CDATA[Android Retrofit2 设置请求头Header]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Retrofit2-%E8%AE%BE%E7%BD%AE%E8%AF%B7%E6%B1%82%E5%A4%B4Header%2F</url>
<content type="text"><![CDATA[@Headers设置固定请求头123456789public interface HttpService { @Headers({ "header1:headerValue1", "header2:headerValue2", "header3:headerValue3", }) @POST("Login") Call<Account> login(@Body ReqLogin body);} @Header设置上可变请求头1234public interface HttpService { @POST("login") Call<Account> login(@Body ReqLogin body, @Header("token") String token);}]]></content>
</entry>
<entry>
<title><![CDATA[Android ImageView setAlpha(float)、setAlpha(int)及setImageAlpha(int)的区别及踩过的坑]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-ImageView-setAlpha-float-%E3%80%81setAlpha-int-%E5%8F%8AsetImageAlpha-int-%E7%9A%84%E5%8C%BA%E5%88%AB%E5%8F%8A%E8%B8%A9%E8%BF%87%E7%9A%84%E5%9D%91%2F</url>
<content type="text"><![CDATA[做项目中踩过的一个坑,记录一下 需求是先把ImageView设置成透明不可见,然后在某个条件下再设置成可见,代码如下 12mImageView.setAlpha(0); // 设置成透明mImageView.setAlpha(1f); // 设置成不透明 结果是设置成透明可以,但是要再设置成不透明显示控件却死活不可以,后来改成如下代码就可以了 12mImageView.setAlpha(0f); // 设置成透明mImageView.setAlpha(1f); // 设置成不透明 原来setAlpha(0)和setAlpha(0f)分别调用的是不同的方法 setAlpha(int)这是在ImageView中定义的方法,已经被废弃了,取值范围是0-255, 0表示透明 255表示不透明 所以如果要设置ImageView为不透明,正确的值是255,而不是1 123456789101112131415161718/** * Sets the alpha value that should be applied to the image. * * @param alpha the alpha value that should be applied to the image * * @deprecated use #setImageAlpha(int) instead */@Deprecated@RemotableViewMethodpublic void setAlpha(int alpha) { alpha &= 0xFF; // keep it legal if (mAlpha != alpha) { mAlpha = alpha; mColorMod = true; applyColorMod(); invalidate(); }} setAlpha(float)这其实是在ImageView的父类View中定义的方法,推荐用此方法,取值范围为0.0f-1.0f 0.0f表示透明 1.0f表示不透明 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253/** * Sets the opacity of the view to a value from 0 to 1, where 0 means the view is * completely transparent and 1 means the view is completely opaque. * * <p class="note"><strong>Note:</strong> setting alpha to a translucent value (0 < alpha < 1) * can have significant performance implications, especially for large views. It is best to use * the alpha property sparingly and transiently, as in the case of fading animations.</p> * * <p>For a view with a frequently changing alpha, such as during a fading animation, it is * strongly recommended for performance reasons to either override * {@link #hasOverlappingRendering()} to return <code>false</code> if appropriate, or setting a * {@link #setLayerType(int, android.graphics.Paint) layer type} on the view for the duration * of the animation. On versions {@link android.os.Build.VERSION_CODES#M} and below, * the default path for rendering an unlayered View with alpha could add multiple milliseconds * of rendering cost, even for simple or small views. Starting with * {@link android.os.Build.VERSION_CODES#M}, {@link #LAYER_TYPE_HARDWARE} is automatically * applied to the view at the rendering level.</p> * * <p>If this view overrides {@link #onSetAlpha(int)} to return true, then this view is * responsible for applying the opacity itself.</p> * * <p>On versions {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1} and below, note that if * the view is backed by a {@link #setLayerType(int, android.graphics.Paint) layer} and is * associated with a {@link #setLayerPaint(android.graphics.Paint) layer paint}, setting an * alpha value less than 1.0 will supersede the alpha of the layer paint.</p> * * <p>Starting with {@link android.os.Build.VERSION_CODES#M}, setting a translucent alpha * value will clip a View to its bounds, unless the View returns <code>false</code> from * {@link #hasOverlappingRendering}.</p> * * @param alpha The opacity of the view. * * @see #hasOverlappingRendering() * @see #setLayerType(int, android.graphics.Paint) * * @attr ref android.R.styleable#View_alpha */public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) { ensureTransformationInfo(); if (mTransformationInfo.mAlpha != alpha) { setAlphaInternal(alpha); if (onSetAlpha((int) (alpha * 255))) { mPrivateFlags |= PFLAG_ALPHA_SET; // subclass is handling alpha - don't optimize rendering cache invalidation invalidateParentCaches(); invalidate(true); } else { mPrivateFlags &= ~PFLAG_ALPHA_SET; invalidateViewProperty(true, false); mRenderNode.setAlpha(getFinalAlpha()); } }} setImageAlpha(int)看源码可知setImageAlpha(int)实际内部调用的是setAlpha(int),所以取值返回也是0-255,表示从透明到不透明 123456789101112/** * Sets the alpha value that should be applied to the image. * * @param alpha the alpha value that should be applied to the image (between * 0 and 255 inclusive, with 0 being transparent and 255 being opaque) * * @see #getImageAlpha() */@RemotableViewMethodpublic void setImageAlpha(int alpha) { setAlpha(alpha);} 总结 setAlpha(float)推荐使用,取值范围为0.0f-1.0f,表示透明到不透明 setAlpha(int),已废弃,不推荐使用,取值范围为1-255,表示透明到不透明 setImageAlpha(int),内部实际调用setAlpha(int),取值范围也是1-255,表示透明到不透明]]></content>
</entry>
<entry>
<title><![CDATA[Android LayoutParams改变View的宽高]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-LayoutParams%E6%94%B9%E5%8F%98View%E7%9A%84%E5%AE%BD%E9%AB%98%2F</url>
<content type="text"><![CDATA[12345<Button android:id="@+id/button" android:layout_width="80dp" android:layout_height="40dp" android:text="变大"/> 取出原来的LayoutParams,修改宽高值后再重新设置回去 123456789findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ViewGroup.LayoutParams params = v.getLayoutParams(); params.width = params.width * 2; params.height = params.height* 2; v.setLayoutParams(params); }}); 或者取出原来的LayoutParams,修改宽高值后再重新设置回去 123456789findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ViewGroup.LayoutParams params = v.getLayoutParams(); params.width = params.width * 2; params.height = params.height* 2; v.requestLayout(); }}); 需注意原来的宽高需设置明确的值以上方法才可用,若设置wrap_content或match_parent则不可用 我们还可以直接new一个LayoutParams,然后设置给控件,这种方法不管原来控件宽高是确定的值还是wrap_content或match_parent,都可以成功修改宽高 1234567findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(800, 400); v.setLayoutParams(params); }});]]></content>
</entry>
<entry>
<title><![CDATA[Android Studio小技巧:region代码折叠]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Studio%E5%B0%8F%E6%8A%80%E5%B7%A7%EF%BC%9Aregion%E4%BB%A3%E7%A0%81%E6%8A%98%E5%8F%A0%2F</url>
<content type="text"><![CDATA[我们可以把相关的代码放在一组region-endregion中,这样这组代码就可以收起和展开 示例代码1234567891011121314151617181920212223242526272829303132333435public class Student { private String name; private int age; private String gender; //region name public String getName() { return name; } public void setName(String name) { this.name = name; } //endregion //region age public int getAge() { return age; } public void setAge(int age) { this.age = age; } //endregion //region gender public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } //endregion} 折叠后的效果如下图所示 快捷键 先选择要折叠的一组代码 按下快捷键Ctrl+Alt+T 在弹出的菜单中选择region…endregion Comments 这样就会自动在选中的代码最开始会最后生成region和endregion注释,自己再添加下分组的注释说明就ok了。]]></content>
</entry>
<entry>
<title><![CDATA[Android 使用Stetho在Chrome浏览器查看SQLite数据库]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-%E4%BD%BF%E7%94%A8Stetho%E5%9C%A8Chrome%E6%B5%8F%E8%A7%88%E5%99%A8%E6%9F%A5%E7%9C%8BSQLite%E6%95%B0%E6%8D%AE%E5%BA%93%2F</url>
<content type="text"><![CDATA[前情提要做Android经常要查看本地SQLite数据库的数据,可以直接用RootExplorer查看,或者在Android Studio中导出数据库文件,然后用第三方SQLite可视化工具查看,比如SQLiteStudio,但是这些方法的前提是设备要Root,否则还是没有权限查看数据库,今天要介绍一款Facebook出品的工具,Stetho,设备可以不用Root,直接在Chrome浏览器中查看数据库。 集成Stetho添加依赖 123dependencies { implementation 'com.facebook.stetho:stetho:1.5.1'} 在Application中初始化,OK了,就这一行代码 1234567public class App extends Application { @Override public void onCreate() { super.onCreate(); Stetho.initializeWithDefaults(this); }} 1234567<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.yeyuanxinyi.androidtest"> <application android:name=".App"> </application></manifest> 在Chrome中查看首先设备用USB线连接电脑,然后打开手机应用 然后在谷歌浏览器中输入chrome://inspect并回车,出现如下页面,可以看到我的手机MX5和我的应用“野猿新一” 点击inspect会弹出如下页面,切换到Resource页签就可以查看数据库了,在左侧Web SQL下可以看我的应用中创建的数据库school及下面的表student 点击数据库名,可以进入SQL编辑页面,在这里可以直接执行增删改查等操作 缺点 相比一些第三方的可视化SQLite工具,Chrome的DevTools查看数据库功能还是比较少的 想导出数据库文件却未发现导出功能 第一次使用点击inspect无法进入,出现“http/1.1 404 not found”错误,需要用SVN翻墙使用,第一次成功后后面就可以直接进入了。]]></content>
</entry>
<entry>
<title><![CDATA[Sqlite可视化工具-SQLiteStudio使用介绍]]></title>
<url>%2F2019%2F07%2F06%2FSqlite%E5%8F%AF%E8%A7%86%E5%8C%96%E5%B7%A5%E5%85%B7-SQLiteStudio%E4%BD%BF%E7%94%A8%E4%BB%8B%E7%BB%8D%2F</url>
<content type="text"><![CDATA[下载地址https://sqlitestudio.pl/index.rvt?act=download 我下载的是Windows(portable) 绿色版,解压直接运行,很方便,推荐使用 主界面直接运行SQLiteStudio.exe打开SQLiteStudio,界面如下 导入数据库文件直接将数据库文件拖入SQLiteStudio即可 数据库结构导入的数据库展开后的结构如下所示 查看表结构双击数据库表,切换到Structure标签即可查看表的结构 查看表数据双击数据库表,切换到Data标签即可查看表数据 SQL语句编辑及运行]]></content>
</entry>
<entry>
<title><![CDATA[java SimpleDateFormat设置时区格式化时区]]></title>
<url>%2F2019%2F07%2F06%2Fjava-SimpleDateFormat%E8%AE%BE%E7%BD%AE%E6%97%B6%E5%8C%BA%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%97%B6%E5%8C%BA%2F</url>
<content type="text"><![CDATA[前情提要在Android开发中经常遇到有的接口需要上传当前时间,如果后台要求直接传一个long类型的时间戳还好,因为这个时间戳是跟时区无关的,如果后台接口要求传的是格式化的时间,若本地设备设置的时区与后台要就的时区不一致,就会导致上传的时间不准确。 有问题的写法这种写法SimpleDateFormat默认用的是本地设备设置的时区,若本地时区与后代约定的不一致就会导致问题 12SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String timestamp = dateFormat.format(new Date()); 设置时区以格式化东八区时间为例,设置时区有以下三种写法,格式化后的时间是一样的 1234567891011public class Test5 { public static void main(String[] args) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); dateFormat.setTimeZone(TimeZone.getTimeZone("Etc/GMT-8")); System.out.println("方法一:" + dateFormat.format(new Date())); dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8:00")); System.out.println("方法二:" + dateFormat.format(new Date())); dateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); System.out.println("方法三:" + dateFormat.format(new Date())); }} 输出结果 123方法一:2019-05-18 17:02:17方法二:2019-05-18 17:02:17方法三:2019-05-18 17:02:17]]></content>
</entry>
<entry>
<title><![CDATA[推荐一款Android Gif动图播放框架android-gif-drawabl]]></title>
<url>%2F2019%2F07%2F06%2F%E6%8E%A8%E8%8D%90%E4%B8%80%E6%AC%BEAndroid-Gif%E5%8A%A8%E5%9B%BE%E6%92%AD%E6%94%BE%E6%A1%86%E6%9E%B6android-gif-drawabl%2F</url>
<content type="text"><![CDATA[引用包123dependencies { implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.16'} 使用GifImageView最简单的使用方法就是直接在src属性设置gif资源,然后就可以直接播放啦 12345<pl.droidsonroids.gif.GifImageView android:id="@+id/gif_iamge_view" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@mipmap/gif"/> 循环播放GifImageView默认播放一次就停止了,我们可以通过GifImageView获取GifDrawable,然后再通过GifDrawable设置循环播放的次数,或者设置无限循环播放 1234GifImageView gifImageView = findViewById(R.id.gif_iamge_view);GifDrawable gifDrawable = (GifDrawable) gifImageView.getDrawable();gifDrawable.setLoopCount(5); // 设置具体播放次数gifDrawable.setLoopCount(0); // 设置无限循环播放]]></content>
</entry>
<entry>
<title><![CDATA[Android xUtils3 update更新数据库操作]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-xUtils3-update%E6%9B%B4%E6%96%B0%E6%95%B0%E6%8D%AE%E5%BA%93%E6%93%8D%E4%BD%9C%2F</url>
<content type="text"><![CDATA[update方法定义第三个参数可以传入多个KeyValue修改多个字段值 1int update(Class<?> var1, WhereBuilder var2, KeyValue... var3) throws DbException; 使用方法1234567try { KeyValue keyValue1 = new KeyValue("name", "野猿新二"); KeyValue keyValue2 = new KeyValue("age", "28"); mDbManager.update(Student.class, WhereBuilder.b("name", "=", "野猿新一").and("age", "=", 18), keyValue1, keyValue2);} catch (DbException e) { e.printStackTrace();}]]></content>
</entry>
<entry>
<title><![CDATA[java 单键值对类AbstractMap.SimpleEntry使用方法]]></title>
<url>%2F2019%2F07%2F06%2Fjava-%E5%8D%95%E9%94%AE%E5%80%BC%E5%AF%B9%E7%B1%BBAbstractMap-SimpleEntry%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95%2F</url>
<content type="text"><![CDATA[1234567891011public class Test { public static void main(String[] args) { Map.Entry<String,String> entry = new AbstractMap.SimpleEntry<String, String>("name", "野猿新一"); System.out.println("new AbstractMap.SimpleEntry:" + entry); System.out.println("getKey:" + entry.getKey()); System.out.println("getValue:" + entry.getValue()); entry.setValue("野猿新二"); System.out.println("setValue:" + entry); }} 测试结果如下 1234new AbstractMap.SimpleEntry:name=野猿新一getKey:namegetValue:野猿新一setValue:name=野猿新二]]></content>
</entry>
<entry>
<title><![CDATA[java break停止外层循环和continue继续外层循环]]></title>
<url>%2F2019%2F07%2F06%2Fjava-break%E5%81%9C%E6%AD%A2%E5%A4%96%E5%B1%82%E5%BE%AA%E7%8E%AF%E5%92%8Ccontinue%E7%BB%A7%E7%BB%AD%E5%A4%96%E5%B1%82%E5%BE%AA%E7%8E%AF%2F</url>
<content type="text"><![CDATA[停止外层循环12345678910111213public class Test { public static void main(String[] args) { out:for (int i = 0; i < 3; i++) { System.out.println("out:" + i); for (int j = 0; j < 3; j++) { if(i == 1 && j == 1) { break out; } System.out.println(" in:" + j); } } }} 输出如下,可以看到(i == 1 && j == 1)条件满足后,内外层循环都停止了 123456out:0 in:0 in:1 in:2out:1 in:0 继续外层循环12345678910111213public class Test { public static void main(String[] args) { out:for (int i = 0; i < 3; i++) { System.out.println("out:" + i); for (int j = 0; j < 3; j++) { if(i == 1 && j == 1) { continue out; } System.out.println(" in:" + j); } } }} 结果如下,可以看到(i == 1 && j == 1)条件满足后,该次的内层循环停止了,回到外层循环继续 12345678910out:0 in:0 in:1 in:2out:1 in:0out:2 in:0 in:1 in:2 停止多层循环同理可以跳出或者继续多层的循环,只要在要跳出或继续的循环出设置标识即可 12345678910111213141516public class Test { public static void main(String[] args) { out:for (int i = 0; i < 3; i++) { System.out.println("out:" + i); for (int j = 0; j < 3; j++) { System.out.println(" in:" + j); for (int k = 0; k < 3; k++) { System.out.println(" inner:" + k); if(i == 1 && j == 1 && k== 1) { break out; } } } } }} 输出结果如下 123456789101112131415161718192021out:0 in:0 inner:0 inner:1 inner:2 in:1 inner:0 inner:1 inner:2 in:2 inner:0 inner:1 inner:2out:1 in:0 inner:0 inner:1 inner:2 in:1 inner:0 inner:1 继续多层循环类似,就不贴代码了]]></content>
</entry>
<entry>
<title><![CDATA[介绍几个在线json转java实体类的POJO网站]]></title>
<url>%2F2019%2F07%2F06%2F%E4%BB%8B%E7%BB%8D%E5%87%A0%E4%B8%AA%E5%9C%A8%E7%BA%BFjson%E8%BD%ACjava%E5%AE%9E%E4%BD%93%E7%B1%BB%E7%9A%84POJO%E7%BD%91%E7%AB%99%2F</url>
<content type="text"><![CDATA[https://www.bejson.com/json2javapojo/new/ http://www.jsons.cn/json2java/ http://tool.chinaz.com/Tools/json2entity.aspx]]></content>
</entry>
<entry>
<title><![CDATA[gson @SerializedName 用法]]></title>
<url>%2F2019%2F07%2F06%2Fgson-SerializedName-%E7%94%A8%E6%B3%95%2F</url>
<content type="text"><![CDATA[假设我们想用gson把如下的json字符串转成一个Student对象,正常情况下是无法成功解析的,因为json的键名和Java类的变量名不一致,这种情况在开发中很常见,后台服务器返回的json字段经常不符合java的驼峰命名规范,有的带下划线,有的首字母没有小写,如果我们不想改变原有的Java类变量名又想成功解析,可以借助@SerializedName 同理,在Java对象转json字符串时如果像转成和变量名不同的json key,也可以用@SerializedName 1{"student_age":28,"student_name":"野猿新一"} 123456789101112131415161718public class Student { @SerializedName("student_name") public String name; @SerializedName("student_age") public int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; }} json转Java对象123456public void toObject() { // {"student_age":28,"student_name":"野猿新一"} String json = "{\"student_age\":28,\"student_name\":\"野猿新一\"}"; Student student = new Gson().fromJson(json, Student.class); Log.d("gson", student.toString()); // 输出结果为 Student{name='野猿新一', age=28}} java对象转json12345public void printJson2() { Student student = new Student("野猿新一", 28); String json = new Gson().toJson(student); Log.d("json", json); // 输出结果为{"student_age":28,"student_name":"野猿新一"}}]]></content>
</entry>
<entry>
<title><![CDATA[gson解析json串]]></title>
<url>%2F2019%2F07%2F06%2Fgson%E8%A7%A3%E6%9E%90json%E4%B8%B2%2F</url>
<content type="text"><![CDATA[先定义一个Java对象12345678910111213141516public class Student { public String name; public int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; }} json转Java对象123456public void toObject() { // {"age":28,"name":"野猿新一"} String json = "{\"age\":28,\"name\":\"野猿新一\"}"; Student student = new Gson().fromJson(json, Student.class); Log.d("gson", student.toString());} json转List12345678public void toList() { // [{"age":21,"name":"野猿新一"},{"age":22,"name":"野猿新二"},{"age":23,"name":"野猿新三"}] String json = "[{\"age\":21,\"name\":\"野猿新一\"},{\"age\":22,\"name\":\"野猿新二\"},{\"age\":23,\"name\":\"野猿新三\"}]"; List<Student> students = new Gson().fromJson(json, new TypeToken<List<Student>>(){}.getType()); for (Student student:students) { Log.d("gson", student.toString()); }}]]></content>
</entry>
<entry>
<title><![CDATA[Gson生成json的几种方法]]></title>
<url>%2F2019%2F07%2F06%2FGson%E7%94%9F%E6%88%90json%E7%9A%84%E5%87%A0%E7%A7%8D%E6%96%B9%E6%B3%95%2F</url>
<content type="text"><![CDATA[引用gson123dependencies { implementation 'com.google.code.gson:gson:2.8.5'} 方法一:Map对象转json1234567891011121314public void printJson1() { Map<String, Object> student = new HashMap<>(); student.put("name", "野猿新一"); student.put("age", 28); student.put("emails", new String[]{"yeyuanxinyi@sina.com", "yeyuanxinyi@sohu.com", "yeyuanxinyi@163.com"}); Map<String, Object> girlfriend = new HashMap<>(); girlfriend.put("name", "野援新二"); girlfriend.put("age", 18); student.put("girlfriend", girlfriend); String json = new Gson().toJson(student); Log.d("json", json);} 方法二:Java对象转json123456789101112131415161718192021// 首先定义一个Java类Studentpublic class Student { public String name; public int age; public String[] emails; public Student girlfriend; public Student(String name, int age) { this.name = name; this.age = age; }}public void printJson2() { Student student = new Student("野猿新一", 28); student.emails = new String[]{"yeyuanxinyi@sina.com", "yeyuanxinyi@sohu.com", "yeyuanxinyi@163.com"}; student.girlfriend = new Student("野援新二", 18); String json = new Gson().toJson(student); Log.d("json", json);} 生成的json结果以上代码生成的json结果是一样的,如下 12345678910111213{ "age": 28, "emails": [ "yeyuanxinyi@sina.com", "yeyuanxinyi@sohu.com", "yeyuanxinyi@163.com" ], "girlfriend": { "age": 18, "name": "野援新二" }, "name": "野猿新一"} 总结 json内容少建议直接用Map生成,省去还要创建一个Java类的步骤。 json内容多,层级复杂,建议用Java对象生成,代码结构比较清晰。]]></content>
</entry>
<entry>
<title><![CDATA[介绍几个json在线格式化网站]]></title>
<url>%2F2019%2F07%2F06%2F%E4%BB%8B%E7%BB%8D%E5%87%A0%E4%B8%AAjson%E5%9C%A8%E7%BA%BF%E6%A0%BC%E5%BC%8F%E5%8C%96%E7%BD%91%E7%AB%99%2F</url>
<content type="text"><![CDATA[http://www.bejson.com/jsonviewernew/ https://www.json.cn/ http://tool.chinaz.com/tools/jsonformat.aspx]]></content>
</entry>
<entry>
<title><![CDATA[java 二进制常量、八进制常量、十进制常量、十六进制常量表示方法]]></title>
<url>%2F2019%2F07%2F06%2Fjava-%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%B8%B8%E9%87%8F%E3%80%81%E5%85%AB%E8%BF%9B%E5%88%B6%E5%B8%B8%E9%87%8F%E3%80%81%E5%8D%81%E8%BF%9B%E5%88%B6%E5%B8%B8%E9%87%8F%E3%80%81%E5%8D%81%E5%85%AD%E8%BF%9B%E5%88%B6%E5%B8%B8%E9%87%8F%E8%A1%A8%E7%A4%BA%E6%96%B9%E6%B3%95%2F</url>
<content type="text"><![CDATA[二进制 以0b或者0B开头 可用符号0、1 负数在前面加符号- 正确的值:0x10、0X11、-0x11 错误的值:0x12 需要注意的是这里0b或者0B开头表示的只是进制,而不是说该常量是byte类型,千万不要搞混了 八进制 以数字0开头 可用符号0、1、2、3、4、5、6、7 负数在前面加符号- 正确的值:01234567、0123、0777、-555 错误的值:088 十进制 无需开头的前缀 可用符号0、1、2、3、4、5、6、7、8、9 负数在前面加符号- 正确的值:1234567890、123、0777、-10 错误的值:088 十六进制 以0x或0X开头 可用符号0、1、2、3、4、5、6、7、8、9、a、b、c、d、e、f(或A、B、C、D、E、F) 负数在前面加符号- 正确的值:0x1234567890abcdefL、-0X1234567890ABCDEFL、0x123 错误的值:0x123ghi 示例123456789101112public class Test5 { public static void main(String[] args) { System.out.println("二进制常量10:" + 0b1010); System.out.println("二进制常量-10:" + -0b1010); System.out.println("八进制常量10:" + 012); System.out.println("八进制常量-10:" + -012); System.out.println("十进制常量10:" + 10); System.out.println("十进制常量-10:" + -10); System.out.println("十六进制常量10:" + 0xa); System.out.println("十六进制常量-10:" + -0xa); }} 输出结果为 12345678二进制常量10:10二进制常量-10:-10八进制常量10:10八进制常量-10:-10十进制常量10:10十进制常量-10:-10十六进制常量10:10十六进制常量-10:-10]]></content>
</entry>
<entry>
<title><![CDATA[java整型的原码反码与补码表示]]></title>
<url>%2F2019%2F07%2F06%2Fjava%E6%95%B4%E5%9E%8B%E7%9A%84%E5%8E%9F%E7%A0%81%E5%8F%8D%E7%A0%81%E4%B8%8E%E8%A1%A5%E7%A0%81%E8%A1%A8%E7%A4%BA%2F</url>
<content type="text"><![CDATA[java中byte、short、int、long都是以二进制补码表示的整数 原码最高位为符号位,0表示正数,1表示负数,其余位为正常的二进制表示 5的原码:00000000 00000000 00000000 00000101 -5的原码:10000000 00000000 00000000 00000101 反码正数:与原码一样 负数:符号位不变还是为1,其余位取反 5的反码:00000000 00000000 00000000 00000101 -5的反码:11111111 11111111 11111111 11111010 补码正数:与原码一样 负数:补码加1 5的补码:00000000 00000000 00000000 00000101 -5的补码:11111111 11111111 11111111 11111011 验证验证整数是以二进制补码的表示的 123456public class Test5 { public static void main(String[] args) { System.out.println("5的补码:" + Integer.toBinaryString(5)); System.out.println("-5的补码:" + Integer.toBinaryString(-5)); }} 结果为如下所示 125的补码:101 // 高位29个零省略-5的补码:11111111111111111111111111111011 总结 java中整数都是以二进制补码表示 最高位符号位,0表整数,1表负数 正数:原码、反码、补码都一样 负数:反码为原码的符号位不变,其余位取反;补码为反码加1 负数:可以反推出反码为补码减1,原码为反码符号位不变,其余位取反]]></content>
</entry>
<entry>
<title><![CDATA[MySQL设置成所有ip可以访问解决Host is not allowed to connect to this MySQL server问题]]></title>
<url>%2F2019%2F07%2F06%2FMySQL%E8%AE%BE%E7%BD%AE%E6%88%90%E6%89%80%E6%9C%89ip%E5%8F%AF%E4%BB%A5%E8%AE%BF%E9%97%AE%E8%A7%A3%E5%86%B3Host-is-not-allowed-to-connect-to-this-MySQL-server%E9%97%AE%E9%A2%98%2F</url>
<content type="text"><![CDATA[连接数据库的时候报如下错误,原因是本机的ip未被允许访问该数据库服务。我们可以修改为所有ip都可以访问。 1java.sql.SQLException: null, message from server: "Host '192.168.1.103' is not allowed to connect to this MySQL server" 我们先看下用户表 首先切到mysql数据库 12mysql> use mysql;Database changed 然后查看user表下的用户及允许访问的host 和我猜测的一样,目前我的数据库所有账号的host都设置成localhost,只允许本机访问 1234567891011mysql> select user,host from user;+------------------+-----------+| user | host |+------------------+-----------+| Him | localhost || mysql.infoschema | localhost || mysql.session | localhost || mysql.sys | localhost || root | localhost |+------------------+-----------+5 rows in set (0.00 sec) 我们把Him这个账号的host改成%,表示允许所有ip访问 123mysql> update user set host='%' where user='Him';Query OK, 1 row affected (0.01 sec)Rows matched: 1 Changed: 1 Warnings: 0 我们再查询一次,这时候host已经改成%了 1234567891011mysql> select user,host from user;+------------------+-----------+| user | host |+------------------+-----------+| Him | % || mysql.infoschema | localhost || mysql.session | localhost || mysql.sys | localhost || root | localhost |+------------------+-----------+5 rows in set (0.00 sec) 我们再访问一次,可是却还是连接不了,还是报java.sql.SQLException: null, message from server: “Host ‘192.168.1.103’ is not allowed to connect to this MySQL server”错误 查资料发现是未刷新,执行以下命令即可 12mysql> flush privileges;Query OK, 0 rows affected (0.01 sec) 大功告成,再次连接提示成功。]]></content>
</entry>
<entry>
<title><![CDATA[Android 直连MySQL数据库]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-%E7%9B%B4%E8%BF%9EMySQL%E6%95%B0%E6%8D%AE%E5%BA%93%2F</url>
<content type="text"><![CDATA[1.准备数据库驱动包准备mysql的数据库驱动,我在mysql官网下载的最新版的驱动在Android上运行会报错,找不到某些类,无奈只能一些旧版本的驱动包,可以正常编译。 可以到这里下载 然后把下载的jar包导入到项目中 2.加载数据库驱动1Class.forName("com.mysql.jdbc.Driver"); 3.连接getConnection有三个参数 第一个参数为连接地址,格式为jdbc:mysql://IP地址或域名:端口号/数据库名,端口号默认都是3306 第二个参数是数据库用户名 第三个参数是数据库密码 1DriverManager.getConnection("jdbc:mysql://192.168.1.101:3306/hellospring", "Him", "123456"); 4.Android直连MySQL数据库示例代码12345678910111213141516171819202122232425262728293031public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.connect).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { connect(); } }).start(); } }); } private void connect() { try { Class.forName("com.mysql.jdbc.Driver"); Connection con = DriverManager.getConnection("jdbc:mysql://192.168.1.101:3306/mydatabase", "user", "password"); Log.d("TAG", "连接成功"); // 连接成功后通过Connection执行数据库操作 } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); Log.d("TAG", "连接失败:" + e.getLocalizedMessage()); } }} 5.注意事项 数据库连接也属于网络连接,所以记得添加权限 数据库连接也属于网络连接,比较耗时,所以需放在子线程中操作,否则会连接失败,报com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.异常]]></content>
</entry>
<entry>
<title><![CDATA[MySQL java 驱动包,亲测可用,Android平台也可用于直连数据库]]></title>
<url>%2F2019%2F07%2F06%2FMySQL-java-%E9%A9%B1%E5%8A%A8%E5%8C%85%EF%BC%8C%E4%BA%B2%E6%B5%8B%E5%8F%AF%E7%94%A8%EF%BC%8CAndroid%E5%B9%B3%E5%8F%B0%E4%B9%9F%E5%8F%AF%E7%94%A8%E4%BA%8E%E7%9B%B4%E8%BF%9E%E6%95%B0%E6%8D%AE%E5%BA%93%2F</url>
<content type="text"><![CDATA[MySQL java 驱动包,亲测可用,Android平台也可用于直连数据库,分享给大家。 链接:https://pan.baidu.com/s/19_pwdJMxI90_gLgX1n629A提取码:jfd4]]></content>
</entry>
<entry>
<title><![CDATA[Android build.gradle配置buildConfigField字符串及注意事项]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-build-gradle%E9%85%8D%E7%BD%AEbuildConfigField%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8F%8A%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9%2F</url>
<content type="text"><![CDATA[在开发中经常分测试环境和正式环境,每次发布版本总是要修改服务器地址或者端口号。 其实可以在build.gradle中为测试环境和正式环境配置不同的服务地址,在编译的时候会自动根据当前的buildType选择不同的服务地址。 12345678910android { buildTypes { debug { buildConfigField 'String', 'domain', '"http://192.168.0.1:5555/MyProject/"' } release { buildConfigField 'String', 'domain', '"http://192.168.0.1:8888/MyProject/"' } }} 在自动生成的类BuildConfig中会新增domain字段,且在不同的buildType环境下的值是不一样的,如下: 测试环境的值 1234public final class BuildConfig { public static final boolean DEBUG = Boolean.parseBoolean("true"); public static final String domain = "http://192.168.0.1:5555/MyProject/";} 生产环境的值 1234public final class BuildConfig { public static final boolean DEBUG = Boolean.parseBoolean("true"); public static final String domain = "http://192.168.0.1:8888/MyProject/";} 调用方法直接用BuildConfig调用,例如 1String url = BuildConfig.domain; 注意事项由于我们这里介绍的buildConfigField是String类型,所以要注意以下例子,前三张写法都是可以的,但是后两种是错误的 1234567891011121314buildTypes { debug { // 单引号写法 buildConfigField 'String', 'string1', '\"string1value\"' // 双引号写法 buildConfigField "String", "string2", "\"string2value\"" // 单双引号的写法,最简洁不用转义,推荐该写法 buildConfigField 'String', 'string3', '"string3value"' // 以下两种写法都没转义,会报错 buildConfigField 'String', 'string4', 'string4value' buildConfigField "String", "string5", "string5value" }} 结果如下,可以看到最后两个生成的值没加双引号,不是字符串会报错,在IDE中会标红显示错误。 1234567public final class BuildConfig { public static final String string1 = "string1value"; public static final String string2 = "string2value"; public static final String string3 = "string3value"; public static final String string4 = string4value; public static final String string5 = string5value;}]]></content>
</entry>
<entry>
<title><![CDATA[Android 不依赖于Activity的全局对话框Dialog]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-%E4%B8%8D%E4%BE%9D%E8%B5%96%E4%BA%8EActivity%E7%9A%84%E5%85%A8%E5%B1%80%E5%AF%B9%E8%AF%9D%E6%A1%86Dialog%2F</url>
<content type="text"><![CDATA[正常初始化对话框的写法一般的Dialog对象初始化都需要依赖于Activity,如下 1234new AlertDialog.Builder(activity) .setTitle("野猿新一") .setMessage("我是对话框内容啦") .show(); BroadcastReceiver如果是在Activity中注册的,用onReceive(Context context, Intent intent)方法的context参数来初始化Dialog是可以正常弹出对话框的。因为这里的context其实是一个Activity对象,如下所示: 12345678910111213141516171819public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (context instanceof Activity) { new AlertDialog.Builder(context) .setTitle("野猿新一") .setMessage("我是对话框内容啦") .show(); } } }, new IntentFilter("com.him.action")); }} 会报错的写法虽说AlertDialog.Builder(Context context)的参数是Context,但是如果传入的是非Activity的Context,比如说Application,就会报如下所示的错误 123456789101112131415161718android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application at android.view.ViewRootImpl.setView(ViewRootImpl.java:700) at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:289) at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85) at android.app.Dialog.show(Dialog.java:298) at android.app.AlertDialog.show(AlertDialog.java:1128) at android.app.AlertDialog$Builder.show(AlertDialog.java:1008) at com.him.autosizingtest.MainActivity$1.onClick(MainActivity.java:21) at android.view.View.performClick(View.java:4918) at android.view.View$PerformClick.run(View.java:20399) at android.os.Handler.handleCallback(Handler.java:815) at android.os.Handler.dispatchMessage(Handler.java:104) at android.os.Looper.loop(Looper.java:194) at android.app.ActivityThread.main(ActivityThread.java:5869) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1019) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:814) 所以以下的几种写法都会报错 123456789new AlertDialog.Builder(activity.getApplication()) .setTitle("野猿新一") .setMessage("我是对话框内容啦") .show();new AlertDialog.Builder(activity.getApplicationContext()) .setTitle("野猿新一") .setMessage("我是对话框内容啦") .show(); BroadcastReceiver如果是在Manifest中声明的,用onReceive(Context context, Intent intent)方法的context参数来初始化Dialog,一样还会报Unable to add window – token null is not for an application错误。所以如下写法也不可行 12345678910111213141516public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { new AlertDialog.Builder(context) .setTitle("野猿新一") .setMessage("我是对话框内容啦") .show(); }}// 在manifest中声明<receiver android:name=".MyBroadcastReceiver"> <intent-filter> <action android:name="com.him.action"/> </intent-filter></receiver> 全局Dialog但是如果说有些情况下获取不到Activity对象,但是又想弹出Dialog呢?这时候用全局的Dialog是可以实现的。 只需要设置dialog为WindowManager.LayoutParams.TYPE_SYSTEM_ALERT类型 然后添加android.permission.SYSTEM_ALERT_WINDOW权限 12345678910Dialog dialog = new AlertDialog.Builder(activity.getApplicationContext()) .setTitle("野猿新一") .setMessage("我是对话框内容啦") .create();// 增加这句代码dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);dialog.show();// 然后在manifest中添加权限<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />]]></content>
</entry>
<entry>
<title><![CDATA[批处理脚本实现Android apk安装包拖拽安装]]></title>
<url>%2F2019%2F07%2F06%2F%E6%89%B9%E5%A4%84%E7%90%86%E8%84%9A%E6%9C%AC%E5%AE%9E%E7%8E%B0Android-apk%E5%AE%89%E8%A3%85%E5%8C%85%E6%8B%96%E6%8B%BD%E5%AE%89%E8%A3%85%2F</url>
<content type="text"><![CDATA[每次别人发个apk安装包过来还要先用usb连接手机把apk拷到手机存储,然后还要在手机存储里找到该安装包点击安装。这样实在是太麻烦。其实有个简单快速安装电脑上apk到手机的方法,只需把apk拖拽到bat文件上即可。 首先新建一个txt文件,把如下批处理代码拷贝到该文件中 12345678910@ECHO OFFECHO [安装APK]ECHO -------------------------------ECHO [等待插入手机...]adb wait-for-deviceECHO [安装] %~nx1adb install -r %1ECHO [暂停5秒自动关闭...]ping -n 5 127.0.0.1>nul@ECHO ON 然后保存该文件为.bat后缀名,比如InstallAPK.bat ok,这样就大功告成了,下次再要安装apk,先把手机通过usb连接电脑,然后把apk安装包拖拽到该bat文件就会自动静默安装,都不需要你在手机上点确定安装下一步下一步啥的。 实际运行如下所示]]></content>
</entry>
<entry>
<title><![CDATA[Android adb shell 查询当前设备所有安装应用包名]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-adb-shell-%E6%9F%A5%E8%AF%A2%E5%BD%93%E5%89%8D%E8%AE%BE%E5%A4%87%E6%89%80%E6%9C%89%E5%AE%89%E8%A3%85%E5%BA%94%E7%94%A8%E5%8C%85%E5%90%8D%2F</url>
<content type="text"><![CDATA[很简单,一条命令搞定 首先确保设备连接上电脑 然后在cmd命令行中输入adb shell pm list packages命令并回车 结果如下所示,输出的包名包括系统预装应用和我们自己装的所有应用的包名 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149C:\Users\Him>adb shell pm list packagespackage:com.huawei.internetaudioservicepackage:com.android.defcontainerpackage:com.tencent.mmpackage:com.example.android.notepadpackage:com.sohu.newsclientpackage:com.android.quicksearchboxpackage:com.android.contactspackage:com.huawei.wifihotspotpackage:com.huawei.hwidpackage:com.huawei.mmitest2package:com.android.phonepackage:com.duomi.androidpackage:com.android.calculator2package:com.huawei.phoneservicepackage:com.huawei.android.airsharingcastpackage:com.huawei.android.wifitrafficmonitorpackage:com.android.htmlviewerpackage:com.huawei.powergeniepackage:com.huawei.android.WeatherWallpaperpackage:com.google.android.gsf.loginpackage:com.android.providers.calendarpackage:com.android.bluetoothpackage:com.huawei.bluetoothpackage:com.android.calendarpackage:com.android.browserpackage:com.huawei.magnifierpackage:com.android.huawei.countlapsetimepackage:com.android.onetimeinitializerpackage:com.android.providers.downloads.uipackage:com.huawei.icos.arpackage:com.android.documentsuipackage:com.android.sharedstoragebackuppackage:com.android.vpndialogspackage:com.android.mmspackage:com.android.provisionpackage:com.huawei.gameboxpackage:com.android.huawei.projectmenupackage:com.android.providers.mediapackage:com.huawei.flashlightpackage:com.huawei.android.pushagentpackage:com.huawei.motionservicepackage:com.android.certinstallerpackage:cn.wps.moffice_i18npackage:com.huawei.hwstartupguidepackage:com.sohu.sohuvideopackage:com.google.android.gmspackage:com.infinit.wostore.uipackage:com.android.dreams.phototablepackage:com.android.settingspackage:org.simalliance.openmobileapi.servicepackage:com.sinovatech.unicom.uipackage:com.huawei.dataservicediagnosepackage:com.huawei.systemmanagerpackage:com.android.gallery3dpackage:com.baidu.input_huaweipackage:com.android.musicvispackage:com.android.exchangepackage:com.youdao.dictpackage:com.android.wallpaper.livepickerpackage:com.huawei.android.hwoucpackage:com.android.packageinstallerpackage:com.android.providers.telephonypackage:com.android.providers.agpspackage:com.svox.picopackage:com.huawei.mmifunctionpackage:com.android.noisefieldpackage:com.android.emailpackage:com.android.mediacenterpackage:com.nuance.swype.emuipackage:com.android.wallpapercropperpackage:com.android.location.fusedpackage:com.android.backupconfirmpackage:com.android.magicsmokepackage:com.huawei.android.AutoRegSmspackage:com.android.providers.settingspackage:com.huawei.android.totemweatherpackage:com.huawei.android.karaokeeffectpackage:com.android.providers.downloadspackage:com.huawei.imspackage:com.huawei.android.airsharingpackage:com.android.phasebeampackage:com.tencent.mttpackage:com.android.hwmirrorpackage:com.android.soundrecorderpackage:com.autonavi.minimappackage:com.huawei.android.dspackage:com.neusoft.td.android.wo116114package:com.android.inputmethod.latinpackage:com.android.proxyhandlerpackage:com.huawei.KoBackuppackage:com.android.inputdevicespackage:com.android.wallpaper.holospiralpackage:com.huawei.lcagentpackage:com.mypackage.appnamepackage:com.android.stkpackage:com.huawei.android.airsharingcastclientpackage:com.huawei.omacppackage:com.android.providers.userdictionarypackage:com.chaozh.iReaderFree15package:com.tencent.mobileqqpackage:com.android.pacprocessorpackage:com.baidu.searchbox_huaweipackage:com.huawei.android.nffpackage:androidhwextpackage:com.android.galaxy4package:com.vmall.clientpackage:com.android.printspoolerpackage:androidpackage:com.android.providers.contactspackage:com.asiainfo.androidpackage:com.huawei.android.mewidgetpackage:com.huawei.android.apkbatchinstallpackage:com.android.protipspackage:com.android.externalstoragepackage:com.huawei.android.thememanagerpackage:com.android.providers.applicationspackage:com.mobiletools.systemhelperpackage:com.android.dreams.basicpackage:com.huawei.bdpackage:com.him.stickyheaderpackage:com.huawei.android.hwpaypackage:com.android.apps.tagpackage:com.huawei.android.calendarwidgetpackage:com.huawei.android.FMRadiopackage:com.android.systemuipackage:com.huawei.android.multiscreenpackage:com.sina.weibopackage:com.huawei.wo3gpackage:com.android.keychainpackage:com.huawei.vassistantpackage:com.android.smspushpackage:com.huawei.vdrivepackage:com.android.wallpaperpackage:com.mt.mtxx.mtxxpackage:com.android.providers.streamingpackage:com.android.deskclockpackage:com.meitu.mtxx.huaweipackage:com.huawei.appmarketpackage:com.google.android.gsfpackage:com.huawei.hidiskpackage:com.android.keyguardpackage:com.android.shellpackage:com.huawei.audioalgoservicepackage:com.tiantian.android.player.videopackage:com.huawei.android.wfdftpackage:com.huawei.android.launcherC:\Users\Him>]]></content>
</entry>
<entry>
<title><![CDATA[Android adb install 命令安装apk]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-adb-install-%E5%91%BD%E4%BB%A4%E5%AE%89%E8%A3%85apk%2F</url>
<content type="text"><![CDATA[如果有人发送一个apk安装包给你安装,普通人都是先把apk文件拷到手机存储内,然后在手机存储找到该安装包,点击安装。 但是这样太麻烦了,而且对我们程序猿来说不够逼格,其实我们可以通过adb命令,一行搞定 adb install apk安装包路径 123456C:\Users\Him>adb install d:/app-debug.apkd:/app-debug.apk: 1 file pushed. 4.5 MB/s (1716428 bytes in 0.365s) pkg: /data/local/tmp/app-debug.apkSuccessC:\Users\Him>]]></content>
</entry>
<entry>
<title><![CDATA[Android adb uninstall 通过包名删除卸载某个App应用]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-adb-uninstall-%E9%80%9A%E8%BF%87%E5%8C%85%E5%90%8D%E5%88%A0%E9%99%A4%E5%8D%B8%E8%BD%BD%E6%9F%90%E4%B8%AAApp%E5%BA%94%E7%94%A8%2F</url>
<content type="text"><![CDATA[直接在cmd命令行中输入adb uninstall 包名,就可以删除相应的应用了 实际运行如下所示 1234C:\Users\Him>adb uninstall com.mypackage.appnameSuccessC:\Users\Him>]]></content>
</entry>
<entry>
<title><![CDATA[Android adb 查看当前连接设备]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-adb-%E6%9F%A5%E7%9C%8B%E5%BD%93%E5%89%8D%E8%BF%9E%E6%8E%A5%E8%AE%BE%E5%A4%87%2F</url>
<content type="text"><![CDATA[在Android Studio中我们可以在底部LogCat中很直观地查看到当前连接的设备 还有一种方法是直接在cmd命令行中输入adb devices命令查看 如下所示,我电脑目前连接了两台设备,一台我的手机真机,一台是模拟器 1234567C:\Users\Him>adb devicesList of devices attachedBY2QLS149K055335 deviceemulator-5554 deviceC:\Users\Him>]]></content>
</entry>
<entry>
<title><![CDATA[Android adb 切换到无线调试和USB调试]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-adb-%E5%88%87%E6%8D%A2%E5%88%B0%E6%97%A0%E7%BA%BF%E8%B0%83%E8%AF%95%E5%92%8CUSB%E8%B0%83%E8%AF%95%2F</url>
<content type="text"><![CDATA[Android开发真机调试的时候总是要连着一条USB线很不方便,特别是如果还要拿着真机四处走动测试还要看log或或者异常信息的时候更是不方便,这种清空我们可以切换到无效调试的模式,只要保证电脑和真机在同一个局域网内,走到哪都可以测试。 切换无线调试 .设备通过USB线连接电脑。 在cmd命令行中输入adb tcpip 5555 拔掉数据线 在cmd命令行中输入adb connect 192.168.1.5(设备在局域网中的ip) 注意以上步骤3不一定需要,有的设备需拔掉数据线才能够继续输入下一条语句。 切换USB调试如果不想用无线调试了,也可以切换回USB调试 直接在命令行中输入adb usb就可以切回usb调试模式了。 注意切换的时候不要插着数据线,否则会报error: more than one device/emulator错误,因为此时无线模式开着同时又连接usb,adb会以为有两台设备,不知要为那一台执行命令,所以需拔掉usb线。 实际运行12345678910111213Microsoft Windows [Version 10.0.17134.648](c) 2018 Microsoft Corporation. All rights reserved.C:\Users\Him>adb tcpip 5555restarting in TCP mode port: 5555C:\Users\Him>adb connect 192.168.1.5connected to 192.168.1.5:5555C:\Users\Him>adb usbrestarting in USB modeC:\Users\Him>]]></content>
</entry>
<entry>
<title><![CDATA[Android gradle 修改apk输出路径]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-gradle-%E4%BF%AE%E6%94%B9apk%E8%BE%93%E5%87%BA%E8%B7%AF%E5%BE%84%2F</url>
<content type="text"><![CDATA[Android Studio中编译打包生成的Apk默认是输出到如下图片所示的路径中,但是我们可以修改输出的路径到我们想保存的地方 修改输出路径 1234567android { applicationVariants.all { variant -> variant.outputs.all { variant.getPackageApplication().outputDirectory = new File("D:/MyAppName/apk") } }}]]></content>
</entry>
<entry>
<title><![CDATA[Android gradle配置productFlavors多渠道打包,一次性编译不同应用商店的分发包渠道包]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-gradle%E9%85%8D%E7%BD%AEproductFlavors%E5%A4%9A%E6%B8%A0%E9%81%93%E6%89%93%E5%8C%85%EF%BC%8C%E4%B8%80%E6%AC%A1%E6%80%A7%E7%BC%96%E8%AF%91%E4%B8%8D%E5%90%8C%E5%BA%94%E7%94%A8%E5%95%86%E5%BA%97%E7%9A%84%E5%88%86%E5%8F%91%E5%8C%85%E6%B8%A0%E9%81%93%E5%8C%85%2F</url>
<content type="text"><![CDATA[应用编译打包的时候经常会为不同的应用市场打不同的包,为不同的包做不同的配置。 比如集成友盟统计,会对不同的渠道包配置一个不同的渠道号用于各渠道的统计。 如下所示 12345678910111213141516171819202122232425262728293031323334android { compileSdkVersion 28 defaultConfig { applicationId "com.him.autosizingtest" minSdkVersion 15 targetSdkVersion 28 versionCode 1 versionName "1.0" } signingConfigs { release { keyAlias 'myalias' keyPassword 'password' storeFile file('D:/mykeystore.jks') storePassword 'password' } } buildTypes { release { signingConfig signingConfigs.release } } productFlavors { wandoujia { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"] } yingyongbao { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "yingyongbao"] } xiaomi { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"] } }} 直接双击如下图中的assembleRelease,就可以一口气编译所有渠道的release包 编译完成后,各渠道包在如下路径 如果说你配置的友盟渠道号和flavor名称一样,还有一种更简洁的写法,如下所示 12345678910111213141516171819202122232425262728293031android { compileSdkVersion 28 defaultConfig { applicationId "com.him.autosizingtest" minSdkVersion 15 targetSdkVersion 28 versionCode 1 versionName "1.0" } signingConfigs { release { keyAlias 'myalias' keyPassword 'password' storeFile file('D:/mykeystore.jks') storePassword 'password' } } buildTypes { release { signingConfig signingConfigs.release } } productFlavors { wandoujia {} yingyongbao {} xiaomi {} } productFlavors.all { flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] }}]]></content>
</entry>
<entry>
<title><![CDATA[Android gradle 重命名编译生成的apk安装包名]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-gradle-%E9%87%8D%E5%91%BD%E5%90%8D%E7%BC%96%E8%AF%91%E7%94%9F%E6%88%90%E7%9A%84apk%E5%AE%89%E8%A3%85%E5%8C%85%E5%90%8D%2F</url>
<content type="text"><![CDATA[Android Studio中编译生成的安装包文件名默认如下所示 ,格式为module-buildtype.apk 但是为了区分不同项目,很多情况下编译完都要重新命名,有点麻烦 其实我们可以直接在gradle中配置生成的文件名,而且可以区分不同的buildtype 首先在app module的build.gradle文件中增加如下配置 编译后的文件名为MyAppName_debug_1.0.apk和MyAppName_release_1.0.apk 1234567891011android { defaultConfig { versionCode 1 versionName "1.0" } applicationVariants.all { variant -> variant.outputs.all { outputFileName = "MyAppName_${variant.name}_${variant.versionName}.apk" } }}]]></content>
</entry>
<entry>
<title><![CDATA[Android Studio编译生成debug和Release安装包apk]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Studio%E7%BC%96%E8%AF%91%E7%94%9F%E6%88%90debug%E5%92%8CRelease%E5%AE%89%E8%A3%85%E5%8C%85apk%2F</url>
<content type="text"><![CDATA[在Android Studio中编译项目有以下两种方法 方法一如下图所示,双击Gradle中的assembleDebug或者assembleRelease就可以开始编译 当然,也可以直接双击assemble一次性编译debug和release版本 方法二如下如所示,在Terminal命令行中输入gradlew assembleRelease或者gradlew assembleDebug,然后回车开始编译。 生成的安装包路径编译成功后可以在如下路径找到编译后的apk安装包]]></content>
</entry>
<entry>
<title><![CDATA[Android gradle signingConfig配置编译签名的keystore]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-gradle-signingConfig%E9%85%8D%E7%BD%AE%E7%BC%96%E8%AF%91%E7%AD%BE%E5%90%8D%E7%9A%84keystore%2F</url>
<content type="text"><![CDATA[配置很简单,如下所示,可以分别为release,debug等不同的buildType设置不同的keystore,及配置相应的alias、密码。 123456789101112131415161718192021222324android { signingConfigs { release { keyAlias 'myalias' keyPassword 'password' storeFile file('D:/mykeystore_release.jks') storePassword 'password' } debug { keyAlias 'myalias' keyPassword 'password' storeFile file('D:/mykeystore_debug.jks') storePassword 'password' } } buildTypes { release { signingConfig signingConfigs.release } debug { signingConfig signingConfigs.debug } }} 配置完就可以直接编译了,有以下两种编译方法 方法一 如下图所示,双击Gradle中的assembleDebug或者assembleRelease就可以开始编译 方法二 如下如所示,在Terminal命令行中输入gradlew assembleRelease或者gradlew assembleDebug,然后回车开始编译。 编译成功后可以在如下路径找到编译后的apk安装包]]></content>
</entry>
<entry>
<title><![CDATA[Android Activity显示成对话框Dialog]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Activity%E6%98%BE%E7%A4%BA%E6%88%90%E5%AF%B9%E8%AF%9D%E6%A1%86Dialog%2F</url>
<content type="text"><![CDATA[方法一直接设置已有的Dialog样式的主题,比如 123<activity android:name=".DialogActivity" android:theme="@style/Theme.AppCompat.Dialog"/> 方法二自定义主题theme,让后设置Activity的主题为该主题 先在styles.xml文件中自定义对话框样式的主题 1234567891011121314<resources> <style name="ActivityDialog"> <!--这是重点,把Activity显示成Dialog--> <item name="android:windowIsFloating">true</item> <!--这是第二个重点,对话框外背景半透,才有对话的效果--> <item name="android:backgroundDimEnabled">true</item> <!--是否显示标题--> <item name="android:windowNoTitle">true</item> <!--设置对话框的背景--> <item name="android:windowBackground">@android:color/transparent</item> <!--点击对话框外是否关闭窗口--> <item name="android:windowCloseOnTouchOutside">true</item> </style></resources> 然后把Activity的theme设置成自定义的主题 123<activity android:name=".DialogActivity" android:theme="@style/ActivityDialog"/> 方法三还有一种方法是继承已有的Dialog主题,这样比较灵活,可以继承父主题已定义的属性,又可以自定义一些属性 比如继承已有的”Theme.AppCompat.Dialog”主题,然后不显示标题,自定义背景色,点击对话框外部关闭对话框。 12345<style name="MyDialog" parent="Theme.AppCompat.Dialog"> <item name="android:windowNoTitle">true</item> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowCloseOnTouchOutside">true</item></style> 然后设置该主题 123<activity android:name=".DialogActivity" android:theme="@style/MyDialog"/>]]></content>
</entry>
<entry>
<title><![CDATA[java生成UUID]]></title>
<url>%2F2019%2F07%2F06%2Fjava%E7%94%9F%E6%88%90UUID%2F</url>
<content type="text"><![CDATA[简介UUID是通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部分。其目的,是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建不与其它人冲突的UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问题。目前最广泛应用的UUID,是微软公司的全局唯一标识符(GUID),而其他重要的应用,则有Linux ext2/ext3文件系统、LUKS加密分区、GNOME、KDE、Mac OS X等等。另外我们也可以在e2fsprogs包中的UUID库找到实现。 总之一句话,保证在不同时间、不同地点、不同设备上产生的UUID的唯一性,不重复。 Java生成UUID在java中有现成的api可以直接生成UUID 123456System.out.println(UUID.randomUUID().toString());System.out.println(UUID.randomUUID().toString());System.out.println(UUID.randomUUID().toString());System.out.println(UUID.randomUUID().toString());System.out.println(UUID.randomUUID().toString());System.out.println(UUID.randomUUID().toString()); 生成的结果每次都不一样,比如 12345647992182-32cc-4979-88b8-ee1db54b0172f6162463-4f26-4520-ba4f-f853fb5f8c99104a8450-7c8f-4c34-839d-5d5902b9cc90f97389f9-81e6-406e-820a-1da96a68ddac59a6dc1a-07d6-467b-a6df-eb11207fa16843daf170-1f79-4e8c-af22-001c32c045c2 可看出生成的UUID格式是这样的xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,位数为8-4-4-4-12共32位16进制,中间用”-“号隔开 如果想单纯返回32位16进制,可以用replace去掉“-”号 123456System.out.println(UUID.randomUUID().toString().replace("-", ""));System.out.println(UUID.randomUUID().toString().replace("-", ""));System.out.println(UUID.randomUUID().toString().replace("-", ""));System.out.println(UUID.randomUUID().toString().replace("-", ""));System.out.println(UUID.randomUUID().toString().replace("-", ""));System.out.println(UUID.randomUUID().toString().replace("-", "")); 返回结果如下 123456df12e44ebc3a420c808cdcdc6e650c6dfdfd016ccbf644e29ccc81e589eeca90bb08a994992f46feadde1aa5764816350cc5d367b8024cbcaa8441e4284c1e3ab7c7ba6bc422456c98226d8ce5cff1f5c17e81750471463ca064cc2efcf3849e]]></content>
</entry>
<entry>
<title><![CDATA[Android Studio 如何录屏]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Studio-%E5%A6%82%E4%BD%95%E5%BD%95%E5%B1%8F%2F</url>
<content type="text"><![CDATA[首先连接手机或者打开模拟器,然后打开要录制的App 在Android Studio底部找到Logcat切换到Logcat面板,然后点击左边的录屏的小按钮 在弹出的对话框中点击Start Recording开始录制 下图表示录制中,若想结束录制直接点右边的Stop Recording按钮 在弹出的保存对话框中选择保存路径,设置保存文件的名称,最后点击OK按钮保存。]]></content>
</entry>
<entry>
<title><![CDATA[Android Studio 如何对应用截屏]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Studio-%E5%A6%82%E4%BD%95%E5%AF%B9%E5%BA%94%E7%94%A8%E6%88%AA%E5%B1%8F%2F</url>
<content type="text"><![CDATA[首先要连接手机,然后进入到你要截屏的页面 然后在Android Studio中切换到Logcat,然后点击左边的相机图标 在弹出的页面中可以预览要截屏的页面,点击右下角的保存按钮 最后选择要保存的路径,点击OK按钮即可。]]></content>
</entry>
<entry>
<title><![CDATA[Android gradle用exclude排除引用包中的dependency引用]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-gradle%E7%94%A8exclude%E6%8E%92%E9%99%A4%E5%BC%95%E7%94%A8%E5%8C%85%E4%B8%AD%E7%9A%84dependency%E5%BC%95%E7%94%A8%2F</url>
<content type="text"><![CDATA[项目突然编译不通过,报如下错误 1234567891011121314FAILURE: Build failed with an exception.* What went wrong:Execution failed for task ':app:transformDexArchiveWithExternalLibsDexMergerForDebug'.> com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives: D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\104.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\115.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\93.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\39.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\12.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\21.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\47.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\4.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\5.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\22.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\14.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\30.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\31.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\13.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\48.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\10.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\19.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\49.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\6.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\23.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\11.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\41.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\24.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\7.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\34.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\17.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\26.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\42.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\51.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\25.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\43.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\8.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\52.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\27.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\0.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\44.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\18.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\35.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\9.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\45.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\28.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\15.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\1.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\2.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\32.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\46.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\16.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\29.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\3.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\50.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\20.jar Program type already present: android.support.v4.widget.SlidingPaneLayout$AccessibilityDelegate Learn how to resolve the issue at https://developer.android.com/studio/build/dependencies#duplicate_classes.* Try:Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.* Get more help at https://help.gradle.orgBUILD FAILED in 19s 经排查发现原来是项目中引用的第三方包’liji.library.dev:citypickerview:4.1.1’中引用的v7包和项目引用的v7包冲突导致的。 可以直接删除项目的v7包引用,直接用第三包里面的v7包就可以了。 但是如果第三方包引用的包太老旧,你执意要用最新的,那该怎么办呢?你可以用exclude把第三方包里的指定dependency排除掉,然后再重新编译就可以成功了。 123456dependencies { implementation 'com.android.support:appcompat-v7:28.0.0' implementation ('liji.library.dev:citypickerview:4.1.1') { exclude group: 'com.android.support' }}]]></content>
</entry>
<entry>
<title><![CDATA[Android 编译报Run with --stacktrace option to get the stack trace]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-%E7%BC%96%E8%AF%91%E6%8A%A5Run-with-stacktrace-option-to-get-the-stack-trace%2F</url>
<content type="text"><![CDATA[Android开发在编译过程中经常会报类似如下的错误,突如其来经常会让我们不知所措。这时候最需要的就是静下心来,照着报错的提示排查问题。 123456789101112131415FAILURE: Build failed with an exception.* What went wrong:Execution failed for task ':app:transformDexArchiveWithExternalLibsDexMergerForDebug'.> com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives: D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\77.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\80.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\81.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\82.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\79.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\74.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\73.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\76.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\75.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\78.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\17.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\26.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\25.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\12.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\8.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\21.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\4.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\5.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\27.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\0.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\22.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\14.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\30.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\31.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\13.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\18.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\9.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\28.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\10.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\15.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\1.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\2.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\19.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\6.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\23.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\11.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\16.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\29.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\3.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\24.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\7.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\20.jar, D:\ws\AutosizingTest\app\build\intermediates\transforms\dexBuilder\debug\72.jar Learn how to resolve the issue at https://developer.android.com/studio/build/dependencies#duplicate_classes. Program type already present: com.google.gson.FieldAttributes* Try:Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.* Get more help at https://help.gradle.orgBUILD FAILED in 17s39 actionable tasks: 15 executed, 24 up-to-date Run with –stacktrace option to get the stack trace. Run with –info or –debug option to get more log output. Run with –scan to get full insights.这句话是重点,但是说得不明不白的也不知道要怎么操作。 其实很简单,首先在Android Studio找到Terminal控制台(这个其实和CMD命令行控制台是一样的) 然后在控制台中输入如下指令,就可以重新编译debug版本,且带参数–stacktrace编译,如果编译失败会有失败的路径信息。 1gradlew compileDebug --stacktrace 或者–info和–debug参数也用上会打印更多的log信息。 1gradlew compileDebug --stacktrace --info --debug]]></content>
</entry>
<entry>
<title><![CDATA[java 四舍五入BigDecimal.ROUND_HALF_DOWN和BigDecimal.ROUND_HALF_UP的区别]]></title>
<url>%2F2019%2F07%2F06%2Fjava-%E5%9B%9B%E8%88%8D%E4%BA%94%E5%85%A5BigDecimal-ROUND-HALF-DOWN%E5%92%8CBigDecimal-ROUND-HALF-UP%E7%9A%84%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[BigDecimal.ROUND_HALF_DOWN最贴切的说法应该是叫五舍六入,舍弃的部分如果大于5才进位,小于或等于5直接舍弃。 BigDecimal.ROUND_HALF_UP就是我们小学教的四舍五入,舍弃的部分如果大于等于5就进位,小于5的直接舍弃。 直接写几行代码输出验证下就很清楚了 注意方法setScale第一个参数为保留小数点后的位数 123456System.out.println("BigDecimal.valueOf(1.234).setScale(2,BigDecimal.ROUND_HALF_DOWN)="+BigDecimal.valueOf(1.234).setScale(2,BigDecimal.ROUND_HALF_DOWN));System.out.println("BigDecimal.valueOf(1.235).setScale(2,BigDecimal.ROUND_HALF_DOWN)="+BigDecimal.valueOf(1.235).setScale(2,BigDecimal.ROUND_HALF_DOWN));System.out.println("BigDecimal.valueOf(1.236).setScale(2,BigDecimal.ROUND_HALF_DOWN)="+BigDecimal.valueOf(1.236).setScale(2,BigDecimal.ROUND_HALF_DOWN));System.out.println("BigDecimal.valueOf(1.234).setScale(2,BigDecimal.ROUND_HALF_UP)="+BigDecimal.valueOf(1.234).setScale(2,BigDecimal.ROUND_HALF_UP));System.out.println("BigDecimal.valueOf(1.235).setScale(2,BigDecimal.ROUND_HALF_UP)="+BigDecimal.valueOf(1.235).setScale(2,BigDecimal.ROUND_HALF_UP));System.out.println("BigDecimal.valueOf(1.236).setScale(2,BigDecimal.ROUND_HALF_UP)="+BigDecimal.valueOf(1.236).setScale(2,BigDecimal.ROUND_HALF_UP)); 输出结果为 123456BigDecimal.valueOf(1.234).setScale(2,BigDecimal.ROUND_HALF_DOWN)=1.23BigDecimal.valueOf(1.235).setScale(2,BigDecimal.ROUND_HALF_DOWN)=1.23BigDecimal.valueOf(1.236).setScale(2,BigDecimal.ROUND_HALF_DOWN)=1.24BigDecimal.valueOf(1.234).setScale(2,BigDecimal.ROUND_HALF_UP)=1.23BigDecimal.valueOf(1.235).setScale(2,BigDecimal.ROUND_HALF_UP)=1.24BigDecimal.valueOf(1.236).setScale(2,BigDecimal.ROUND_HALF_UP)=1.24 再看下操作负数 123456System.out.println("BigDecimal.valueOf(-1.234).setScale(2,BigDecimal.ROUND_HALF_DOWN)="+BigDecimal.valueOf(-1.234).setScale(2,BigDecimal.ROUND_HALF_DOWN));System.out.println("BigDecimal.valueOf(-1.235).setScale(2,BigDecimal.ROUND_HALF_DOWN)="+BigDecimal.valueOf(-1.235).setScale(2,BigDecimal.ROUND_HALF_DOWN));System.out.println("BigDecimal.valueOf(-1.236).setScale(2,BigDecimal.ROUND_HALF_DOWN)="+BigDecimal.valueOf(-1.236).setScale(2,BigDecimal.ROUND_HALF_DOWN));System.out.println("BigDecimal.valueOf(-1.234).setScale(2,BigDecimal.ROUND_HALF_UP)="+BigDecimal.valueOf(-1.234).setScale(2,BigDecimal.ROUND_HALF_UP));System.out.println("BigDecimal.valueOf(-1.235).setScale(2,BigDecimal.ROUND_HALF_UP)="+BigDecimal.valueOf(-1.235).setScale(2,BigDecimal.ROUND_HALF_UP));System.out.println("BigDecimal.valueOf(-1.236).setScale(2,BigDecimal.ROUND_HALF_UP)="+BigDecimal.valueOf(-1.236).setScale(2,BigDecimal.ROUND_HALF_UP)); 输出结果为 123456BigDecimal.valueOf(-1.234).setScale(2,BigDecimal.ROUND_HALF_DOWN)=-1.23BigDecimal.valueOf(-1.235).setScale(2,BigDecimal.ROUND_HALF_DOWN)=-1.23BigDecimal.valueOf(-1.236).setScale(2,BigDecimal.ROUND_HALF_DOWN)=-1.24BigDecimal.valueOf(-1.234).setScale(2,BigDecimal.ROUND_HALF_UP)=-1.23BigDecimal.valueOf(-1.235).setScale(2,BigDecimal.ROUND_HALF_UP)=-1.24BigDecimal.valueOf(-1.236).setScale(2,BigDecimal.ROUND_HALF_UP)=-1.24 可以看出所谓的down和up是相对0点的,down就是选择靠近0那边的数,up就是选择远离0一边的数 比如1.235,靠近0那边的是1.23,远离0的是1.24,用BigDecimal.ROUND_HALF_DOWN处理就选择靠近0那边的1.23,用BigDecimal.ROUND_HALF_UP处理就选择远离0的1.24 再比如-1.235,靠近0那边的是-1.23,远离0的是-1.24,用BigDecimal.ROUND_HALF_DOWN处理就选择靠近0那边的-1.23,用BigDecimal.ROUND_HALF_UP处理就选择远离0的-1.24 也可以这样说,down就是选择绝对值小的那个(绝对值越小越靠近0),up就是选择绝对值大的那个(绝对值越大越远离0)]]></content>
</entry>
<entry>
<title><![CDATA[java 四舍五入new BigDecimal(double)及BigDecimal valueOf(double)的区别]]></title>
<url>%2F2019%2F07%2F06%2Fjava-%E5%9B%9B%E8%88%8D%E4%BA%94%E5%85%A5new-BigDecimal-double-%E5%8F%8ABigDecimal-valueOf-double-%E7%9A%84%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[最近在研究java的四舍五入,其中有一个方法如下 1new BigDecimal(val).setScale(newScale, BigDecimal.ROUND_HALF_DOWN)); 其中val是要处理的浮点数 newScale表示要保留小数点后几位 BigDecimal.ROUND_HALF_DOWN表示若舍弃的部分>0.5则进位,否则直接舍弃,说白了就是五舍六入 比如说1.234,保留两位小数处理结果为1.23 比如说1.235,保留两位小数处理结果为1.23 比如说1.236,保留两位小数处理结果为1.24 为了验证我的猜测,马上写几行代码验证下 123System.out.println(new BigDecimal(1.234).setScale(2, BigDecimal.ROUND_HALF_DOWN));System.out.println(new BigDecimal(1.235).setScale(2, BigDecimal.ROUND_HALF_DOWN));System.out.println(new BigDecimal(1.236).setScale(2, BigDecimal.ROUND_HALF_DOWN)); 发现第二条结果与我的猜测不一致,感觉有点受伤 1231.231.241.24 1.235小数点后三位为5,明明是要舍弃的,为何进位了,难道网上的教程都是骗人 折腾了好久,最后发现原来我的写法跟网上别人的还是有细微差别 我的是:new BigDecimal(1.235).setScale(2, BigDecimal.ROUND_HALF_DOWN) 别人家的是:new BigDecimal(“1.235”).setScale(2, BigDecimal.ROUND_HALF_DOWN),这样的写法结果确实是1.23 可是为何同样的数值,传double类型不行,传String就可以?难道这两种方法初始化的BigDecimal大小不一样?打印出来发现确实不一样 1234// 打印结果为1.2350000000000000976996261670137755572795867919921875 System.out.println(new BigDecimal(1.235).toString());// 打印结果为1.235System.out.println(new BigDecimal("1.235").toString()); new BigDecimal(1.235)的真实值为1.2350000000000000976996261670137755572795867919921875这一长串数字,舍弃的部分是大于0.5的,所以才进位的。 所以为了避免得到错误的结果还是建议传String类型的值,如果是double类型就用如下方法先转成String再处理 12new BigDecimal(String.valueOf(1.235)).setScale(2, BigDecimal.ROUND_HALF_DOWN);new BigDecimal(Double.toString(1.235)).setScale(2, BigDecimal.ROUND_HALF_DOWN); 还有另一种方法是直接传double类型的 1BigDecimal.valueOf(1.235).setScale(2, BigDecimal.ROUND_HALF_DOWN); 查看BigDecimal.valueOf源码,其内部实现其实也是先转成String 123456789101112131415161718192021222324/** * Translates a {@code double} into a {@code BigDecimal}, using * the {@code double}'s canonical string representation provided * by the {@link Double#toString(double)} method. * * <p><b>Note:</b> This is generally the preferred way to convert * a {@code double} (or {@code float}) into a * {@code BigDecimal}, as the value returned is equal to that * resulting from constructing a {@code BigDecimal} from the * result of using {@link Double#toString(double)}. * * @param val {@code double} to convert to a {@code BigDecimal}. * @return a {@code BigDecimal} whose value is equal to or approximately * equal to the value of {@code val}. * @throws NumberFormatException if {@code val} is infinite or NaN. * @since 1.5 */public static BigDecimal valueOf(double val) { // Reminder: a zero double returns '0.0', so we cannot fastpath // to use the constant ZERO. This might be important enough to // justify a factory approach, a cache, or a few private // constants, later. return new BigDecimal(Double.toString(val));} 总结用BigDecimal处理浮点数时BigDecimal的初始化最好采用如下方法 12double d = 1.235;BigDecimal bd = BigDecimal.valueOf(d);]]></content>
</entry>
<entry>
<title><![CDATA[Java Math.floor()、Math.ceil()和Math.round()四舍五入的使用及区别]]></title>
<url>%2F2019%2F07%2F06%2FJava-Math-floor-%E3%80%81Math-ceil-%E5%92%8CMath-round-%E5%9B%9B%E8%88%8D%E4%BA%94%E5%85%A5%E7%9A%84%E4%BD%BF%E7%94%A8%E5%8F%8A%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[Math.floor()、Math.ceil()和Math.round()都是对浮点数取整(floor和ceil返回不带小数的double,round返回long) floor字面意思为地板,返回的是小于或等于该数值的最大的整数 ceil字面意思是天花板,返回的是大于或等于该值的最小的整数 round就是我们常见的四舍五入,不再多说 直接看下demo及运行结果 123456789101112131415161718192021222324252627282930public class Test4 { public static void main(String[] args) { System.out.println("Math.floor(1.0) = " + Math.floor(1.0)); System.out.println("Math.floor(1.4) = " + Math.floor(1.4)); System.out.println("Math.floor(1.5) = " + Math.floor(1.5)); System.out.println("Math.floor(1.6) = " + Math.floor(1.6)); System.out.println("Math.floor(-1.0) = " + Math.floor(-1.0)); System.out.println("Math.floor(-1.4) = " + Math.floor(-1.4)); System.out.println("Math.floor(-1.5) = " + Math.floor(-1.5)); System.out.println("Math.floor(-1.6) = " + Math.floor(-1.6)); System.out.println("Math.ceil(1.0) = " + Math.ceil(1.0)); System.out.println("Math.ceil(1.4) = " + Math.ceil(1.4)); System.out.println("Math.ceil(1.5) = " + Math.ceil(1.5)); System.out.println("Math.ceil(1.6) = " + Math.ceil(1.6)); System.out.println("Math.ceil(-1.0) = " + Math.ceil(-1.0)); System.out.println("Math.ceil(-1.4) = " + Math.ceil(-1.4)); System.out.println("Math.ceil(-1.5) = " + Math.ceil(-1.5)); System.out.println("Math.ceil(-1.6) = " + Math.ceil(-1.6)); System.out.println("Math.round(1.0) = " + Math.round(1.0)); System.out.println("Math.round(1.4) = " + Math.round(1.4)); System.out.println("Math.round(1.5) = " + Math.round(1.5)); System.out.println("Math.round(1.6) = " + Math.round(1.6)); System.out.println("Math.round(-1.0) = " + Math.round(-1.0)); System.out.println("Math.round(-1.4) = " + Math.round(-1.4)); System.out.println("Math.round(-1.5) = " + Math.round(-1.5)); System.out.println("Math.round(-1.6) = " + Math.round(-1.6)); }} 运行结果 123456789101112131415161718192021222324Math.floor(1.0) = 1.0Math.floor(1.4) = 1.0Math.floor(1.5) = 1.0Math.floor(1.6) = 1.0Math.floor(-1.0) = -1.0Math.floor(-1.4) = -2.0Math.floor(-1.5) = -2.0Math.floor(-1.6) = -2.0Math.ceil(1.0) = 1.0Math.ceil(1.4) = 2.0Math.ceil(1.5) = 2.0Math.ceil(1.6) = 2.0Math.ceil(-1.0) = -1.0Math.ceil(-1.4) = -1.0Math.ceil(-1.5) = -1.0Math.ceil(-1.6) = -1.0Math.round(1.0) = 1Math.round(1.4) = 1Math.round(1.5) = 2Math.round(1.6) = 2Math.round(-1.0) = -1Math.round(-1.4) = -1Math.round(-1.5) = -1Math.round(-1.6) = -2 需要注意的是正负值处理后的结果 比如 Math.floor(1.5) = 1.0,Math.floor(-1.5) = -2.0 Math.ceil(1.5) = 2.0,Math.ceil(-1.5) = -1.0 Math.round(1.5) = 2,Math.round(-1.5) = -1]]></content>
</entry>
<entry>
<title><![CDATA[Android dp和px互转、sp和px互转及背后的原理]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-dp%E5%92%8Cpx%E4%BA%92%E8%BD%AC%E3%80%81sp%E5%92%8Cpx%E4%BA%92%E8%BD%AC%E5%8F%8A%E8%83%8C%E5%90%8E%E7%9A%84%E5%8E%9F%E7%90%86%2F</url>
<content type="text"><![CDATA[先上代码,拿来即用 12345678910111213141516171819202122232425262728293031/** * dp转px */public static int dp2px(Context context, float dp) { float density = context.getResources().getDisplayMetrics().density; return (int) (dp * density + 0.5f);}/** * sp转px */public static int sp2px(Context context, float sp) { float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity; return (int) (sp * scaledDensity + 0.5f);}/** * px转dp */public static int px2dp(Context context, float px) { float density = context.getResources().getDisplayMetrics().density; return (int) (px / density + 0.5f);}/** * px转sp */public static int px2sp(Context context, float px) { float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity; return (int) (px / scaledDensity + 0.5f);} 源码解析关于dp和px、sp和px的互转,网上很多都是这样写的,很多人都是copy过来直接使用,但是有没有人想过为什么呢? 其实Android原生Api有提供dip和sp转px的方法,使用如下: 1234// 10dp转pxTypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources().getDisplayMetrics());// 10sp转pxTypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10, context.getResources().getDisplayMetrics()); 我们再看下TypedValue.applyDimension()方法的源码 1234567891011121314151617181920212223242526272829303132/** * Converts an unpacked complex data value holding a dimension to its final floating * point value. The two parameters <var>unit</var> and <var>value</var> * are as in {@link #TYPE_DIMENSION}. * * @param unit The unit to convert from. * @param value The value to apply the unit to. * @param metrics Current display metrics to use in the conversion -- * supplies display density and scaling information. * * @return The complex floating point value multiplied by the appropriate * metrics depending on its unit. */public static float applyDimension(int unit, float value, DisplayMetrics metrics){ switch (unit) { case COMPLEX_UNIT_PX: return value; case COMPLEX_UNIT_DIP: return value * metrics.density; case COMPLEX_UNIT_SP: return value * metrics.scaledDensity; case COMPLEX_UNIT_PT: return value * metrics.xdpi * (1.0f/72); case COMPLEX_UNIT_IN: return value * metrics.xdpi; case COMPLEX_UNIT_MM: return value * metrics.xdpi * (1.0f/25.4f); } return 0;} 看了以上源码是不是瞬间就明白了dp2px和sp2px的原理,只要看以上源码的第二和第三个case px = dp * density px = sp * scaledDensity 然后我们又可以反推出 dp = px / density sp = px / scaledDensity 为什么还要加0.5可能有人会问了,为什么我们开头的dp2px、sp2px、px2dp、px2sp四个方法里都还要加0.5? 其实也可以不加0.5,如果返回值是float的话 12345678910111213141516171819202122232425262728293031/** * dp转px */public static float dp2px(Context context, float dp) { float density = context.getResources().getDisplayMetrics().density; return dp * density;}/** * sp转px */public static float sp2px(Context context, float sp) { float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity; return sp * scaledDensity;}/** * px转dp */public static float px2dp(Context context, float px) { float density = context.getResources().getDisplayMetrics().density; return px / density;}/** * px转sp */public static float px2sp(Context context, float px) { float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity; return px / scaledDensity;} 但是Android中很多有关尺寸的设置都是px,且是int类型,比如view.setWidth()、view.setHeight()的参数都是int类型,所以更多时候我们需要用到的是int类型。 那么如果要转化为int类型就需要加上0.5达到四舍五入的效果。 那么问题又来了,为什么加上0.5就可以四舍五入呢?举个栗子 java中浮点型强制类型转换成int类型是没有四舍五入的,而是直接把小数点后的值直接去掉 123456i1 = (int) 1.0; // 结果为1i2 = (int) 1.1; // 结果为1i3 = (int) 1.4; // 结果为1i4 = (int) 1.5; // 结果为1i5 = (int) 1.6; // 结果为1i6 = (int) 1.9; // 结果为1 如果加上0.5再转换,看下结果 123456i1 = (int) (1.0 + 0.5); // 结果为1i2 = (int) (1.1 + 0.5); // 结果为1i3 = (int) (1.4 + 0.5); // 结果为1i4 = (int) (1.5 + 0.5); // 结果为2i5 = (int) (1.6 + 0.5); // 结果为2i6 = (int) (1.9 + 0.5); // 结果为2 只要小数点后一位大于等于5再加上0.5就会达到五入的效果,得到更精确的值。这也就是为什么要加0.5的原因。]]></content>
</entry>
<entry>
<title><![CDATA[Android通过代码主动弹出或隐藏输入法软键盘]]></title>
<url>%2F2019%2F07%2F06%2FAndroid%E9%80%9A%E8%BF%87%E4%BB%A3%E7%A0%81%E4%B8%BB%E5%8A%A8%E5%BC%B9%E5%87%BA%E6%88%96%E9%9A%90%E8%97%8F%E8%BE%93%E5%85%A5%E6%B3%95%E8%BD%AF%E9%94%AE%E7%9B%98%2F</url>
<content type="text"><![CDATA[软键盘的弹出一般是在用户点击Edittext获取焦点后自动弹出,隐藏的话是用户主动点击软件盘上的向下收起按钮或完成按钮后软件盘会收起来。 但是有时候我们有这样的需求,比如说点击某个按钮弹出软键盘,点击另一个按钮收起软键盘。这是候就需要通过InputMethodManager来实现。 先看下实现的效果: 弹出软键盘1234567public static void showSoftInput(Context context, View view) { InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); if (view != null && imm != null){ imm.showSoftInput(view, 0); // imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); // 或者第二个参数传InputMethodManager.SHOW_IMPLICIT }} 需要注意的是传入的View必须是Editext等能够获取焦点接收软件盘输入的控件才有效果,比如传入Button控件的话就无法弹出软键盘。 对于InputMethodManager.showSoftInput(View view, int flags)方法的第二个参数,看源码注释说可以传入0或者InputMethodManager.SHOW_IMPLICIT。我实际测试都可以弹出软键盘,目前还未发现有何区别。 该方法源码如下 12345678910111213/** * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without * a result receiver: explicitly request that the current input method's * soft input area be shown to the user, if needed. * * @param view The currently focused view, which would like to receive * soft keyboard input. * @param flags Provides additional operating flags. Currently may be * 0 or have the {@link #SHOW_IMPLICIT} bit set. */public boolean showSoftInput(View view, int flags) { return showSoftInput(view, flags, null);} 收起软键盘1234567public static void hideSoftInput(Context context, View view) { InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); if (view != null && imm != null){ imm.hideSoftInputFromWindow(view.getWindowToken(), 0); // imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY); // 或者第二个参数传InputMethodManager.HIDE_IMPLICIT_ONLY }} 收起软键盘传入的View参数没要求一定要是Edittext,只要是当前页面的任意View都可以,比如我传入DecorView照样可以弹出软键盘hideSoftInput(context, getWindow().getDecorView())。 对于InputMethodManager.hideSoftInputFromWindow(IBinder windowToken, int flags)方法的第二个参数,看源码注释说可以传入0或者InputMethodManager.HIDE_IMPLICIT_ONLY。根据源码注释和测试可以知道这两者的区别: 设置成0的话,不管软键盘是由用户点击Edittext后弹出的还是通过调用代码弹出的,都可以收起来。 设置成InputMethodManager.HIDE_IMPLICIT_ONLY的话,用户点击Edittext弹出的软键盘可以收起来,但是通过代码弹出的软键盘无法收起。 InputMethodManager.hideSoftInputFromWindow的源码如下: 12345678910111213/** * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)} * without a result: request to hide the soft input window from the * context of the window that is currently accepting input. * * @param windowToken The token of the window that is making the request, * as returned by {@link View#getWindowToken() View.getWindowToken()}. * @param flags Provides additional operating flags. Currently may be * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set. */public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) { return hideSoftInputFromWindow(windowToken, flags, null);}]]></content>
</entry>
<entry>
<title><![CDATA[Android 编译报XML declaration not well-formed错误的解决方法]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-%E7%BC%96%E8%AF%91%E6%8A%A5XML-declaration-not-well-formed%E9%94%99%E8%AF%AF%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95%2F</url>
<content type="text"><![CDATA[项目上一秒还运行得好好的,下一秒编译却突然不成功,报如下错误 12345678910111213141516171819FAILURE: Build failed with an exception.* What went wrong:Execution failed for task ':app:mergeDebugResources'.> java.util.concurrent.ExecutionException: com.android.builder.internal.aapt.v2.Aapt2Exception: Android resource compilation failed Output: D:\ws\AutosizingTest\app\src\main\res\layout\activity_main.xml:1: error: XML declaration not well-formed. Command: C:\Users\Him\.gradle\caches\transforms-1\files-1.1\aapt2-3.2.1-4818971-windows.jar\4fc997b490c0c003506edf14eef98945\aapt2-3.2.1-4818971-windows\aapt2.exe compile --legacy \ -o \ D:\ws\AutosizingTest\app\build\intermediates\res\merged\debug \ D:\ws\AutosizingTest\app\src\main\res\layout\activity_main.xml Daemon: AAPT2 aapt2-3.2.1-4818971-windows Daemon #0* Try:Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.* Get more help at https://help.gradle.orgBUILD FAILED in 1s 根据错误提示是布局文件错误,可是看了老半天没发现什么错误啊,而且布局文件也没动过啊 123456789<?xml dversion="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content"/> <!--此处省略好多代码--></RelativeLayout> 而且错误提示也是说的不清不楚的,无法定位问题的原因,这种情况你越急越找不到原因。 遇到问题不要总是想当然的认为自己代码没问题,自己什么也没有动,就开始骂mmp。这时候需要静下心来。 根据错误提示这句话activity_main.xml:1: error: XML declaration not well-formed可以知道问题肯定出在activity_main.xml,而且是在第一行,仔细一看发现这句话中version前多了一个d,导致了编译不通过。有可能是不小心按到键盘导致的。 所以遇到问题一定不要慌,事出必定有因,根据错误提示静下心来排查,肯定能找出问题的原因。]]></content>
</entry>
<entry>
<title><![CDATA[Android adjustResize实现弹出软键盘不遮挡Edittext且顶部标题栏固定]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-adjustResize%E5%AE%9E%E7%8E%B0%E5%BC%B9%E5%87%BA%E8%BD%AF%E9%94%AE%E7%9B%98%E4%B8%8D%E9%81%AE%E6%8C%A1Edittext%E4%B8%94%E9%A1%B6%E9%83%A8%E6%A0%87%E9%A2%98%E6%A0%8F%E5%9B%BA%E5%AE%9A%2F</url>
<content type="text"><![CDATA[这里以一个简单的Demo演示下 界面如下所以 界面的XML如下所示 1234567891011121314151617181920<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="我是顶部的Button"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@id/edit_text" android:text="我是底部的Button"/> <EditText android:id="@+id/edit_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:text="我是底部的Edittext"/></RelativeLayout> 在点击底部的Edittext后弹出软键盘,软键盘把Edittext顶上去了,没有被遮挡,但是顶部的标题栏和按钮却被顶到了布局外看不见了,如下gif所示。这样的体验其实也不好,因为用户在输入的同时,有可能要操作标题栏上的ActionButton,但是此时标题已被顶出布局外,用户还要先收起软键盘,再去操作标题栏上的按钮。 这时候就该adjustResize上场了,我们可以在Manifest中设置Activity的android:windowSoftInputMode属性值为”adjustResize”,如下代码所示 1234<activity android:name=".MainActivity" android:windowSoftInputMode="adjustResize"></activity> 设置完的运行效果如gif图所示 Perfect,正是我们想要的效果,底部的内容被输入法往上顶,输入的时候不会被遮挡,顶部的标题栏又不会被顶出布局。]]></content>
</entry>
<entry>
<title><![CDATA[Android 通过父布局抢占Edittext焦点实现刚进入Activity不弹出软键盘]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-%E9%80%9A%E8%BF%87%E7%88%B6%E5%B8%83%E5%B1%80%E6%8A%A2%E5%8D%A0Edittext%E7%84%A6%E7%82%B9%E5%AE%9E%E7%8E%B0%E5%88%9A%E8%BF%9B%E5%85%A5Activity%E4%B8%8D%E5%BC%B9%E5%87%BA%E8%BD%AF%E9%94%AE%E7%9B%98%2F</url>
<content type="text"><![CDATA[在Android 进入Activity禁止弹出软键盘输入法及stateHidden和stateAlwaysHidden的区别这篇文章中我们通过设置Activity的android:windowSoftInputMode属性为stateHidden或者stateAlwaysHidden实现了进入Activity不马上弹出软键盘的功能,今天我们介绍另一种方法,通过父布局抢占Edittext焦点,从而不然软键盘弹出。 实现代码如下所示 12345678910<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:focusableInTouchMode="true" android:focusable="true"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content"/></LinearLayout> 马上在我的魅族MX5手机上运行一下,发现软键盘是收起来了,但是却是在弹出后马上又收起来的。看起来就是软键盘闪了一下马上消失,这样的用户体验是很不好的。 既然这样的体验不好为什么网上一搜还是有很多人推荐这样的方法,唯一的可能就是他们看到的结果和我不一样,他们的手机上的运行结果可能确实是软键盘没有弹出来。 我马上用用Android模拟器试了下,发现模拟器上确实未弹出软键盘,不像在MX5手机上那样弹出又马上消失。 总结这种方法并不能保证对所有型号手机使用,所以不推荐使用。还是推荐Android官方推荐的方法,通过设置android:windowSoftInputMode属性来实现,具体参考这篇文章Android 进入Activity禁止弹出软键盘输入法及stateHidden和stateAlwaysHidden的区别。]]></content>
</entry>
<entry>
<title><![CDATA[Android 进入Activity主动弹出软键盘输入法及stateVisible和stateAlwaysVisible的区别]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-%E8%BF%9B%E5%85%A5Activity%E4%B8%BB%E5%8A%A8%E5%BC%B9%E5%87%BA%E8%BD%AF%E9%94%AE%E7%9B%98%E8%BE%93%E5%85%A5%E6%B3%95%E5%8F%8AstateVisible%E5%92%8CstateAlwaysVisible%E7%9A%84%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[在App开发中有些页面的功能定位就是搜索,用户进入该页面的唯一仅有的目的就是搜索,比如淘宝点击搜索框跳转的搜索商品页面,对于这种页面最好的设计就是一进入就把焦点定位在输入框,且弹出输入法,用户一进入进可以直接在软键盘上输入,而不必要再点一下输入框再弹出输入法。虽然是一个很细节的东西,但却是很好的用户体验。 废话这么多,该上代码了 123<activity android:name=".MainActivity" android:windowSoftInputMode="stateVisible"/> 还有另一个属性stateAlwaysVisible也可以实现同样的功能。 123<activity android:name=".MainActivity" android:windowSoftInputMode="stateAlwaysVisible"/> stateVisible和stateAlwaysVisible的区别既然stateVisible和stateAlwaysVisible都可以实现一进入Activity就弹出软键盘的功能,那么这两者又有什么区别呢?我们先看下谷歌官网的说明: stateVisible The soft keyboard is visible when that’s normally appropriate (when the user is navigating forward to the activity’s main window). stateAlwaysVisible The soft keyboard is made visible when the user chooses the activity — that is, when the user affirmatively navigates forward to the activity, rather than backs into it because of leaving another activity. 但是我个人觉得官网的解释有误(如果我的英语理解能力没错的话),对于stateAlwaysVisible,官网的说明是说从其他地方进入Activity的话,会弹出软件盘,如果是从其他页面返回的话是不会弹出的。 单我的理解是设置成stateAlwaysVisible的话,不管是往前进入,还是从其他页面返回,总是会弹出软键盘。 为了验证我的想法,我写了两个Activity验证下 MainActivity中有一个Edittext和一个Button,点击Button跳转到SecondActivity 首先先设置MainActibity的android:windowSoftInputMode=”stateVisible”,首先进入MainActivity,软键盘自动弹出,然后收起软键盘,点击Button跳转SecondActivity后再返回MainActivity,发现软键盘不弹出了。但是如果我们收起软键盘后不是进入SecondActivity再返回,而是按Home键返回左面再重新进入MainActivity,会发现输入法会重新弹出。 接下来设置MainActibity的android:windowSoftInputMode=”stateAlwaysVisible”,首先进入MainActivity,软键盘自动弹出,然后收起软键盘,点击Button跳转SecondActivity后再返回MainActivity,发现已经收起的软键盘会再次弹出来。这也验证了我的想法,说明官网的描述有误。 总结stateVisible:从其他页面或桌面往前进入该Activity,会弹出软件盘,如果是从其他页面返回该Activity,则已收起的软键盘不会再弹出。 stateAlwaysVisible:不管是从从其他页面或桌面往前进入该Activity,还是从其他页面返回该Activity,已收起的软键盘都会再弹出。 在代码中设置softInputMode当然,我们也可以在Activity的onCreate中设置softInputMode,如下代码所示,和在Manifest中设置是等价的 1234// 等价于android:windowSoftInputMode="stateVisible"getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);// 等价于android:windowSoftInputMode="stateAlwaysVisible"getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);]]></content>
</entry>
<entry>
<title><![CDATA[Android 进入Activity禁止弹出软键盘输入法及stateHidden和stateAlwaysHidden的区别]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-%E8%BF%9B%E5%85%A5Activity%E7%A6%81%E6%AD%A2%E5%BC%B9%E5%87%BA%E8%BD%AF%E9%94%AE%E7%9B%98%E8%BE%93%E5%85%A5%E6%B3%95%E5%8F%8AstateHidden%E5%92%8CstateAlwaysHidden%E7%9A%84%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[在开发中经常会遇到这样的问题,进入一个含Edittext的Activity后总是会自动弹出软件盘。但是有时候我们又不想让它弹出,因为影响美观,而且用户可能也会反感。最好的做法是刚进入时不让弹出,在用户输入的时候才让弹出,把主动权交个用户是对用户最大的尊重。 废话这么多,直接上代码 123<activity android:name=".MainActivity" android:windowSoftInputMode="stateHidden"/> 发现用stateAlwaysHidden也可以禁止弹出软键盘 123<activity android:name=".MainActivity" android:windowSoftInputMode="stateAlwaysHidden"/> stateHidden和stateAlwaysHidden的区别既然stateHidden和stateAlwaysHidden都可以实现禁止弹出软键盘,那这两者到底有什么区别呢?网上看很多人都说得不清不楚,还不如自己上Android官网看最权威 “stateHidden“ The soft keyboard is hidden when the user chooses the activity — that is, when the user affirmatively navigates forward to the activity, rather than backs into it because of leaving another activity. “stateAlwaysHidden“ The soft keyboard is always hidden when the activity’s main window has input focus. 发现官网的说明还是有点晦涩难懂的,我以我的理解说明下 stateHidden:当用户进入Activity后保证是隐藏软键盘的,但是如果是从另一个页面返回该页面就不能保证了。比如说用户进入A页面,然后在输入框输入内容,未收起软键盘直接进入B页面,再从B页面返回A页面,此时A页面中软键盘还是显示的。又或者是用户由A页面进入B页面,在B页面弹出软键盘,未收起直接返回A页面,此时A页面中软键盘还是显示的。 stateAlwaysHidden:当用户刚进入Activity后软键盘也是隐藏的。与stateHidden的区别是,还是以上面的例子来说明,比如说用户进入A页面,然后在输入框输入内容,未收起软键盘直接进入B页面,再从B页面返回A页面,此时A页面中软键盘是收起来的。又或者是用户由A页面进入B页面,在B页面弹出软键盘,未收起直接返回A页面,此时A页面中软键盘是收起来的。 在代码中设置softInputMode当然,我们也可以在Activity的onCreate中设置softInputMode,如下代码所示,和在Manifest中设置是等价的 1234// 等价于android:windowSoftInputMode="stateHidden"getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);// 等价于android:windowSoftInputMode="stateAlwaysHidden"getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);]]></content>
</entry>
<entry>
<title><![CDATA[Android Edittext 软键盘输入法的回车键设置成搜索按钮并监听点击事件]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Edittext-%E8%BD%AF%E9%94%AE%E7%9B%98%E8%BE%93%E5%85%A5%E6%B3%95%E7%9A%84%E5%9B%9E%E8%BD%A6%E9%94%AE%E8%AE%BE%E7%BD%AE%E6%88%90%E6%90%9C%E7%B4%A2%E6%8C%89%E9%92%AE%E5%B9%B6%E7%9B%91%E5%90%AC%E7%82%B9%E5%87%BB%E4%BA%8B%E4%BB%B6%2F</url>
<content type="text"><![CDATA[很多App中都有搜索功能,比如微信中的搜索好友,你会发现在页面中是没有搜索按钮的,而是软键盘的回车键变成了搜索按钮。这样设计其实挺好的,节省了页面空间,而且用户输入内容后直接在软件盘上单击搜索而无需再返回页面点击搜索按钮。 不说废话了,直接上代码: 首先先设置回车键为搜索按钮,记得 android:singleLine=”true”这句必不可少,否则无法生效 123456<EditText android:id="@+id/edit_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:imeOptions="actionSearch" android:singleLine="true"/> 然后设置回车键的点击事件监听 123456789101112EditText editText = findViewById(R.id.edit_text);editText.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_SEARCH) { String keyword = v.getText().toString().trim(); Toast.makeText(MainActivity.this, keyword, Toast.LENGTH_SHORT).show(); return true; } return false; }});]]></content>
</entry>
<entry>
<title><![CDATA[Android Edittext设置软键盘输入法Enter回车键为完成按钮]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Edittext%E8%AE%BE%E7%BD%AE%E8%BD%AF%E9%94%AE%E7%9B%98%E8%BE%93%E5%85%A5%E6%B3%95Enter%E5%9B%9E%E8%BD%A6%E9%94%AE%E4%B8%BA%E5%AE%8C%E6%88%90%E6%8C%89%E9%92%AE%2F</url>
<content type="text"><![CDATA[软键盘中回车键默认功能是换行,单有时候我们想要实现的是点回车后收起软键盘,表示输入完成。比如登录页面中输完密码后点回车收起软件盘,然后登录。 示例代码代码很简单,如下所示,通过android:imeOptions=”actionDone”设置回车键为完成按钮,在不同的输入法中可能显示的内容会有不同,常见的会显示“完成”或者”Done”,点击完成按钮后软键盘会收起来。 还有一点别忘了,就是要设置android:singleLine=”true”,让Edittext只能输入一行,否则点击回车还是会换行。 12345<EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:imeOptions="actionDone" android:singleLine="true"/> 当然,我们还可以在代码中设置,实现相同的效果。 123EditText editText = findViewById(R.id.edit_text);editText.setImeOptions(EditorInfo.IME_ACTION_DONE);editText.setSingleLine(); // 这句话也是必不可少的]]></content>
</entry>
<entry>
<title><![CDATA[Android Edittext 软键盘输入法回车键改成下一步Next]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-Edittext-%E8%BD%AF%E9%94%AE%E7%9B%98%E8%BE%93%E5%85%A5%E6%B3%95%E5%9B%9E%E8%BD%A6%E9%94%AE%E6%94%B9%E6%88%90%E4%B8%8B%E4%B8%80%E6%AD%A5Next%2F</url>
<content type="text"><![CDATA[软件盘中回车键默认功能是换行,但是有时候我们在Edittext中输完内容后点回车想要把焦点切到下一个Edittext继续输入,比如常见的登录页面,在输完用户名后,点回车调到输入密码输入框继续输入。 示例代码代码很简单,如下所示: 123456789101112131415161718<LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <EditText android:id="@+id/account" android:layout_width="match_parent" android:layout_height="wrap_content" android:imeOptions="actionNext" android:singleLine="true"/> <EditText android:id="@+id/password" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textPassword" android:imeOptions="actionDone" android:singleLine="true"/></LinearLayout> 分析其实重点就以下两句话 android:imeOptions=”actionNext” android:singleLine=”true” android:imeOptions=”actionNext” 表示把回车键设置成下一步按钮,这里不同的输入法,不同的语言可能按钮上显示的文字会些许不同,比如有些手机上回显示下一步,有的显示下一个,有的英语输入法显示Next,意思大同小异。 android:singleLine=”true”意思是设置Edittext只能输入一行,要注意的这句话必不可少,否则android:imeOptions=”actionNext”的设置还是无法生效,点击回车还是会换行。想说用android:maxLines=”1”设置是不是也是等效的,结果发现还是会换行,只能用android:singleLine=”true”,虽然说android:singleLine属性已经被@Deprecated了。 nextFocusForward在上面的示例代码中,输完账号后点回车默认焦点是传递给下一个Edittext的。假设有三个Edittext,输完第一个后想跳过第二个Edittext直接输入第三个呢?这就需要靠nextFocusForward属性来实现 1234567891011121314151617181920212223242526<LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <EditText android:id="@+id/edit1" android:layout_width="match_parent" android:layout_height="wrap_content" android:imeOptions="actionNext" android:nextFocusForward="@+id/edit3" android:singleLine="true"/> <EditText android:id="@+id/edit2" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textPassword" android:imeOptions="actionNext" android:singleLine="true"/> <EditText android:id="@+id/edit3" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textPassword" android:imeOptions="actionDone" android:singleLine="true"/></LinearLayout> 代码很简单,关键看第一个Edittext中的android:nextFocusForward=”@+id/edit3”这句话,字面意思就是说下一个获取焦点的控件。需要注意设置的值写法是@+id/edit3而不是@id/edit3,少了加号的话无法编译成功。]]></content>
</entry>
<entry>
<title><![CDATA[Android 监听开机广播实现应用开机自启动]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-%E7%9B%91%E5%90%AC%E5%BC%80%E6%9C%BA%E5%B9%BF%E6%92%AD%E5%AE%9E%E7%8E%B0%E5%BA%94%E7%94%A8%E5%BC%80%E6%9C%BA%E8%87%AA%E5%90%AF%E5%8A%A8%2F</url>
<content type="text"><![CDATA[应用开机自启动的原理是监听开机广播android.intent.action.BOOT_COMPLETED,然后在BroadcastReceiver中打开应用 实现BroadcastReceiver首先实现一个BroadcastReceiver,该广播接收者监听”android.intent.action.BOOT_COMPLETED”广播,当接收到该广播时,打开该应用的启动页面。 123456789public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) { Intent toIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()); context.startActivity(toIntent); } }} 在Manifest中声明该广播接收者需要注意的是该广播接收者只能在Manifest中声明,而不能在代码中启动,否则无法接收到开机广播。 1234567<receiver android:name=".base.BootReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter></receiver> 声明权限最后别忘了在Manifest中声明接收开机广播的权限,很多人都忘了这一步,导致无法接收到广播 1<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> 无法接收到广播这里列举下一些常见的无法接收到开机广播的原因: 未添加权限。 被系统自带或360手机助手等拦截,需在权限管理设置里放开开机自启动的权限。 应用安装到sd卡上,安装在sd卡上的应用是收不到BOOT_COMPLETED广播的。 系统开启了Fast Boot模式,这种模式下系统启动并不会发送BOOT_COMPLETED广播。 应用程序安装后重来没有启动过,这种情况下应用程序接收不到任何广播。]]></content>
</entry>
<entry>
<title><![CDATA[adb shell 命令行模拟发送开机广播android.intent.action.BOOT_COMPLETED测试开机自启动]]></title>
<url>%2F2019%2F07%2F06%2Fadb-shell-%E5%91%BD%E4%BB%A4%E8%A1%8C%E6%A8%A1%E6%8B%9F%E5%8F%91%E9%80%81%E5%BC%80%E6%9C%BA%E5%B9%BF%E6%92%ADandroid-intent-action-BOOT-COMPLETED%E6%B5%8B%E8%AF%95%E5%BC%80%E6%9C%BA%E8%87%AA%E5%90%AF%E5%8A%A8%2F</url>
<content type="text"><![CDATA[开发中需求需要监听开机广播android.intent.action.BOOT_COMPLETED,然后让应用开机自启动。以前测试总是傻傻的关机然后再开机再验证有没自启动,很浪费时间,现在发现原来可以通过adb shell命令模拟开机广播,记录下 模拟开机广播1adb shell am broadcast -a android.intent.action.BOOT_COMPLETED 当然你也可以直接指定自己定义BroadcastReceiver接收广播,如下 1adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -n com.him.mypkg/com.him.mypkg.receiver.BootReceiver 权限拒绝在有写设备上可能会报类似java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.BOOT_COMPLETED from pid=3715, uid=2000这样的错误 遇到这个错误可以先执行adb root再执行发送广播的命令就可以了 12adb rootadb shell am broadcast -a android.intent.action.BOOT_COMPLETED 无法接收到广播无法接收到广播可能是以下几个原因造成的 未添加权限。 被系统自带或360手机助手等拦截,需在权限管理设置里放开开机自启动的权限。 应用安装到sd卡上,安装在sd卡上的应用是收不到BOOT_COMPLETED广播的。 系统开启了Fast Boot模式,这种模式下系统启动并不会发送BOOT_COMPLETED广播。 应用程序安装后重来没有启动过,这种情况下应用程序接收不到任何广播。 第一次安装无效,需重新启动 收不到广播,被系统拦截]]></content>
</entry>
<entry>
<title><![CDATA[byte[]字节数组转hex16进制字符串的三种方法]]></title>
<url>%2F2019%2F07%2F06%2Fbyte-%E5%AD%97%E8%8A%82%E6%95%B0%E7%BB%84%E8%BD%AChex16%E8%BF%9B%E5%88%B6%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E4%B8%89%E7%A7%8D%E6%96%B9%E6%B3%95%2F</url>
<content type="text"><![CDATA[方法1这种方法代码量是最少的,推荐 123private String bytesToHex(byte[] bytes) { String hex = new BigInteger(1, bytes).toString(16);} 方法21234567private String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02x", b)); } return sb.toString();} 方法31234567891011public String bytesToHex(byte[] bytes) { char[] hexDigits = {'0','1','2','3','4','5','6','7','8','9', 'a','b','c','d','e','f'}; // 一个字节对应两个16进制数,所以长度为字节数组乘2 char[] resultCharArray = new char[bytes.length * 2]; int index = 0; for (byte b : bytes) { resultCharArray[index++] = hexDigits[b>>>4 & 0xf]; resultCharArray[index++] = hexDigits[b & 0xf]; } return new String(resultCharArray); }]]></content>
</entry>
<entry>
<title><![CDATA[java String计算MD5的三种方法以及文件计算MD5的方法]]></title>
<url>%2F2019%2F07%2F06%2Fjava-String%E8%AE%A1%E7%AE%97MD5%E7%9A%84%E4%B8%89%E7%A7%8D%E6%96%B9%E6%B3%95%E4%BB%A5%E5%8F%8A%E6%96%87%E4%BB%B6%E8%AE%A1%E7%AE%97MD5%E7%9A%84%E6%96%B9%E6%B3%95%2F</url>
<content type="text"><![CDATA[MD5简介MD5,Message Digest Algorithm 5,是一种被广泛使用的信息摘要算法,可以将给定的任意长度数据通过一定的算法计算得出一个128位``二进制的散列值。 常见的表示方法是将128位二进制转成32位16进制,这样看起来比较简短。 方法1这种方法要注意一点的是不足32位高位需补零,否则会不足位,比如: 6531经MD5计算后正确的结果为0a7d83f084ec258aefd128569dda03d7 用方法1如果不高位补零返回的结果为a7d83f084ec258aefd128569dda03d7,前面的0少了 12345678910111213141516171819202122public static String MD51(String input) { if(input == null || input.length() == 0) { return null; } try { MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(input.getBytes()); byte[] byteArray = md5.digest(); BigInteger bigInt = new BigInteger(1, byteArray); // 参数16表示16进制 String result = bigInt.toString(16); // 不足32位高位补零 while(result.length() < 32) { result = "0" + result; } return result; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null;} 方法21234567891011121314151617181920212223public static String MD52(String input) { if(input == null || input.length() == 0) { return null; } try { MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(input.getBytes()); byte[] byteArray = md5.digest(); char[] hexDigits = {'0','1','2','3','4','5','6','7','8','9', 'a','b','c','d','e','f'}; // 一个字节对应两个16进制数,所以长度为字节数组乘2 char[] charArray = new char[byteArray.length * 2]; int index = 0; for (byte b : byteArray) { charArray[index++] = hexDigits[b>>>4 & 0xf]; charArray[index++] = hexDigits[b & 0xf]; } return new String(charArray); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null;} 方法31234567891011121314151617181920private static String MD53(String input) { if(input == null || input.length() == 0) { return null; } try { MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(input.getBytes()); byte[] byteArray = md5.digest(); StringBuilder sb = new StringBuilder(); for (byte b : byteArray) { // 一个byte格式化成两位的16进制,不足两位高位补零 sb.append(String.format("%02x", b)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null;} 计算文件的MD5值任何文件都可以计算MD5值,因为任何文件实际上就是字节数组 12345678910111213141516171819202122public static String fileToMD5(String path){ try { MessageDigest md5 = MessageDigest.getInstance("MD5"); FileInputStream fis = new FileInputStream(path); byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { md5.update(buffer, 0, len); } fis.close(); byte[] byteArray = md5.digest(); StringBuilder sb = new StringBuilder(); for (byte b : byteArray) { sb.append(String.format("%02x", b)); } return sb.toString(); } catch (IOException | NoSuchAlgorithmException e){ e.printStackTrace(); } return null;} 大写的MD5以上三种方法返回的MD5值包含的字母都是小写的,如果想返回大写的直接在方法返回前用toUpperCase就可以了,不再赘述 1234public static String MD5(String input) { // 省略计算MD5代码 return result.toUpperCase();}]]></content>
</entry>
<entry>
<title><![CDATA[java八进制、十进制、十六进制(hex)ASCII码字符串和String互转]]></title>
<url>%2F2019%2F07%2F06%2Fjava%E5%85%AB%E8%BF%9B%E5%88%B6%E3%80%81%E5%8D%81%E8%BF%9B%E5%88%B6%E3%80%81%E5%8D%81%E5%85%AD%E8%BF%9B%E5%88%B6%EF%BC%88hex%EF%BC%89ASCII%E7%A0%81%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%92%8CString%E4%BA%92%E8%BD%AC%2F</url>
<content type="text"><![CDATA[笔者在做Android串口开发的时候,与串口设备间的通信经常内容都是16进制的 发送命令的时候需要先把命令转成16进制的ASCII字符串。 接收到设备的反馈时需要把16进制的ASCII字符串转成对应的明文。 比如我们要发送的明文为ABCDEF,需要先转成对应的16进制ASCII码字符串414243444546 比如我们收到的反馈为16进制的ASCII码字符串313233343536,需要转成对应的明文123456 16进制ASCII码和单个字符char的互转我们先来了解单个字符char和ASCII码的互转 123456789101112// hex转char// 先将hex字符串转成intint i = Integer.parseInt("46", 16);// hex转char方法一,结果为FString str1 = new String(new char[]{(char)i});// hex转char方法二,结果为FString str2 = new StringBuffer().append((char)i).toString();// char转hex方法一,结果为46(第二个参数16表16进制)String hex1 = Integer.toString(c, 16);// char转hex方法二,结果为46String hex2 = Integer.toHexString('F'); ASCII码hex字符串转String明文代码很简单,就是每两个字符表示的16进制ASCII码解析成一个明文字符 123456789101112public static String hex2Str(String hex) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < hex.length() - 1; i += 2) { String h = hex.substring(i, (i + 2)); int decimal = Integer.parseInt(h, 16); sb.append((char) decimal); } return sb.toString();}// 输出结果为ABCDEFSystem.out.println(hex2Str("414243444546")); String明文转ASCII码hex字符串代码很简单,就是一个明文字符生成两个字符表示的16进制ASCII码 1234567891011121314public static String str2Hex(String str) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); // 这里的第二个参数16表示十六进制 sb.append(Integer.toString(c, 16)); // 或用toHexString方法直接转成16进制 // sb.append(Integer.toHexString(c)); } return sb.toString();}// 输出结果为414243444546System.out.println(str2Hex("ABCDEF")); 十进制ASCII码字符串和String明文互转10进制的转换和16进制的类似,只有细微的差别,直接看代码 10进制ASCII转String 12345678910111213public static String dec2Str(String ascii) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < ascii.length() - 1; i += 2) { String h = ascii.substring(i, (i + 2)); // 这里第二个参数传10表10进制 int decimal = Integer.parseInt(h, 10); sb.append((char) decimal); } return sb.toString();}// 结果为ABCDEFSystem.out.println(dec2Str("656667686970")); String转10进制ASCII 1234567891011121314public static String str2Dec(String str) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); // 第二个参数10表示10进制 sb.append(Integer.toString(c, 10)); // 或者省略第二个参数,默认为10进制 // sb.append(Integer.toString(c)); } return sb.toString();}// 结果为656667686970System.out.println(str2Dec("ABCDEF")); 八进制ASCII码字符串和String明文互转八进制ASCII码的转换也类似,主要要注意的地方是八进制的ASCII码占三位,而16进制和十进制表示法只占两位 8进制ASCII转String 1234567891011121314public static String oct2Str(String ascii) { StringBuilder sb = new StringBuilder(); // 这里这里循环的步进为3,因为8进制的ASCII码占3位 for (int i = 0; i < ascii.length() - 2; i += 3) { String h = ascii.substring(i, (i + 3)); // 第二个参数8表8进制 int decimal = Integer.parseInt(h, 8); sb.append((char) decimal); } return sb.toString();}// 结果为ABCDEFSystem.out.println(oct2Str("101102103104105106")); String转8进制ASCII 1234567891011121314public static String str2Oct(String str) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); // 这里的第二个参数8表8进制 sb.append(Integer.toString(c, 8)); // 或者直接用toOctalString方法转8进制 // sb.append(Integer.toOctalString(c)); } return sb.toString();}// 结果为101102103104105106System.out.println(str2Oct("ABCDEF"));]]></content>
</entry>
<entry>
<title><![CDATA[英文大写字母A~Z,小写字母a~z对应的ASCII码快速查询]]></title>
<url>%2F2019%2F07%2F06%2F%E8%8B%B1%E6%96%87%E5%A4%A7%E5%86%99%E5%AD%97%E6%AF%8DA-Z%EF%BC%8C%E5%B0%8F%E5%86%99%E5%AD%97%E6%AF%8Da-z%E5%AF%B9%E5%BA%94%E7%9A%84ASCII%E7%A0%81%E5%BF%AB%E9%80%9F%E6%9F%A5%E8%AF%A2%2F</url>
<content type="text"><![CDATA[在做开发的过程中经常会遇到用ASCII码表示的字母,或者字母要用ASCII码来表示。每次都要临时百度查ASCII表。 今天做个记录,以备下次用到。也方便大家查阅。 大写字母ASCII码 Bin(二进制) Oct(八进制) Dec(十进制) Hex(十六进制) 缩写/字符 解释 0100 0001 0101 65 0x41 A 大写字母A 0100 0010 0102 66 0x42 B 大写字母B 0100 0011 0103 67 0x43 C 大写字母C 0100 0100 0104 68 0x44 D 大写字母D 0100 0101 0105 69 0x45 E 大写字母E 0100 0110 0106 70 0x46 F 大写字母F 0100 0111 0107 71 0x47 G 大写字母G 0100 1000 0110 72 0x48 H 大写字母H 0100 1001 0111 73 0x49 I 大写字母I 01001010 0112 74 0x4A J 大写字母J 0100 1011 0113 75 0x4B K 大写字母K 0100 1100 0114 76 0x4C L 大写字母L 0100 1101 0115 77 0x4D M 大写字母M 0100 1110 0116 78 0x4E N 大写字母N 0100 1111 0117 79 0x4F O 大写字母O 0101 0000 0120 80 0x50 P 大写字母P 0101 0001 0121 81 0x51 Q 大写字母Q 0101 0010 0122 82 0x52 R 大写字母R 0101 0011 0123 83 0x53 S 大写字母S 0101 0100 0124 84 0x54 T 大写字母T 0101 0101 0125 85 0x55 U 大写字母U 0101 0110 0126 86 0x56 V 大写字母V 0101 0111 0127 87 0x57 W 大写字母W 0101 1000 0130 88 0x58 X 大写字母X 0101 1001 0131 89 0x59 Y 大写字母Y 0101 1010 0132 90 0x5A Z 大写字母Z 小写字母ASCII码 Bin(二进制) Oct(八进制) Dec(十进制) Hex(十六进制) 缩写/字符 解释 0110 0001 0141 97 0x61 a 小写字母a 0110 0010 0142 98 0x62 b 小写字母b 0110 0011 0143 99 0x63 c 小写字母c 0110 0100 0144 100 0x64 d 小写字母d 0110 0101 0145 101 0x65 e 小写字母e 0110 0110 0146 102 0x66 f 小写字母f 0110 0111 0147 103 0x67 g 小写字母g 0110 1000 0150 104 0x68 h 小写字母h 0110 1001 0151 105 0x69 i 小写字母i 0110 1010 0152 106 0x6A j 小写字母j 0110 1011 0153 107 0x6B k 小写字母k 0110 1100 0154 108 0x6C l 小写字母l 0110 1101 0155 109 0x6D m 小写字母m 0110 1110 0156 110 0x6E n 小写字母n 0110 1111 0157 111 0x6F o 小写字母o 0111 0000 0160 112 0x70 p 小写字母p 0111 0001 0161 113 0x71 q 小写字母q 0111 0010 0162 114 0x72 r 小写字母r 0111 0011 0163 115 0x73 s 小写字母s 0111 0100 0164 116 0x74 t 小写字母t 0111 0101 0165 117 0x75 u 小写字母u 0111 0110 0166 118 0x76 v 小写字母v 0111 0111 0167 119 0x77 w 小写字母w 0111 1000 0170 120 0x78 x 小写字母x 0111 1001 0171 121 0x79 y 小写字母y 0111 1010 0172 122 0x7A z 小写字母z 总结ASCII码大小写是不一样的,所以所有大小写字母总共对应52个ASCII码,有人会觉得52个这么多怎么记得住。其实也没必要记住,每次用到再临时查也很快。但总结下规律要记住也不是那么难。 大小写字母ASCII码是不一样的,比如大写字母A的ASCII码是65,小写字母a的ASCII码是97。 同样字母,大写字母的ASCII码值比小写字母的ASCII码值小,比如大写字母A的ASCII码值65比小写字母a的ASCII码值97小。 大写字母AZ的ASCII码值从6590,其实只要记住第一个A的ASCII码值65,后面的字母的ASCII码值累加上去就行了。 小写字母az的ASCII码值从97122,其实只要记住第一个a的ASCII码值97,后面的字母的ASCII码值累加上去就行了。 同样的字母,小写字母的ASCII码值比大写字母的ASCII码值大32,小写字母ASCII = 大写子ASCII + 32]]></content>
</entry>
<entry>
<title><![CDATA[数字0123456789对应的ASCII码值]]></title>
<url>%2F2019%2F07%2F06%2F%E6%95%B0%E5%AD%970123456789%E5%AF%B9%E5%BA%94%E7%9A%84ASCII%E7%A0%81%E5%80%BC%2F</url>
<content type="text"><![CDATA[做开发的时候经常会遇到需要用ASCII码来表示数字,或者由ASCII码查找对应的数字 每个数字对应一个ASCII码值,也就十个值,但是记不住,每次都要百度查表 这里做个记录以备下次用到,也方便大家查询 Bin(二进制) Oct(八进制) Dec(十进制) Hex(十六进制) 缩写/字符 解释 0011 0000 060 48 0x30 0 字符0 0011 0001 061 49 0x31 1 字符1 0011 0010 062 50 0x32 2 字符2 0011 0011 063 51 0x33 3 字符3 0011 0100 064 52 0x34 4 字符4 0011 0101 065 53 0x35 5 字符5 0011 0110 066 54 0x36 6 字符6 0011 0111 067 55 0x37 7 字符7 0011 1000 070 56 0x38 8 字符8 0011 1001 071 57 0x39 9 字符9 数字转ASCII码换算这里教大家一个小技巧,其实没必要记住10个数字对应的ASCII码。由于十个数字对应的ASCII码值是连续的,所以只要记住第一个值,后面的就知道了。 我们只需要记住数字0对应的ASCII码为48(十进制)就可以了,其余数字的ASCII码累加上去就行了。 记住一个简单的公式 x的ASCII码 = 48 + x,你想知道某个数字的ASCII码,直接用48加上这个数字值就对了 比如: 1的ASCII码 = 48 + 1 = 49 2的ASCII码 = 48 + 2 = 50 3的ASCII码 = 48 + 3 = 51 …… 9的ASCII码 = 48 + 9 = 57 如果需要知道其他进制的ASCII码的表示,直接用十进制ASCII码值换算下就可以了 ASCII码转数字换算同理,根据上面数字转ASCII码的公式 ASCII = 48 + x 可知,x = ASCII - 48 我们可以根据ASCII码值计算出所表示的数字,直接用该ASCII码值减去48所得结果就是表示的数字 比如: 49表示的数字 = 49 - 48 = 1 50表示的数字 = 50 - 48 = 1 51表示的数字 = 51 - 48 = 1 … 57表示的数字 = 57 - 48 = 9 是不是很简单呢,说了这么多其实你需要记住的就一个值48]]></content>
</entry>
<entry>
<title><![CDATA[换行、回车、空格等常用的ASCII码值]]></title>
<url>%2F2019%2F07%2F06%2F%E6%8D%A2%E8%A1%8C%E3%80%81%E5%9B%9E%E8%BD%A6%E3%80%81%E7%A9%BA%E6%A0%BC%E7%AD%89%E5%B8%B8%E7%94%A8%E7%9A%84ASCII%E7%A0%81%E5%80%BC%2F</url>
<content type="text"><![CDATA[换行符的ASCII码值为10,十六进制表示为0x0A 回车符的ASCII码值为13,十六进制表示为0x0D 空格符的ASCII码值为32,十六进制表示为0x20 以下列出其他一些常用到的符号的ASCII码 二进制 八进制 十进制 十六进制 缩写/字符 解释 0000 0000 00 0 0x00 NUL(null) 空字符 0000 1000 010 8 0x08 BS (backspace) 退格 0000 1001 011 9 0x09 HT (horizontal tab) 水平制表符 0000 1010 012 10 0x0A LF (NL line feed, new line) 换行键 0000 1011 013 11 0x0B VT (vertical tab) 垂直制表符 0000 1100 014 12 0x0C FF (NP form feed, new page) 换页键 0000 1101 015 13 0x0D CR (carriage return) 回车键 0001 1000 030 24 0x18 CAN (cancel) 取消 0010 0000 040 32 0x20 (space) 空格 完整的ASCII码表如果你想查看完整的ASCII码表,可以参考这篇文章 最全的ASCII码表,EASCII码表,ISO/IEC 8859码表二进制、八进制、十进制、十六进制快速查询]]></content>
</entry>
<entry>
<title><![CDATA[最全的ASCII码表,EASCII码表,ISO/IEC 8859码表二进制、八进制、十进制、十六进制快速查询]]></title>
<url>%2F2019%2F07%2F06%2F%E6%9C%80%E5%85%A8%E7%9A%84ASCII%E7%A0%81%E8%A1%A8%EF%BC%8CEASCII%E7%A0%81%E8%A1%A8%EF%BC%8CISO-IEC-8859%E7%A0%81%E8%A1%A8%E4%BA%8C%E8%BF%9B%E5%88%B6%E3%80%81%E5%85%AB%E8%BF%9B%E5%88%B6%E3%80%81%E5%8D%81%E8%BF%9B%E5%88%B6%E3%80%81%E5%8D%81%E5%85%AD%E8%BF%9B%E5%88%B6%E5%BF%AB%E9%80%9F%E6%9F%A5%E8%AF%A2%2F</url>
<content type="text"><![CDATA[查询ASCII码只有128个字符,在本页面直接按Ctrl+F输入你要查找的字符查询对应的ASCII吗,或者输入ASCII值(二进制、八进制、十进制、十六进制)查找对应的字符。 简介ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套计算机编码系统。它主要用于显示现代英语,而其扩展版本EASCII则可以部分支持其他西欧语言,并等同于国际标准ISO/IEC 646。 ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,至今为止共定义了128个字符;其中33个字符无法显示(一些终端提供了扩展,使得这些字符可显示为诸如笑脸、扑克牌花式等8-bit符号),且这33个字符多数都已是陈废的控制字符。控制字符的用途主要是用来操控已经处理过的文字。在33个字符之外的是95个可显示的字符。用键盘敲下空白键所产生的空白字符也算1个可显示字符(显示为空白)。 规律由ASCII码表我们可以做如下总结(以下说明均以10进制表示法): 共定义了128个字符,其中33控制字符个字符无法显示,剩余95个可显示字符 控制字符包含0~31的32个字符,加上一个删除符(127),共33个 可显示字符包含32~126的95个字符(包含数字、大小写字符、英文标点符号等) 数字19对应的ASCII码为4857 大写字母AZ对应的ASCII码为6590 小写字母az对应的ASCII码为97122 ASCII码表 Bin(二进制) Oct(八进制) Dec(十进制) Hex(十六进制) 缩写/字符 解释 0000 0000 00 0 0x00 NUL(null) 空字符 0000 0001 01 1 0x01 SOH(start of headline) 标题开始 0000 0010 02 2 0x02 STX (start of text) 正文开始 0000 0011 03 3 0x03 ETX (end of text) 正文结束 0000 0100 04 4 0x04 EOT (end of transmission) 传输结束 0000 0101 05 5 0x05 ENQ (enquiry) 请求 0000 0110 06 6 0x06 ACK (acknowledge) 收到通知 0000 0111 07 7 0x07 BEL (bell) 响铃 0000 1000 010 8 0x08 BS (backspace) 退格 0000 1001 011 9 0x09 HT (horizontal tab) 水平制表符 0000 1010 012 10 0x0A LF (NL line feed, new line) 换行键 0000 1011 013 11 0x0B VT (vertical tab) 垂直制表符 0000 1100 014 12 0x0C FF (NP form feed, new page) 换页键 0000 1101 015 13 0x0D CR (carriage return) 回车键 0000 1110 016 14 0x0E SO (shift out) 不用切换 0000 1111 017 15 0x0F SI (shift in) 启用切换 0001 0000 020 16 0x10 DLE (data link escape) 数据链路转义 0001 0001 021 17 0x11 DC1 (device control 1) 设备控制1 0001 0010 022 18 0x12 DC2 (device control 2) 设备控制2 0001 0011 023 19 0x13 DC3 (device control 3) 设备控制3 0001 0100 024 20 0x14 DC4 (device control 4) 设备控制4 0001 0101 025 21 0x15 NAK (negative acknowledge) 拒绝接收 0001 0110 026 22 0x16 SYN (synchronous idle) 同步空闲 0001 0111 027 23 0x17 ETB (end of trans. block) 结束传输块 0001 1000 030 24 0x18 CAN (cancel) 取消 0001 1001 031 25 0x19 EM (end of medium) 媒介结束 0001 1010 032 26 0x1A SUB (substitute) 代替 0001 1011 033 27 0x1B ESC (escape) 换码(溢出) 0001 1100 034 28 0x1C FS (file separator) 文件分隔符 0001 1101 035 29 0x1D GS (group separator) 分组符 0001 1110 036 30 0x1E RS (record separator) 记录分隔符 0001 1111 037 31 0x1F US (unit separator) 单元分隔符 0010 0000 040 32 0x20 (space) 空格 0010 0001 041 33 0x21 ! 叹号 0010 0010 042 34 0x22 “ 双引号 0010 0011 043 35 0x23 # 井号 0010 0100 044 36 0x24 $ 美元符 0010 0101 045 37 0x25 % 百分号 0010 0110 046 38 0x26 & 和号 0010 0111 047 39 0x27 ‘ 闭单引号 0010 1000 050 40 0x28 ( 开括号 0010 1001 051 41 0x29 ) 闭括号 0010 1010 052 42 0x2A * 星号 0010 1011 053 43 0x2B + 加号 0010 1100 054 44 0x2C , 逗号 0010 1101 055 45 0x2D - 减号/破折号 0010 1110 056 46 0x2E . 句号 0010 1111 057 47 0x2F / 斜杠 0011 0000 060 48 0x30 0 字符0 0011 0001 061 49 0x31 1 字符1 0011 0010 062 50 0x32 2 字符2 0011 0011 063 51 0x33 3 字符3 0011 0100 064 52 0x34 4 字符4 0011 0101 065 53 0x35 5 字符5 0011 0110 066 54 0x36 6 字符6 0011 0111 067 55 0x37 7 字符7 0011 1000 070 56 0x38 8 字符8 0011 1001 071 57 0x39 9 字符9 0011 1010 072 58 0x3A : 冒号 0011 1011 073 59 0x3B ; 分号 0011 1100 074 60 0x3C < 小于 0011 1101 075 61 0x3D = 等号 0011 1110 076 62 0x3E > 大于 0011 1111 077 63 0x3F ? 问号 0100 0000 0100 64 0x40 @ 电子邮件符号 0100 0001 0101 65 0x41 A 大写字母A 0100 0010 0102 66 0x42 B 大写字母B 0100 0011 0103 67 0x43 C 大写字母C 0100 0100 0104 68 0x44 D 大写字母D 0100 0101 0105 69 0x45 E 大写字母E 0100 0110 0106 70 0x46 F 大写字母F 0100 0111 0107 71 0x47 G 大写字母G 0100 1000 0110 72 0x48 H 大写字母H 0100 1001 0111 73 0x49 I 大写字母I 01001010 0112 74 0x4A J 大写字母J 0100 1011 0113 75 0x4B K 大写字母K 0100 1100 0114 76 0x4C L 大写字母L 0100 1101 0115 77 0x4D M 大写字母M 0100 1110 0116 78 0x4E N 大写字母N 0100 1111 0117 79 0x4F O 大写字母O 0101 0000 0120 80 0x50 P 大写字母P 0101 0001 0121 81 0x51 Q 大写字母Q 0101 0010 0122 82 0x52 R 大写字母R 0101 0011 0123 83 0x53 S 大写字母S 0101 0100 0124 84 0x54 T 大写字母T 0101 0101 0125 85 0x55 U 大写字母U 0101 0110 0126 86 0x56 V 大写字母V 0101 0111 0127 87 0x57 W 大写字母W 0101 1000 0130 88 0x58 X 大写字母X 0101 1001 0131 89 0x59 Y 大写字母Y 0101 1010 0132 90 0x5A Z 大写字母Z 0101 1011 0133 91 0x5B [ 开方括号 0101 1100 0134 92 0x5C \ 反斜杠 0101 1101 0135 93 0x5D ] 闭方括号 0101 1110 0136 94 0x5E ^ 脱字符 0101 1111 0137 95 0x5F _ 下划线 0110 0000 0140 96 0x60 ` 开单引号 0110 0001 0141 97 0x61 a 小写字母a 0110 0010 0142 98 0x62 b 小写字母b 0110 0011 0143 99 0x63 c 小写字母c 0110 0100 0144 100 0x64 d 小写字母d 0110 0101 0145 101 0x65 e 小写字母e 0110 0110 0146 102 0x66 f 小写字母f 0110 0111 0147 103 0x67 g 小写字母g 0110 1000 0150 104 0x68 h 小写字母h 0110 1001 0151 105 0x69 i 小写字母i 0110 1010 0152 106 0x6A j 小写字母j 0110 1011 0153 107 0x6B k 小写字母k 0110 1100 0154 108 0x6C l 小写字母l 0110 1101 0155 109 0x6D m 小写字母m 0110 1110 0156 110 0x6E n 小写字母n 0110 1111 0157 111 0x6F o 小写字母o 0111 0000 0160 112 0x70 p 小写字母p 0111 0001 0161 113 0x71 q 小写字母q 0111 0010 0162 114 0x72 r 小写字母r 0111 0011 0163 115 0x73 s 小写字母s 0111 0100 0164 116 0x74 t 小写字母t 0111 0101 0165 117 0x75 u 小写字母u 0111 0110 0166 118 0x76 v 小写字母v 0111 0111 0167 119 0x77 w 小写字母w 0111 1000 0170 120 0x78 x 小写字母x 0111 1001 0171 121 0x79 y 小写字母y 0111 1010 0172 122 0x7A z 小写字母z 0111 1011 0173 123 0x7B { 开花括号 0111 1100 0174 124 0x7C | 垂线 0111 1101 0175 125 0x7D } 闭花括号 0111 1110 0176 126 0x7E ~ 波浪号 0111 1111 0177 127 0x7F DEL (delete) 删除 ASCII码缺点ASCII的局限在于只能显示26个基本拉丁字母、阿拉伯数目字和英式标点符号,因此只能用于显示现代美国英语(而且在处理英语当中,即使会违反拼写规则,外来词如naïve、café、élite等等时,所有重音符号都必须去掉)。虽然EASCII解决了部分西欧语言的显示问题,但对更多其他语言依然无能为力。因此有其他的扩展方案,如EASCII和ISO/IEC 8859,现在的软件系统大多采用Unicode。 EASCIIEASCII(Extended ASCII,扩展美国标准信息交换码)是将ASCII码由7位二进制扩充为8位而成。EASCII码比ASCII码扩充出来的符号包括表格符号、计算符号、希腊字母和特殊的拉丁符号。 原来的ASCII码二进制表示范围为0000 0000~0111 1111(共128个字符),可以看到高八位都是0,是没有用到的。 而新增的EASCII二进制表示范围为1000 00001111 1111(十进制为129255,共128个字符),可以看到高八位都是1。 ISO/IEC 8859不过,EASCII码目前已经很少使用,常用的是ISO/IEC 8859字符编码方案。该方案与EASCII码类似,也同样是在ASCII码的基础上,利用了ASCII的7位编码所没有用到的最高位(首位),将编码范围从原先ASCII码的0x000x7F(十进制为0127),扩展到了0x800xFF(十进制为128255)。 ISO/IEC 8859字符编码方案所扩展的这128个编码中,实际上只有0xA00xFF(十进制为160255)被实际使用。也就是说,只有0xA00xFF(十进制为160255)这96个编码定义了字符,而0x800x9F(十进制为128159)这32个编码并未定义字符。 ISO/IEC 8859字符编码方案同样是单字节编码方案,也同样完全兼容ASCII。 符号 十六进制 十进制 表示方法 名称 00A0 160 NBSP 不换行空格 ¡ 00A1 161 ¡ 倒感叹号 ¢ 00A2 162 ¢ 英分 £ 00A3 163 £ 英镑 ¤ 00A4 164 ¤ 货币记号 ¥ 00A5 165 ¥ 人民币/日元 ¦ 00A6 166 ¦ 断竖线 § 00A7 167 § 小节符 ¨ 00A8 168 ¨ 分音符(元音变音) © 00A9 169 © 著作权符 ª 00AA 170 ª 阴性序数记号 « 00AB 171 « 左指双尖引号 ¬ 00AC 172 ¬ 非标记 00AD 173 SHY 选择性连接号 ® 00AE 174 ® 注册商标 ¯ 00AF 175 ¯ 长音符 ° 00B0 176 ° 度 ± 00B1 177 ± 正负号 ² 00B2 178 ² 二次方号 ³ 00B3 179 ³ 三次方号 ´ 00B4 180 ´ 锐音符 µ 00B5 181 µ 微符 ¶ 00B6 182 ¶ 段落标记 · 00B7 183 · 中心点 ¸ 00B8 184 ¸ 软音符 ¹ 00B9 185 ¹ 一次方号 º 00BA 186 º 阳性序数记号 » 00BB 187 » 右指双尖引号 ¼ 00BC 188 ¼ 四分之一 ½ 00BD 189 ½ 二分之一 ¾ 00BE 190 ¾ 四分之三 ¿ 00BF 191 ¿ 竖翻问号 À 00C0 192 À 带抑音符的A Á 00C1 193 Á 带锐音符的A Â 00C2 194 Â 带扬抑符的A Ã 00C3 195 Ã 带颚化符的A Ä 00C4 196 Ä 带分音符的A Å 00C5 197 Å 带上圆圈的A Æ 00C6 198 Æ 大写连字AE Ç 00C7 199 Ç 带下加符的C È 00C8 200 È 带抑音符的E É 00C9 201 É 带锐音符的E Ê 00CA 202 Ê 带扬抑符的E Ë 00CB 203 Ë 带分音符的E Ì 00CC 204 Ì 带抑音符的I Í 00CD 205 Í 带锐音符的I Î 00CE 206 Î 带扬抑符的I Ï 00CF 207 Ï 带分音符的I Ð 00D0 208 Ð 带横线符的D Ñ 00D1 209 Ñ 带颚化符的N Ò 00D2 210 Ò 带抑音符的O Ó 00D3 211 Ó 带锐音符的O Ô 00D4 212 Ô 带扬抑符的O Õ 00D5 213 Õ 带颚化符的O Ö 00D6 214 Ö 带分音符的O × 00D7 215 × 乘号 Ø 00D8 216 Ø 带斜线的O Ù 00D9 217 Ù 带抑音符的U Ú 00DA 218 Ú 带锐音符的U Û 00DB 219 Û 带扬抑符的U Ü 00DC 220 Ü 带分音符的U Ý 00DD 221 Ý 带锐音符的Y Þ 00DE 222 Þ 清音p ß 00DF 223 ß 清音s à 00E0 224 à 带抑音符的a á 00E1 225 á 带锐音符的a â 00E2 226 â 带扬抑符的a ã 00E3 227 ã 带颚化符的a ä 00E4 228 ä 带分音符的a å 00E5 229 å 带分音符的a æ 00E6 230 æ 小写连字AE ç 00E7 231 ç 带下加符的c è 00E8 232 è 带抑音符的e é 00E9 233 é 带锐音符的e ê 00EA 234 ê 带扬抑符的e ë 00EB 235 ë 带分音符的e ì 00EC 236 ì 带抑音符的i í 00ED 237 í 带锐音符的i î 00EE 238 î 带扬抑符的i ï 00EF 239 ï 带分音符的i ð 00F0 240 ð 带斜线的d ñ 00F1 241 ñ 带颚化符的n ò 00F2 242 ò 带抑音符的o ó 00F3 243 ó 带锐音符的o ô 00F4 244 ô 带扬抑符的o õ 00F5 245 õ 带颚化符的o ö 00F6 246 ö 带分音符的o ÷ 00F7 247 ÷ 除号 ø 00F8 248 ø 带斜线的o ù 00F9 249 ù 带抑音符的u ú 00FA 250 ú 带锐音符的u û 00FB 251 û 带扬抑符的u ü 00FC 252 ü 带分音符的u ý 00FD 253 ý 带锐音符的y þ 00FE 254 þ 小写字母Thorn ÿ 00FF 255 ÿ 带分音符的y]]></content>
</entry>
<entry>
<title><![CDATA[Android TextView Autosizing 字号自动调整大小,字号自适应]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-TextView-Autosizing-%E5%AD%97%E5%8F%B7%E8%87%AA%E5%8A%A8%E8%B0%83%E6%95%B4%E5%A4%A7%E5%B0%8F%EF%BC%8C%E5%AD%97%E5%8F%B7%E8%87%AA%E9%80%82%E5%BA%94%2F</url>
<content type="text"><![CDATA[什么是Autosizeng呢,简单说就是TextView文本内容的字号大小是会根据内容多少而变大或者变小以适应布局,尽可能让TextView显示所有的文本内容。 比如TextView控件的宽高是固定的,在内容越少的情况下,为了内容填充整个控件,字号就会变大,在内容越多的情况下,为了显示更多的内容,字号就会缩小。如下图所示: 在Android 8.0(API级别26)及更高版本中,可以直接使用Autosizeng,如果想在低版本中使用,可以使用Support V4包中的TextViewCompat,可以向下兼容到Android 4.0(API级别14) 实现Autosizing功能可以在代码中设置也可以在XML中设置属性。 有三种方法可以设置Autosizing 默认设置 设置变化范围及粒度 预设可选值我们下面分别详细介绍 如果要达到Autosizing的效果,建议不要为layout_width和layout_height属性设置wrap_content的值,而是要设置一个固定的值,否则可能达不到你要的效果 使用默认设置默认设置我们不用设置Autosizing TextView字号的最小值和最大值,默认minTextSize = 12sp, maxTextSize = 112sp以及 granularity = 1px,granularity表示粒度,就是每次递增或减小的大小。 12345678910111213141516// 开启AutosizingmTextView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM);// 关闭AutosizingmTextView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_NONE);<!--开启Autosizing--><TextView android:layout_width="match_parent" android:layout_height="200dp" android:autoSizeTextType="uniform" /><!--关闭Autosizing--><TextView android:layout_width="match_parent" android:layout_height="200dp" android:autoSizeTextType="none" /> 以上只实适用Android 8.0及以上版本中,如果要兼容低版本,需使用Support包,如下所示: 12345678910111213141516// 开启AutosizingTextViewCompat.setAutoSizeTextTypeWithDefaults(mTextView, TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);// 关闭AutosizingTextViewCompat.setAutoSizeTextTypeWithDefaults(mTextView, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);<!--开启Autosizing--><TextView android:layout_width="match_parent" android:layout_height="200dp" app:autoSizeTextType="uniform" /><!--关闭Autosizing--><TextView android:layout_width="match_parent" android:layout_height="200dp" app:autoSizeTextType="none" /> 设置变化范围及粒度默认值可能不满足我们的要求,那我们可以自定义变化的区间,设置一个最大值和最小值,还有变化的粒度。 在Android 8.0及更高版本中使用如下 123456789101112// 在代码中设置mTextView.setAutoSizeTextTypeUniformWithConfiguration(8, 22, TypedValue.COMPLEX_UNIT_SP);// 在XML中设置<?xml version="1.0" encoding="utf-8"?><TextView android:layout_width="match_parent" android:layout_height="200dp" android:autoSizeTextType="uniform" android:autoSizeMinTextSize="12sp" android:autoSizeMaxTextSize="100sp" android:autoSizeStepGranularity="2sp" /> support包的使用如下 123456789101112131415161718// 在代码中设置TextViewCompat.setAutoSizeTextTypeWithDefaults(mTextView, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);// 在XML中设置<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="200dp" app:autoSizeTextType="uniform" app:autoSizeMinTextSize="12sp" app:autoSizeMaxTextSize="100sp" app:autoSizeStepGranularity="2sp" /></LinearLayout> 预设可选值你也可以指定一些字号,Autosizing缩放时只会在这几种字号里变化。 在Android 8.0及更高版本中使用如下 1234567891011121314151617181920// 在代码中设置mTextView.setAutoSizeTextTypeUniformWithPresetSizes(new int[]{8, 10, 16, 25}, TypedValue.COMPLEX_UNIT_SP);// 在XML中设置<resources> <array name="autosize_text_sizes"> <item>10sp</item> <item>12sp</item> <item>20sp</item> <item>40sp</item> <item>100sp</item> </array></resources><?xml version="1.0" encoding="utf-8"?><TextView android:layout_width="match_parent" android:layout_height="200dp" android:autoSizeTextType="uniform" android:autoSizePresetSizes="@array/autosize_text_sizes" /> support包设置预设可选值 123456789101112131415161718192021222324252627// 在代码中设置TextViewCompat.setAutoSizeTextTypeUniformWithPresetSizes(mTextView, new int[]{8, 10, 16, 25}, TypedValue.COMPLEX_UNIT_SP);// 在XML中设置<resources> <array name="autosize_text_sizes"> <item>10sp</item> <item>12sp</item> <item>20sp</item> <item>40sp</item> <item>100sp</item> </array></resources><?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="200dp" app:autoSizeTextType="uniform" app:autoSizePresetSizes="@array/autosize_text_sizes" /></LinearLayout> 参考 Autosizing TextViews Android官网教程 文字太多?控件太小?试试 TextView 的新特性 Autosizeing 吧! 如果喜欢我的文章可以关注下我的公号,里面有很多干货哦]]></content>
</entry>
<entry>
<title><![CDATA[Android预定义样式?android:attr/attribute、?attr/attribute和?attribute]]></title>
<url>%2F2019%2F07%2F06%2FAndroid%E9%A2%84%E5%AE%9A%E4%B9%89%E6%A0%B7%E5%BC%8F-android-attr-attribute%E3%80%81-attr-attribute%E5%92%8C-attribute%2F</url>
<content type="text"><![CDATA[系统内建属性比如系统有一个内建属性selectableItemBackground,可以为可点击控件设置点击时的水波纹效果,以下几种方法都可以成功引用 1234567891011121314151617181920212223// 未设置前<Button /> // 设置水波纹点击效果<Button 、、、 android:foreground="?android:attr/selectableItemBackground"/> // 省略attr/<Button 、、、 android:foreground="?android:selectableItemBackground"/> // 省略android:<Button 、、、 android:foreground="?attr/selectableItemBackground"/> // 省略android:attr/<Button 、、、 android:foreground="?selectableItemBackground"/> 自定义属性我们先在attrs.xml文件中自定义一个属性 12345<?xml version="1.0" encoding="utf-8"?><resources> // 通过format可以设置各种不同的类型,包括style,这里不再赘述 <attr name="myTextColor" format="color"/></resources> 此时如果直接引用是没有效果的,而且会报错 123<Button 、、、 android:textColor="?attr/myTextColor" 我们还需要在styles.xml文件中为这个自定义属性设置一个默认值,然后再引用就可以成功了,这里我们把Button的文字设置成红色 12345<resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="myTextColor">#ff0000</item> </style></resources> 当然,和内建属性的引用一样,我们也可以省略掉attr/ 1234// 省略掉attr/也是可以引用成功的<Button 、、、 android:textColor="?myTextColor" 总结 Android内建属性引用前面可以加android:,但也可以省略,自定义属性则不可以加 属性的引用最短可以简化成?attribute,建议该写法比较简短 属性引用引用的其实不是引用属性,而是引用该属性设置的默认值,所以自定义属性记得在主题中设置引用属性的默认值,当人也可以再主题中更改系统内建属性的默认值 更换主题参考 Android Theme主题样式一键换肤 Android应用动态修改主题 有一个想法,可以通过在不同的主题中定义同一个自定义属性的不同默认值,然后通过setTheme();方法来切换主题,马上验证下 首先在attrs.xml文件中自定义一个属性 12345<?xml version="1.0" encoding="utf-8"?><resources> // 通过format可以设置各种不同的类型,包括style,这里不再赘述 <attr name="myTextColor" format="color"/></resources> 然后在styles.xml中定义两个不同的theme 1234567<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="myTextColor">#ff0000</item></style><style name="AppThemeGreen" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="myTextColor">#00ff00</item></style> 本来想通过Application.setTheme();方法来设置主题,但发现未生效,具体原因还未去深究,现在只能通过调用Activity.setTheme();方法来设置出题,且必须在setContentView();方法前调用,否则无法生效,可以将Activity.setTheme();调用写在基类BaseActivity的onCreate方法里。 123456789101112public class MainActivity extends Activity { private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setTheme(R.style.AppTheme); // 可以有一个换肤的页面供选择,然后把选择的选择的theme保存到Sharepreference中 setTheme(R.style.AppThemeGreen); setContentView(R.layout.activity_main);} 总结: Application.setTheme()设置主题无法生效 Activity.setTheme()设置主题需在setContentView之前调用才能生效 缺点:1.假设有个换肤选择的Activity页面,选择后无法实时生效,需重启Activity。就是说已打开的Activity是无法马上看到效果的,新打开的Activity才能够看到换肤效果。2.该换肤实现方式的所有主题需写死在styles.xml文件内,无法实现后台下载新皮肤。至于下载新皮肤网上有通过apk文件的方式,以后可以深入研究下。 参考 @android, ?attr/ 和 ?android 的区别 Android中 @和?区别以及?attr/与@style/等的区别 如果喜欢我的文章,可以扫描以下二维码关注我的微信公众号,我会定期发布最新的文章。]]></content>
</entry>
<entry>
<title><![CDATA[ViewPager通过PageTransformer实现切换时背景色的渐变]]></title>
<url>%2F2019%2F07%2F06%2FViewPager%E9%80%9A%E8%BF%87PageTransformer%E5%AE%9E%E7%8E%B0%E5%88%87%E6%8D%A2%E6%97%B6%E8%83%8C%E6%99%AF%E8%89%B2%E7%9A%84%E6%B8%90%E5%8F%98%2F</url>
<content type="text"><![CDATA[定个小目标我们要实现如下的效果,ViewPager正中的page显示为绿色,其余两边的显示为蓝色。中间page向左右两边切换时会从绿色慢慢过渡到蓝色,同理两边的page向中间切换时颜色也会慢慢过渡,而不是直接跳变。 PageTransformer为了实现以上效果我们要借助PagerTransfromer,所以有必要简单介绍下 1234567891011121314151617181920/** * A PageTransformer is invoked whenever a visible/attached page is scrolled. * This offers an opportunity for the application to apply a custom transformation * to the page views using animation properties. * * <p>As property animation is only supported as of Android 3.0 and forward, * setting a PageTransformer on a ViewPager on earlier platform versions will * be ignored.</p> */public interface PageTransformer { /** * Apply a property transformation to the given page. * * @param page Apply the transformation to this page * @param position Position of page relative to the current front-and-center * position of the pager. 0 is front and center. 1 is one full * page position to the right, and -1 is one page position to the left. */ void transformPage(View page, float position);} PageTransformer很简单,就只有一个transformPage方法。第一个参数是我们要变换颜色的page。 重点看第二个参数,英语好的话直接看注释说得很明白,意思就是我们当前要变化的页面当前位置的中心距离pager中心的距离,比如当前处于中心的page的position就是0,右边第一个page的position=1,右边第二个page的position为2,左边第一个page的position=-1,左边第二个page的position=-2,以此类推。 当然position的值不可能一直是整数,比如当处于中间位置的page向右边切换到右边第一个位置时,position的值会从0变化到1,1就是表示一个page的宽度,从中间位置移到右边第一个位置就是移动了一个page的宽度。如果向右移动到一半,那么position就是0.5。 实现的思路只有中间位置是绿色,其余两边的都是蓝色,所以可以确定的是position=0,肯定是绿色,position的绝对值Math.abs(position)>=1一定是蓝色,剩下的我们就是要处理-1<position<1这些位置的渐变色,也就是中间位置到左右两边第一个page的位置。 中间位置到左右两边第一个page的位置的position取绝对值的值范围从0-1,其实就是0%-100%,我们可以用这个值当做颜色的变化率,0就是绿色,值越大表示越往蓝色变化,1就表示变成蓝色了。 另一个难点就是如何实现颜色的渐变。我们知道颜色由RGB表示,有三个颜色的分量分别表示红绿蓝。我们把起始颜色和最终颜色的三个颜色分量分别取出来,分别计算三个颜色分量的差值,最后用差值乘以变化率再加上初始颜色分量就是当前的颜色分量值,把三个当前颜色分量的值拼起来就是当前的颜色值。 初始颜色:#00ff00最终颜色:#0000ff初始颜色分量:红00,绿ff(255),蓝00最终颜色分量:红00,绿00,蓝ff(255)颜色分量差值:红00,绿-255,蓝255 假设我们要计算position=0.5位置的颜色当前红色分量=0+0.500=0=0x00当前绿色分量=255+0.5(-255)=127=0x7f当前蓝色分量=0+0.5*255=127=0x7f 所以position=0.5时当前的颜色为#007f7f。 代码实现一个很简单的功能说了这么多,就是为了让大家更好的理解,如果直接放代码看了坑能会一头雾水 设置PagerTransfromer 12345678910111213mViewPager.setPageTransformer(true, new ViewPager.PageTransformer() { @Override public void transformPage(@NonNull View view, float v) { // 取绝对值 v = Math.abs(v); // 超过左右两边第一个位置的page都设置成蓝色 if (v > 1) { v = 1; } String color = transColor("#00ff00", "#0000ff", v); view.setBackgroundColor(Color.parseColor(color)); }}); 计算当前颜色值 1234567891011121314151617181920212223242526272829303132333435/** * 根据起始颜色、最终颜色和变化率计算当前颜色 * @param start 起始颜色 * @param end 最终颜色 * @param rate 变化率 * @return 当前颜色 */private static String transColor(String start, String end, float rate) { // 获取初始颜色的RGB分量 String startR = start.substring(1, 3); String startG = start.substring(3, 5); String startB = start.substring(5, 7); // 获取最终颜色的颜色分量 String endR = end.substring(1, 3); String endG = end.substring(3, 5); String endB = end.substring(5, 7); // 计算初始颜色的RGB分量十进制值 int startRI = Integer.parseInt(startR, 16); int startGI = Integer.parseInt(startG, 16); int startBI = Integer.parseInt(startB, 16); // 计算最终颜色的RGB分量十进制值 int endRI = Integer.parseInt(endR, 16); int endGI = Integer.parseInt(endG, 16); int endBI = Integer.parseInt(endB, 16); // 计算当前颜色分量 int curRI = (int) (startRI + (endRI - startRI)*rate); int curGI = (int) (startGI + (endGI - startGI)*rate); int curBI = (int) (startBI + (endBI - startBI)*rate); // 转成16进制 String curR = String.format("%02x", curRI); String curG = String.format("%02x", curGI); String curB = String.format("%02x", curBI); // 拼成颜色码 return "#" + curR + curG + curB;} 如果喜欢我的文章,可以扫描以下二维码关注我的微信公众号,我会定期发布最新的文章。关注我的微信,回复背景色渐变获取本文的完整代码。]]></content>
</entry>
<entry>
<title><![CDATA[Base64加密解密]]></title>
<url>%2F2019%2F07%2F06%2FBase64%E5%8A%A0%E5%AF%86%E8%A7%A3%E5%AF%86%2F</url>
<content type="text"><![CDATA[参考Java8 Base64BASE64在线加密解密 Android自带Base64加密1234import android.util.Base64;String encode2 = new String(Base64.encode("123456".getBytes(), Base64.DEFAULT)); // 结果为"MTIzNDU2\n"// encodeToString内部其实也是调用encode方法,若想直接返回String,推荐用该方法String encode1 = Base64.encodeToString("123456".getBytes(), Base64.DEFAULT); // 结果为"MTIzNDU2\n" 这里要注意以上方法最终返回的结果都有换行符\n,如果不要换行可以用trim()做以下处理 123import android.util.Base64;String encode2 = new String(Base64.encode("123456".getBytes(), Base64.DEFAULT)).trim(); // 结果为"MTIzNDU2"String encode1 = Base64.encodeToString("123456".getBytes(), Base64.DEFAULT).trim(); // 结果为"MTIzNDU2" Android自带Base64解密1234import android.util.Base64;String decode1 = new String(Base64.decode("MTIzNDU2".getBytes(), Base64.DEFAULT)); // 结果为"123456"// 该方式内部其实也是调用上面那个方法,省去了getBytes(),推荐用此方法String decode2 = new String(Base64.decode("MTIzNDU2", Base64.DEFAULT)); // 结果为"123456" Java自带Base64加密解密参考Java8 Base64以上例子用的是Android自带的android.util.Base64类 其实Java也有个java.util.Base64,但是这个类需在Android API 26及以上才可以使用 12345String encode1 = new String(Base64.getEncoder().encode("123456".getBytes())); // 结果为"MTIzNDU2"String encode2 = Base64.getEncoder().encodeToString("123456".getBytes()); // 结果为"MTIzNDU2"String decode1 = new String(Base64.getDecoder().decode("MTIzNDU2".getBytes())); // 结果为"123456"String decode2 = new String(Base64.getDecoder().decode("MTIzNDU2")); // 结果为"123456" 可以看到编码的结果不像Android自带的末尾还有换行符\n 关于Java的Base64还有以下方法 Base64.getUrlEncoder(); Base64.getMimeEncoder(); Base64.getUrlDecoder(); Base64.getMimeDecoder(); 这里不再展开,以后另开一篇文章介绍如果你喜欢我的文章,可以关注我的微信公众号,可以看到我最新发布的文章]]></content>
</entry>
<entry>
<title><![CDATA[Python 虚拟环境 virtualenv]]></title>
<url>%2F2019%2F07%2F06%2FPython-%E8%99%9A%E6%8B%9F%E7%8E%AF%E5%A2%83-virtualenv%2F</url>
<content type="text"><![CDATA[virtualenv安装1pip install virtualenv 创建虚拟环境123C:\Users\Him>d: // 切换到D:盘D:\>cd D:\Python\env // 进入到要创建虚拟环境的目录下D:\Python\env>virtualenv myenv // 创建成功后在D:\Python\env目录下会多一个myenv目录 也可以在创建时指定python版本 1virtualenv -p D:\Program\Python27\python.exe myenv27 进入虚拟环境123D:\Python\env>cd myenv\Scripts // 进入虚拟环境的Scripts目录下D:\Python\env\myenv\Scripts>activate // 运行activate(myenv) D:\Python\env\myenv\Scripts> // 激活成功,注意命令行前多了(myenv) 查看虚拟环境下安装的库123456(myenv) D:\Python\env\myenv\Scripts>pip listPackage Version---------- -------pip 18.0setuptools 40.0.0wheel 0.31.1 在虚拟环境下安装库1(myenv) d:\Python\env\myenv\Scripts>pip install request 退出虚拟环境12(myenv) D:\Python\env\myenv\Scripts>deactivateD:\Python\env\myenv\Scripts> // 注意命令行前的(myenv)没了 requirements文件将当前环境的包写入requirements文件文件,以及根据requirements文件文件安装包。 生成requirements文件文件:pip freeze >requirements.txt 根据requirements文件安装包:pip install -r requirements.txt requirements.txt文件的格式如下: 12345678910111213141516Flask==0.11.1Flask-Bootstrap==3.3.6.0Flask-Login==0.3.2Flask-Migrate==1.8.1Flask-Moment==0.5.1Flask-PageDown==0.2.1Flask-Script==2.0.5Flask-SQLAlchemy==2.1Flask-WTF==0.12html5lib==0.9999999itsdangerous==0.24Jinja2==2.8Mako==1.0.4Markdown==2.6.6MarkupSafe==0.23PyMySQL==0.7.5]]></content>
</entry>
<entry>
<title><![CDATA[python itertools]]></title>
<url>%2F2019%2F07%2F06%2Fpython-itertools%2F</url>
<content type="text"><![CDATA[islice切片 123456789101112131415161718192021222324252627>>> def count(n):... while True:... yield n... n += 1...>>> c = count(0)>>> c[10:20]Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: 'generator' object is not subscriptable>>> # Now using islice()>>> import itertools>>> for x in itertools.islice(c, 10, 20):... print(x)...10111213141516171819>>> 123456789101112131415In [97]: ls = [0,1,2,3,4,5,6,7,8,9]In [98]: for i in itertools.islice(ls, None, 3): # 等价于ls[:3] ...: print(i)012In [99]: for i in itertools.islice(ls, 4, None): # 类似于ls[4:] ...: print(i)456789 dropwhile抛弃符合函数的值,但是实际测试跟我所理解的有所出入 如下代码本来期望去掉b开头的,结果却全部输出了 12345678In [105]: for i in itertools.dropwhile(lambda item : item.startswith('b'), ls): ...: print(i)a1b1a2b2a3b3 再试,去掉小于4的怪哉了,换成数字却又可以了 1234567In [106]: ls = [1,2,3,4,5,6,7,8]In [107]: for i in itertools.dropwhile(lambda item : item < 5, ls): ...: print(i)5678 再试,条件一样还是去掉大于5的,改变列表值这次又不是我所期望的但是好像有点眉目了,第一个item不满足条件就直接输出全部了我们是不是可以假设该函数抛弃前面满足条件的值,直到出现不满足的值后就不再判断,即使在这个值后可能还会出现满足条件的值也不再抛弃 1234567891011In [108]: ls = [6,3,8,9,1,5,4,2]In [109]: for i in itertools.dropwhile(lambda item : item < 5, ls): ...: print(i)63891542 二话不说,赶紧验证一下 1234567891011In [110]: ls = [1,2,3,4,6,3,8,9,1,5,4,2]In [111]: for i in itertools.dropwhile(lambda item : item < 5, ls): ...: print(i)63891542 果然如此 permutations排列(与顺序有关) 123456789101112131415161718In [112]: items = ['a', 'b', 'c']In [113]: for i in itertools.permutations(items): # 默认排列的长度为items的长度 ...: print(i)('a', 'b', 'c')('a', 'c', 'b')('b', 'a', 'c')('b', 'c', 'a')('c', 'a', 'b')('c', 'b', 'a')In [114]: for i in itertools.permutations(items, 2): # 指定排列的长度 ...: print(i)('a', 'b')('a', 'c')('b', 'a')('b', 'c')('c', 'a')('c', 'b') combinations组合(与顺序无关) 1234567891011121314151617In [112]: items = ['a', 'b', 'c']In [116]: for i in itertools.combinations(items, 3): ...: print(i)('a', 'b', 'c')In [117]: for i in itertools.combinations(items, 2): ...: print(i)('a', 'b')('a', 'c')('b', 'c')In [118]: for i in itertools.combinations(items, 1): ...: print(i)('a',)('b',)('c',) combinations_with_replacement元素可重复出现的组合 123456In [119]: items = ['a', 'b']In [120]: for i in itertools.combinations_with_replacement(items, 2): ...: print(i)('a', 'a')('a', 'b')('b', 'b') zip_longest功能与内置函数zip类似,只是输出以最长的序列为准 12345678910111213141516171819202122In [45]: import itertoolsIn [26]: als = [1,2,3,4,5,6]In [27]: bls = ['a','b','c','d']In [46]: for a,b in itertools.zip_longest(als,bls): # 输出以最长的序列为准,不足的为None ...: print(a,b)1 a2 b3 c4 d5 None6 None# None替换成fillvalue的值In [48]: for a,b in itertools.zip_longest(als,bls,fillvalue='letter'): ...: print(a,b)1 a2 b3 c4 d5 letter6 letter chain123456789101112131415161718In [49]: import itertoolsIn [50]: als = [1,2,3,4]In [51]: bls = ('a','b','c','d')# 注意两种不同类型的可迭代随想也可以完美输出In [52]: for item in itertools.chain(als,bls): ...: print(item)1234abcd# 类型In [53]: type(itertools.chain(als,bls))Out[53]: itertools.chain]]></content>
</entry>
<entry>
<title><![CDATA[Android js和原生交互]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-js%E5%92%8C%E5%8E%9F%E7%94%9F%E4%BA%A4%E4%BA%92%2F</url>
<content type="text"><![CDATA[简单的demo1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980package com.xindeco.swingu_h5;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.text.TextUtils;import android.util.Log;import android.view.View;import android.webkit.JavascriptInterface;import android.webkit.ValueCallback;import android.webkit.WebSettings;import android.webkit.WebView;import android.webkit.WebViewClient;import android.widget.EditText;import android.widget.Toast;import com.xindeco.reader.common.Action;import com.xindeco.reader.common.ReaderListener;import com.xindeco.reader.common.Tag;import com.xindeco.reader.swingu.SwingUReader;public class MainActivity extends AppCompatActivity { private WebView mWebView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } private void init() { mWebView = findViewById(R.id.web_view); mWebView.loadUrl("file:///android_asset/test.html"); WebSettings webSettings = mWebView.getSettings(); webSettings.setJavaScriptEnabled(true); mWebView.addJavascriptInterface(new JsInteration(), "android"); mWebView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.equals("https://www.baidu.com/")) { Toast.makeText(MainActivity.this, "就是要拦你", Toast.LENGTH_SHORT).show(); return true; } else { mWebView.loadUrl(url); return true; } } }); findViewById(R.id.call_js).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { final EditText input = findViewById(R.id.input); String content = input.getText().toString().trim(); if (!TextUtils.isEmpty(content)) { // 调用js方法,更新页面// mWebView.loadUrl("javascript:change(\"" + content + "\")"); // 没带返回值的调用方式 mWebView.evaluateJavascript("javascript:change(\"" + content + "\")", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { input.setText(value); } }); } } }); } public class JsInteration { @JavascriptInterface public String toast(String msg) { Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show(); return "我是java的返回值"; } }} 布局 123456789101112131415161718192021222324252627<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <EditText android:id="@+id/input" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="我要改变网页的内容"/> <Button android:id="@+id/call_js" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="我是原生的button啦"/> <WebView android:id="@+id/web_view" android:layout_width="match_parent" android:layout_height="match_parent"/></LinearLayout> HTML页面,把该页面放置在assets文件夹下 123456789101112131415161718192021222324252627282930313233<html> <head> <meta content="text/html; charset=utf-8" http-equiv="content-type"> <title>js和原生交互</title> </head> <script type="text/javascript"> <!--该方法调用原生的toast方法--> function toast(){ var s = document.getElementById("input_toast").value; var result = window.android.toast(s); document.getElementById("input_toast").value=result; } <!--该方法被原生调用--> function change(content){ document.getElementById("input_toast").value=content; return "我是js的返回值"; } </script> <body> <p> <input type="text" id="input_toast"/> </p> <p> <input type="button" id="toast" value="toast" onclick="toast()"/> </p> <a href="https://www.taobao.com/">跳转淘宝,不拦你</a> <br> <a href="https://www.baidu.com/">跳转百度,别拦我</a> </body></html> 记得添加<uses-permission android:name="android.permission.INTERNET"/>权限才能访问网络]]></content>
</entry>
<entry>
<title><![CDATA[Android自定义标题栏]]></title>
<url>%2F2019%2F07%2F06%2FAndroid%E8%87%AA%E5%AE%9A%E4%B9%89%E6%A0%87%E9%A2%98%E6%A0%8F%2F</url>
<content type="text"><![CDATA[void onCreate(Bundle savedInstanceState) {123456public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); // 必须设置在setContentView之前。 setContentView(R.layout.activity_main); getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);//custom_title为自定义标题栏布局} 修改AndroidManifest.xml中该Activity的theme为以下: 其中parent要设为android:Theme或android:Theme.Light,不能设为android:Theme.Holo.Light,否则会报You cannot combine custom titles with other title features错误,可能是因为android:Theme.Holo.Light是用于ActionBar的,跟自定义标题栏有冲突。 50dp//改为跟自定义标题栏的高度一样 @color/title_bg//改为跟自定义标题栏的背景一样]]></content>
</entry>
<entry>
<title><![CDATA[Android 的文件存储]]></title>
<url>%2F2019%2F07%2F06%2FAndroid-%E7%9A%84%E6%96%87%E4%BB%B6%E5%AD%98%E5%82%A8%2F</url>
<content type="text"><![CDATA[选择内存储还是外存储 所有安卓设备都有两个存储区域,内存储和外存储。以前的安卓设备都有提供一个内置的存储(内存储),和一个可插拔的存储(外存储),例如SD卡。但是现在的许多设备是不支持外置存储卡的,而是把内置的存储分成两个区域,相当于内存储和存储。所以不管设备是否支持外置存储卡,它都有内存储和外存储这两个区域。存储操作相关API的调用是一样的。以下是内存储和外存储的区别: 内存储: 一直可用 默认情况下,存储于此的文件只有该应用本身有权限操作。 当用户卸载该应用,系统会删除该应用存储在内存储的所有文件。 如果你不想用户和其他应用操作你的文件,最好的方式就是保存在内存储。 外存储: 并非一直可用,比如当用户用数据线连接电脑并打开数据存储或者移除存储卡。 对用户和其他应用可读。 当用户卸载该应用时必不会删除存储在外存储的文件,除非你保存的时候调用的是getExternalFilesDir(). 如果你要保存的文件对操作权限没有要求,比如和其他应用共享或者允许用户操作,那么最好的方式就是存储在外存储。 Tip:** 默认情况下应用是安装在内存储,但是你可以在manifest中指定android:installLocation的属性值让应用安装在外存储上。特别是当应用应用特别大,而用户又有足够的问存储空间时。更多详情,请查阅App Install Location。 获得外存储权限 要在外存储上执行写的操作,必须在manifest文件中请求WRITE_EXTERNAL_STORAGE权限: 1234<manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ...</manifest> Caution:目前,所有应用不必声明相关权限就可以在外存储上执行读的操作。然而,不久的将来就需要了。如果你的应用需要读取的操作(不需要写的操作),你可以在manifest中声明READ_EXTERNAL_STORAGE权限,这样可以保证应用在后续版本中正常运行(即使当前系统并不要求该权限)。 1234<manifest ...> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> ...</manifest> 如果你的应用已经声明了WRITE_EXTERNAL_STORAGE权限,就没必要再声明READ_EXTERNAL_STORAGE权限,因为WRITE_EXTERNAL_STORAGE默认包含了写的权限。 You don’t need any permissions to save files on the internal storage. Your application always has permission to read and write files in its internal storage directory.对于内存储并不需要任何读写的权限。 在内存储中保存文件 当要在内存储存储文件时,可以通过以下两种方式获取合适的存储位置: getFilesDir() 返回一个分配给该应用的内存储文件夹。 getCacheDir() 返回一个分配给该应用的内存储缓存文件夹File对象。当缓存文件不再需要时记得删除掉。当系统存储不够用时,系统可能会删除掉缓存的文件且没有提示。 要在以上任一种文件夹中创建文件时,可以用File(dir, fileName)构造方法,第一个参数传入以上其中一种方法返回的内存储文件夹对象: 1File file = new File(context.getFilesDir(), filename); 还有另一种方法,你可以调用[openFileOutput()](http://developer.android.com/reference/android/content/Context.html#openFileOutput(java.lang.String, int))来打开一个连接内存储文件的FileOutputStream 对象。如下所示如何在内存储文件中写入内容: 1234567891011String filename = "myfile";String string = "Hello world!";FileOutputStream outputStream;try { outputStream = openFileOutput(filename, Context.MODE_PRIVATE); outputStream.write(string.getBytes()); outputStream.close();} catch (Exception e) { e.printStackTrace();} 或者说,你需要缓存文件,你可以使用[createTempFile()](http://developer.android.com/reference/java/io/File.html#createTempFile(java.lang.String, java.lang.String))。例如以下的示例从一个URL中提取出文件名,再用这个文件名创建一个存储在缓存文件夹中的文件: 12345678910public File getTempFile(Context context, String url) { File file; try { String fileName = Uri.parse(url).getLastPathSegment(); file = File.createTempFile(fileName, null, context.getCacheDir()); catch (IOException e) { // Error while creating file } return file;} Note:一个应用的内存储空间是由应用包名标示的在安卓文件系统中的一个特定区域,技术上来讲,如果把文件的mode设为可读,那么其他应用就可访问。但是,前提是其他应用必须预先知道你的应用的包名和要访问的文件名。除非你明确声明某个文件为可读或可写,否则其他应用无法访问你的内存储文件也没有读写的权限。 在外存储中保存文件 因为外存储有可能是不可用的,比如当用户打开USB数据存储或者是卸载提供外存储的SD卡时,所以每当你要使用外存储是最好先核实下外存储是否可用。你可以通过调用getExternalStorageState()来查询外存储的状态。如果返回的状态是MEDIA_MOUNTED,那么表示你可以读写外存储。例如,以下两个非常实用的方法可以用来确定外存储的可用性: 123456789101112131415161718/* Checks if external storage is available for read and write确定问存储是否可读写 */public boolean isExternalStorageWritable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { return true; } return false;}/* Checks if external storage is available to at least read 确定外存储是都至少可读*/public boolean isExternalStorageReadable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { return true; } return false;} 虽然说整个外存储对于用户和所有应用都是可操作的,但是有两种类型的文件你可能会存储在这里: 公开文件 对其他应用和用户开放的文件应该存储在这里,当用户卸载你的应用时,这些文件应该保留。比如你的应用拍摄的图片或下载的文件。 私有文件 完全属于你的应用的文件并且在用户卸载你的应用时应该被删除的文件可以存储在这里。虽然技术上来说,存储在外存储的文件对于用户和其他应用都可以访问,但是这些文件对于用户和其他应用一文不值。当用户删除你的应用时,系统会自动删除你的应用存储在这里的所有文件。比如说你的应用下载的资源文件或临时的文件。 如果你想在外存储中保存公共文件,可以用getExternalStoragePublicDirectory()方法来获取一个在外存储中的File文件夹对象。这个方法的第一个参数用来指定要保存的文件的类型,可以被系统分类,例如DIRECTORY_MUSIC或者DIRECTORY_PICTURES。如下所示: 123456789public File getAlbumStorageDir(String albumName) { // Get the directory for the user's public pictures directory. File file = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file;} 如果你想保存应用私有的文件,可以调用getExternalFilesDir()来获取相应的文件夹,并在第一个参数指定你想要的类型。每个用这个方式创建的文件夹都是放在一个父文件夹里,这个父文件夹存里放着所有当用户卸载该应用时系统会自动删除的文件。 例如,以下的方法用来创建一个存放照片的文件夹: 123456789public File getAlbumStorageDir(Context context, String albumName) { // Get the directory for the app's private pictures directory. File file = new File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file;} 如果没有一个系统预定义的类型子文件夹的名字适合你要保存的文件,你也可以在调用getExternalFilesDir()方法时第一个参数传null,这样返回的是在外存储中分配给你的应用的私有文件夹根目录。 要记住,存储在由getExternalFilesDir()返回的文件夹中的文件,当用户卸载你的应用时,这些文件全部会被删除。如果你想要用户删除你的应用后还保留这些文件,最好是使用getExternalStoragePublicDirectory()方法。比如说你的应用是个相机应用,当卸载该应用时用户还想保留所拍的照片。 不管你是用getExternalStoragePublicDirectory()来存储共享的文件,还是用getExternalFilesDir()来存储私有文件,最好是使用API常量定义的文件夹名,比如DIRECTORY_PICTURES。使用这些文件夹名可以确保系统正确分类这些文件。例如,保存在DIRECTORY_RINGTONES中的文件会被系统media scanner归类为铃音而非音乐。 查询剩余存储空间 如果你在保存某一文件时事先知道该文件的大小,你可以通过调用getFreeSpace() 或者 getTotalSpace()来确定是否有足够的存储空间来保存该文件以免造成IOException异常。 但是,系统并不保证你通过getFreeSpace()查询出来有多少剩余存储就可以存储多少。如果剩余存储容量比你要存储的文件大小大个几MB,那么一般可以成功存储。 Note:其实你也可以不用在存储文件前预先查询剩余多少存储空间,你可以尝试着直接写入数据,然后如果有出现异常直接捕获就行了。这种写法一般可以用在当你事先不知道要写入数据的大小的时候。比如说当你要保存一张PNG格式的图片前要先把他转化为JPEG格式,那么你就不知道要保存的数据的大小。 删除文件 你应该经常删除不再需要的文件,最直接的方法就是直接调用File对象的delete()方法。 1myFile.delete(); 如果要删除的文件是存储在内存储中,你可以通过调用Conext的deleteFile()方法来删除。 1myContext.deleteFile(fileName); Note:当用户卸载你的应用时,安卓系统会自动删除以下位置的文件: 存储在内存储中的所有文件 通过getExternalFilesDir()方法存储在外存储中的所有文件。 然而你应该制定一个规则定期删除内存储缓存文件夹中的缓存文件和其他你不再需要的文件。]]></content>
</entry>
<entry>
<title><![CDATA[Failed to fetch URL https://dl-ssl.google.com/android/repository/addons_list-1.xml]]></title>
<url>%2F2019%2F07%2F06%2FFailed-to-fetch-URL-https-dl-ssl-google-com-android-repository-addons-list-1-xml%2F</url>
<content type="text"><![CDATA[Do the following: Close the sdk manager and eclipse. Go to the folder where you have stored your adt. In that adt folder you’ll find a folder known as tools. Make a copy of the contents of that folder and paste it in a folder called copytools. Now go to the command prompt and go to the location of the copytools. Then execute the command android.bat the sdk manager will start. Now update all the plugins you want. It’ll update your original folder. After the update delete the copy.]]></content>
</entry>
<entry>
<title><![CDATA[java进制转换]]></title>
<url>%2F2019%2F07%2F06%2Fjava%E8%BF%9B%E5%88%B6%E8%BD%AC%E6%8D%A2%2F</url>
<content type="text"><![CDATA[12345678910111213141516171819202122232425262728int n1 = 14;//十进制转成十六进制:Integer.toHexString(n1);//十进制转成八进制Integer.toOctalString(n1);//十进制转成二进制Integer.toBinaryString(12);//十六进制转成十进制Integer.valueOf("FFFF",16).toString();//十六进制转成二进制Integer.toBinaryString(Integer.valueOf("FFFF",16));//十六进制转成八进制Integer.toOctalString(Integer.valueOf("FFFF",16));//八进制转成十进制Integer.valueOf("576",8).toString();//八进制转成二进制Integer.toBinaryString(Integer.valueOf("23",8));//八进制转成十六进制Integer.toHexString(Integer.valueOf("23",8));//二进制转十进制Integer.valueOf("0101",2).toString();//二进制转八进制Integer.toOctalString(Integer.parseInt("0101", 2));//二进制转十六进制Integer.toHexString(Integer.parseInt("0101", 2));]]></content>
</entry>
<entry>
<title><![CDATA[RecyclerView.ItemDecoration实现StrickyHeader、粘性头部、悬停头部]]></title>
<url>%2F2019%2F06%2F19%2FRecyclerView-ItemDecoration%E5%AE%9E%E7%8E%B0StrickyHeader%E3%80%81%E7%B2%98%E6%80%A7%E5%A4%B4%E9%83%A8%E3%80%81%E6%82%AC%E5%81%9C%E5%A4%B4%E9%83%A8%2F</url>
<content type="text"><![CDATA[参考 RecyclerView探索之通过ItemDecoration实现StickyHeader效果这篇文章真是完美,写得非常清晰,一步步实现下来,看完真是茅塞顿开,以前一直以为这个知识点很难的。 Android 从零开始实现RecyclerView分组及粘性头部效果这篇文章貌似更牛逼,但是没深入去看 RecyclerView 悬浮/粘性头部——StickyHeaderDecoration 其实在GitHub上已经有很多开源的很成熟的StickyHeader项目 timehop/sticky-headers-recyclerview 先来实现效果镇楼 实现代码Activity 123456789101112131415161718192021222324252627282930313233343536373839404142434445package com.him.stickyheader;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity { public List<String> datas; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView mRecyclerView = findViewById(R.id.recycler_view); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mRecyclerView.addItemDecoration(new MyItemDecoration(new MyItemDecoration.GroupInfoCallback() { // 根据全局数据的位置position查询所属分组的信息GroupInfo @Override public GroupInfo getGroupInfo(int position) { // 测试数据,暂时10条数据一组 int size = 10; GroupInfo info = new GroupInfo(); info.groupId = position / size; info.title = info.groupId + ""; info.position = position % size; info.groupSize = size; return info; } })); initDatas(); mRecyclerView.setAdapter(new MyAdapter(datas)); } // 初始化测试数据 private void initDatas() { datas = new ArrayList<>(); for (int i = 0; i < 100;i++) { datas.add("test " + i); } }} Adapter 1234567891011121314151617181920212223242526272829303132333435363738394041package com.him.stickyheader;import android.support.annotation.NonNull;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import java.util.List;public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> { public List<String> datas; public MyAdapter(List<String> datas) { this.datas = datas; } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(@NonNull MyViewHolder viewHolder, int i) { viewHolder.text.setText(datas.get(i)); } @Override public int getItemCount() { return datas == null ? 0 :datas.size(); } public class MyViewHolder extends RecyclerView.ViewHolder { TextView text; public MyViewHolder(@NonNull View itemView) { super(itemView); text = itemView.findViewById(R.id.text); } }} ItemDecoration,这是最重要的代码 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109package com.him.stickyheader;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.support.annotation.NonNull;import android.support.v7.widget.RecyclerView;import android.view.View;import java.util.function.IntBinaryOperator;public class MyItemDecoration extends RecyclerView.ItemDecoration { // 普通分割线的高度 private int dividerHeight = 1; // 分组header高度 private int headerHeight = 65; // 根据position获取分组信息的回调 private GroupInfoCallback callback; // 画笔 private Paint paint; public MyItemDecoration(GroupInfoCallback callback) { this.callback = callback; paint = new Paint(); } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); int itemPosition = parent.getChildAdapterPosition(view); GroupInfo info = callback.getGroupInfo(itemPosition); if (info.isFirstItem()) { // 分组第一个item撑开一个header的高度 outRect.top = headerHeight; } else { // 其他item撑开一个普通divider的高度 outRect.top = dividerHeight; } } @Override public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.onDraw(c, parent, state); // header左右边的坐标,注意考虑parent的左右padding float left = parent.getPaddingLeft(); float right = parent.getWidth() - parent.getPaddingRight(); // 循环RecyclerView中当前可见的View for (int i = 0; i < parent.getChildCount(); i++) { View child = parent.getChildAt(i); // 根据当前view获取在整个列表中的位置 int itemPosition = parent.getChildAdapterPosition(child); // 根据position获取所属分组的信息 GroupInfo info = callback.getGroupInfo(itemPosition); // 重点来了,敲黑板 // 注意这里的i表示在RecyclerView当前可见View中的位置(第几个) // i == 0表示当前可见的第一个View // 这里绘制分组标题悬停的效果 if (i == 0) { // 分组标题停在最顶部,就是parent的顶部,然后再考虑顶部的padding float top = parent.getPaddingTop(); // top加分组高度就是底部坐标 float bottom = top + headerHeight; // 这里是第二个重点 // 如果没这段代码,就无法实现下面的分组标题把当前悬停的分组标题往上推出页面的效果 // 分组的最后一个item且该item的底部坐标小于悬停顶部的header的底部坐标的情况下,需执行以下代码,重新计算header的位置 if (info.isLastItem() && bottom > child.getBottom()) { bottom = child.getBottom(); top = bottom - headerHeight; } drawHeader(left, top, right, bottom, c, info); } else if (info.isFirstItem()) { // 如果不是可见的第一个view但是是分组的第一个item,则绘制分组标题 // item的顶部再往上一个header的高度 float top = child.getTop() - headerHeight; // 顶部坐标加上header高度就是底部坐标 float bottom = top + headerHeight; drawHeader(left, top, right, bottom, c, info); } } } // 根据计算出的header位置绘制header及header中的title private void drawHeader(float left, float top, float right, float bottom, Canvas c, GroupInfo info) { // 设置header背景色,然后绘制 paint.setColor(Color.GREEN); c.drawRect(left, top, right, bottom, paint); // 设置title文字大小,然后计算文字高度,用于接下来计算文字绘制的位置 paint.setTextSize(32); paint.setColor(Color.BLUE); Rect rect = new Rect(); // 计算要绘制的文字的宽高 paint.getTextBounds(info.title, 0, info.title.length(), rect); int textWidth = rect.width(); int textHeight = rect.height(); // 文字距左边的距离 float textX = left + 40; // 计算baseline,中文情况下为文字的底部 float textY = top + (headerHeight + textHeight)/2; c.drawText(info.title, textX, textY, paint); } // 根据所在列表的位置计算所属分组的信息 public interface GroupInfoCallback { GroupInfo getGroupInfo(int position); }} 分组信息的Model类 12345678910111213141516171819202122package com.him.stickyheader;public class GroupInfo { // 分组的id public int groupId; // 分组显示的title public String title; // 在分组中的位置 public int position; // 分组的大小 public int groupSize; // 是否是分组的第一个 public boolean isFirstItem() { return position == 0; } // 是否是分组的最后一个 public boolean isLastItem() { return position == (groupSize - 1); }} Activity布局 123456789101112131415<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" android:background="#b5b8de"> <!--设置和item不同的背景色,当做divider的颜色--> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"/></android.support.constraint.ConstraintLayout> item布局 123456789101112<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#ffffff"> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp"/></android.support.constraint.ConstraintLayout> 获取源代码扫描以下二维码关注我的微信公众号野猿新一,发送“悬停头部”获取源代码的下载方式。]]></content>
</entry>
<entry>
<title><![CDATA[Hello World]]></title>
<url>%2F2019%2F06%2F16%2Fhello-world%2F</url>
<content type="text"><![CDATA[Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1$ hexo new "My New Post" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment]]></content>
</entry>
</search>