Android:第一个Android APP发布

第一个..

开源了APP的所有代码[访问地址].

初试牛刀,学了这么久的Android.总算在磕磕绊绊中完成了第一个APP(虽然问题还有一大堆)…

只记得是从去年开始学习的Android(自学),期间学习进度比较缓慢,但也还是”捣鼓”出了几个不叫APP的APP.现在发布的这个,拖拉拖拉的做了4个月,其实折算下来,时间应该是不长的.没办法,期间有各种问题.大部分时间都花在解决大问题上了.

其实写了这么久,多少还是有点感情的.我有一个不知道是不是不太好的习惯,有时候习惯在代码里面码一些注释,就是写完那个功能或者那个模块的感受……

下面也会贴一些其中的注释.更重要的是,我开源了这个APP[访问地址],如果不愿意去看开源的源码,也要去那边的文件夹才能下载的…(因为哥的服务器实在架不住大楼量啊).还需要说明的一个问题是:因服务器的原因,在代码里面所有跟服务器交互的地方都被去掉了,这也是逼不得已,希望能谅解一下.但我保证,其余部分的代码是完整的(跟发布版的一毛一样的).

很遗憾,这个APP没能在任何市场上架,想必这也是一个不太美好的地方了.后来想想不上就不上吧,直接发布也挺好,反正也是要开源的.(源码地址在上面找)写这个东西的时候,其实已经没多少兴奋劲了,因为都已经打包一段时间了,才把源码公开出来(也是希望新手可以学习下,虽然我也是新手).

啰嗦了一大堆,想起后面还有年终总结要写.好吧……停止啰嗦,下面发干货(对了这个APP叫:凉夜).

大概做了什么

1. 看看(文字内容,采集自网络):
详细:自有国外服务器,提供数据支撑,文字内容同样基于该服务器提供
(对于访问速度是最大的性能瓶颈,希望后期有能力可以改善)。当前更新频率为3天更新一次。
在Android客户端提供了缓存及下拉刷新操作。在分享方面,目前集成了微信、易信、QQ这几个分享。

2. 有图(图片,每天定时更新):
详细:图片源自某门户每天更新的图片,后台服务器每天零时左右定时进行更新。
客户端对于服务器端有相应延迟(避免提前请求导致的加载错误)。
该项同样提供文字方式的分享(在分享方式上有差别),并提供保存本地及即时设置桌面功能。

3. 关于(关于APP、设置等操作):
详细:可在客户端开启[每天自动设置桌面功能(使用两个Service相互监听,
保障可正确更新,若开启,该功能会延迟更新.)]、提交反馈、关于等。

Android LOGO
image-2382

代码里面的啰嗦

下面的都是来自代码里面的.可以在[地址]里面的MainActivity.java里面找到.

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
/*
* 今天开始重写之前的"闲聊",只是现在改名字了--凉夜。
初次的意思是,在每一个凉的夜,都需要一些快乐或者温暖。
刚写还是遇到一些困难,也挺头疼的。
加油!
prd - 2015.8.15 2:20
今天调整了很久ListView的显示,加上泛型也用不了导致解析JSON数据出了很大问题,只能暂时找个办法先解决了。2015.8.16 3:30
今天在一个群的朋友的帮助下,总算解决了Fragment加载图片的问题,真心觉得很开心。也谢谢那个朋友。因为这个问题实在困扰了好几天啊。。。。。。
2015.8.22 1:10
这可能是到目前为止,单日增加功能最多的一次了。
本次共实现以下功能:
        1.为自动设置桌面增加两个Service(保证Service一直存在);
        2.为Image增加一个从底部弹出的框;
        3.将NetworkUtils从new,改为单例.
看起来功能并不多,但是代码量真心不小.
2015.8.29 23:40
=====================================================================================
这次更新了很多东西,已更新如下:
            1.完成ActionBar右侧只在[看图]中显示.
            2.在[我]菜单下,新增一个无限量滚动广告图.
            3.修复在[我->有话要说]里面输入完之后,点击空白处关闭虚拟键盘.
            4.-->重大修改-->修改工具类,获取JSON数据的方法(工具类不在处理,具体的对象转换),代码结构更加清晰.
            5.修复ActionBar的图标大小问题.
            6.修复下载图片的路径,并实时刷新系统图片媒体库.
待优化以下问题:
            1.多次点击ActionBar中的图标按钮,会导致内存溢出.
            2.实现一个留言反馈功能(注意过滤非法字符,可以考虑新增一个图片选择功能..)
            3.实现一个关于界面.
            4.对APP整体颜色进行优化.
            5.对相应代码进行优化或简化.
            6.开源.
2015.9.4 1:40
===================================================================
今天优化了一下界面颜色,有以下改进:
        1.修改[我]界面中的图标颜色.
        2.修正[反馈][关于]中文字不居中的问题.
        3.优化所有颜色,从COLOR文件获取.
        4.优化[看图]底部弹出框颜色.
        5.优化[自动设置桌面]弹窗的颜色.
        6.[看]新增一个复制功能.
       2015.9.8 22:29
2015.9.10  实现了微信的分享,接下来可能需要实现微博,易信的分享功能.
            并且优化了[我]下面的轮播展示.
            2015.9.13 为工具类添加更加详细的注释.并针对代码进行部分优化.
            2015.8.15 2:20 开始开发.
            2015.12.1  0:30 完成开发.
                                  不再开发新的功能,后续几天将进入统一测试.
                                  预计12.5左右发布第一个正式版本.
                                  从8.15开始第一行代码,拖拖拉拉接近3个多月,三大功能模块,最占时间莫过于[分享]部分.
                                  无论如何,这都是一个开始.从刚开始的菜鸟,到如今的菜鸟,虽然不知到进步多少,但只能说,每一次更新
                                  都是一个大的进步.对于下一个版本,将着重优化服务器端,及客户端逻辑代码.
                                  之前的[闲聊]算是一个失败的典型,而当前这个则是完全站立在失败之上.
                                  加油,不为什么!
                                  ->:你的人生永远不会辜负你的。那些转错的弯,那些走错的路,那些流下的泪水,那些滴下的汗水,那些留下的伤痕,全都让你成为独一无二的自己。by 朱学恒
* */

一个小网络工具类

别着急,还要发布个Android的网络工具类,使用了Volley.封装了一些简单的操作.源码里面说的很清楚,就不重复解释了.

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
import android.app.ActivityManager;
import android.app.WallpaperManager;
import android.content.*;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaScannerConnection;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.*;
import com.android.volley.toolbox.*;
import com.w1520.liangye.app.R;

import java.io.*;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <p>工具类</p>
 * <p/>
 * <p>此类使用Volley实现网络操作.</p>
 * <p/>
 * <p>当前类封装了:文字,图片的加载,Toast的各种实现,以及</p>
 * <p>保存图片信息到本地,复制文本数据,转换ImageView等操作.</p>About
 * <p/>
 * <p>这个类不可以被继承或重写,并且使用单例模式保证无论任何时候,</p>
 * <p>内存中仅存在一个当前类的实例.</p>
 * <p/>
 * <p><strong>注意:当前类的加载数据,不适合下载,或者需要操作大量数据.</strong></p>
 * <p>
 * <p>
 * Created by puruidong on 8/14/15.
 */
public final class NetworkUtils {

    //网络请求队列实例.
    private static RequestQueue queue;
    //上下文实例.
    private Context context;
    //当前类实例.默认为null
    private static NetworkUtils network = null;


    /**
     * 创建一个实例时,内部初始化.
     * 外部无需关心此构造函数的实现.
     *
     * @param context
     */
    private NetworkUtils(Context context) {
        this.context = context.getApplicationContext();
        if (queue == null) {
            queue = Volley.newRequestQueue(context);
        }
    }

    /**
     * 获取一个当前类的实例.
     * 注意:当前类的实例仅能通过此方式获得.
     * 以保证在内存中仅存在唯一一个当前类的实例.
     *
     * @param context 上下文实例.
     * @return 当前类的实例.
     */
    public static NetworkUtils getInstance(Context context) {
        if (network == null) {
            network = new NetworkUtils(context);
        }
        return network;
    }

    /**
     * 检查网络连接是否可用.
     * 不区分是3G还是WIFI.
     *
     * @return 若网络可用返回true, 反之返回false.
     */
    public boolean isOnline() {
        ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        return (networkInfo != null && networkInfo.isConnected());
    }

    /*-------------------------------------[GET]从网络获取文字数据---------------------------------------------*/

    /**
     * 获取文本数据.
     * <p>
     * 若无法访问网络,将关闭加载提示框,
     * 并给出无法访问网络的提示.
     *
     * @param url          获取数据的url,此方法不检测url的正确性.
     * @param volleyResult 数据加载成功后的回调接口,用于传递数据
     * @param dialog       数据加载提示框.
     */
    public void getTextData(String url, final VolleyResultData volleyResult, CustomProgressDialog dialog) {
        if (!isOnline()) {
            showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
            if(null!=dialog) {
                dialog.hide();
            }
        } else {
            StringRequest strArray = new StringRequest(url, new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    volleyResult.onSuccess(response);
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    showToast("啊哦!~暂时加载失败了,要不稍候在试试?..呜..", Toast.LENGTH_SHORT);
                }
            }
            );
            queue.add(strArray);
        }
    }

    /*-------------------------------------[GET]从网络获取文字数据---------------------------------------------*/

    /*-------------------------------------[POST]提交请求,从网络获取数据---------------------------------------------*/

    /**
     * POST方式提交普通参数数据.
     * --------------------------------------
     * 若无法访问网络,将关闭加载提示框,
     * 并给出无法访问网络的提示.
     *
     * @param url          获取数据的url,此方法不检测url的正确性.
     * @param params       POST方式提交数据的参数.* :@see {@link android.support.v4.util.ArrayMap}
     * @param volleyResult 数据加载成功后的回调接口,用于传递数据
     * @param dialog       数据加载提示框.
     */
    public void postData(String url, final Map<String, String> params, final VolleyResultData volleyResult, CustomProgressDialog dialog) {
        if (!isOnline()) {
            showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
            if(null!=dialog) {
                dialog.hide();
            }
        } else {
            StringRequest postRequest = new StringRequest(Request.Method.POST, url,
                    new Response.Listener<String>() {
                        @Override
                        public void onResponse(String response) {
                            volleyResult.onSuccess(response);
                        }
                    }, null) {
                @Override
                protected Map<String, String> getParams() {
                    //在这里设置需要post的参数
                    return params;
                }
            };
            queue.add(postRequest);
        }
    }


    /**
     * 使用Volley请求文本数据,
     * 实现此接口自行处理相关String数据.
     * 即可获取数据.
     */
    public interface VolleyResultData {
        void onSuccess(String response);
    }


    /*-------------------------------------[POST]提交请求,从网络获取文字数据---------------------------------------------*/



    /*-------------------------------------从网络获取图片数据---------------------------------------------*/


    /**
     * 通过ImageRequest加载并设置网络图片.
     * 注意:方法不对url的有效性进行检测.
     * 若无法访问网络,将关闭加载提示框,
     * 并给出无法访问网络的提示.
     * <p>
     * 此方法使用了默认实现的缓存.
     *
     * @param url       图片url
     * @param view      图片控件所在的View对象
     * @param imgViewId ImageView或其它图片展示控件的Id
     * @param dialog    图片加载提示框对象.
     * @see {@link com.w1520.liangye.utils.NetworkUtils#getImageByImageRequest(String, onImageLoaderListener, CustomProgressDialog)}
     */
    public void getImageByImageRequest(String url, final View view, final int imgViewId, CustomProgressDialog dialog) {
        if (!isOnline()) {
            showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
            if(null!=dialog) {
                dialog.hide();
            }
        } else {

            ImageRequest request = new ImageRequest(url, new Response.Listener<Bitmap>() {
                @Override
                public void onResponse(Bitmap response) {
                    //显示加载成功的图片.
                    ImageView img = (ImageView) view.findViewById(imgViewId);
                    img.setImageBitmap(response);
                }
            }, 0, 0, ImageView.ScaleType.CENTER_INSIDE, Bitmap.Config.RGB_565, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    //显示加载失败的图片.
                    showToast("啊哦!~暂时加载失败了,要不稍候在试试?..呜..", Toast.LENGTH_SHORT);
                    ImageView img = (ImageView) view.findViewById(imgViewId);
                    img.setImageResource(R.mipmap.image_loader_error);
                }
            });
            queue.add(request);
        }
    }


    /**
     * 通过ImageRequest加载并传递值给回调接口.
     * 若无法访问网络,将关闭加载提示框,
     * 并给出无法访问网络的提示.
     * 注意:方法不对url的有效性进行检测.
     * <p>
     * 此方法使用了默认实现的缓存.
     *
     * @param url            图片url地址.
     * @param loaderlistener 接收Bitmap的回调接口.若请求响应为空,回调接口返回空.
     * @param dialog         图片加载提示框对象.
     * @see {@link com.w1520.liangye.utils.NetworkUtils#getImageByImageRequest(String, View, int, CustomProgressDialog)}
     */
    public void getImageByImageRequest(String url, final onImageLoaderListener loaderlistener, CustomProgressDialog dialog) {
        if (!isOnline()) {
            showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
            if (null != dialog) {
                if(null!=dialog) {
                    dialog.hide();
                }
            }
        } else {
            ImageRequest request = new ImageRequest(url, new Response.Listener<Bitmap>() {
                @Override
                public void onResponse(Bitmap response) {
                    //显示加载成功的图片.
                    loaderlistener.onSuccessImage(response);
                }
            }, 0, 0, null, null, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    //显示加载失败的图片.
                    //showToast("啊哦!~暂时加载失败了,要不稍候在试试?..呜..", Toast.LENGTH_SHORT);
                    //error.printStackTrace();
                }
            });
            queue.add(request);
        }
    }


    /**
     * 通过NetworkImageView加载
     * 并设置网络图片,若无法访问网络,将关闭加载提示框,
     * 并给出无法访问网络的提示.此方法会给予一个默认图片及
     * 一个加载失败时候的图片.注意:方法不对url的有效性进行检测.
     * <p>
     * 注意:此方法已经默认实现了一个图片缓存.具体可参考:
     * {@link com.w1520.liangye.utils.BitmapCache}
     *
     * @param url              图片url地址.
     * @param view             图片控件所在的View对象activitd
     * @param netWorkImgViewId ImageView或其它图片展示控件的Id
     * @param dialog           图片加载提示框对象.
     * @see {@link com.w1520.liangye.utils.NetworkUtils#getImageByImageLoader(String, View, int, CustomProgressDialog)}
     */
    public void getImageByNetworkImageView(String url, final View view, final int netWorkImgViewId, CustomProgressDialog dialog) {
        if (!isOnline()) {
            showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
            if(null!=dialog) {
                dialog.hide();
            }
            return ;
        }
        ImageLoader loader = new ImageLoader(queue, new BitmapCache());
        NetworkImageView networkImageView = (NetworkImageView) view.findViewById(netWorkImgViewId);
        networkImageView.setDefaultImageResId(R.mipmap.image_loading);
        networkImageView.setErrorImageResId(R.mipmap.image_loader_error);
        networkImageView.setImageUrl(url, loader);
    }

    /**
     * 通过ImageLoader加载并设置网络图片
     * 注意:方法不对url的有效性进行检测.
     * 若无法访问网络,将关闭加载提示框,
     * 并给出无法访问网络的提示.
     * <p>
     * 此方法会设置一个默认加载图片,及一个加载失败时
     * 显示的图片.
     * <p>
     * 注意:此方法已经默认实现了一个图片缓存.具体可参考:
     * {@link com.w1520.liangye.utils.BitmapCache}
     *
     * @param url       图片url
     * @param view      图片控件所在的View对象
     * @param imgViewId ImageView或其它图片展示控件的Id
     * @param dialog    图片加载提示框对象.
     * @return 返回一个响应图片的资源.
     * @see {@link com.w1520.liangye.utils.NetworkUtils#getImageByNetworkImageView(String, View, int, CustomProgressDialog)}
     */
    public View getImageByImageLoader(String url, View view, int imgViewId, CustomProgressDialog dialog) {
        if (!isOnline()) {
            showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
            if(null!=dialog) {
                dialog.hide();
            }
        }
        ImageLoader loader = new ImageLoader(queue, new BitmapCache());
        ImageView imageView = (ImageView) view.findViewById(imgViewId);
        ImageLoader.ImageListener listener = ImageLoader.getImageListener(imageView, R.mipmap.image_loading, R.mipmap.image_loader_error);
        loader.get(url, listener);
        return imageView;
    }


    /**
     * 获取网络响应的图片实现此接口,重写接口中的方法即可获得该图片资源.
     * 若网络数据返回为null,则接口中的方法将返回null.
     */
    public interface onImageLoaderListener {
        void onSuccessImage(Bitmap bitmap);
    }

    /*-------------------------------------从网络获取图片数据---------------------------------------------*/


    /**
     * 快速输出Toast,默认显示时长为 {@link  android.widget.Toast#LENGTH_SHORT}.
     * 不支持布局自定义和显示时长自定义.
     *
     * @param message 要显示的信息.
     * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(View view, String message, int duration)}
     * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(String message, int duration)}
     */
    public void showToast(String message) {
        Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
    }


    /**
     * 显示Toast,可以使用自定义的布局,自定义显示时长.
     *
     * @param view     一个针对Toast的自定义布局.
     * @param message  要显示的信息.
     * @param duration 显示时长,请使用Toast中的常量进行设置.
     *                 可选值:{@link android.widget.Toast#LENGTH_SHORT}
     *                 {@link android.widget.Toast#LENGTH_LONG}
     * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(String message, int duration)}
     * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(String message)}
     */
    public void showToast(View view, String message, int duration) {
        Toast toast = new Toast(context);
        toast.setView(view);
        toast.setDuration(duration);
        toast.setText(message);
        toast.show();
    }

    /**
     * 显示带有图片,居中的Toast.
     *
     * @param message  要显示的信息.
     * @param duration 显示时长,请使用Toast中的常量进行设置.
     *                 可选值:{@link android.widget.Toast#LENGTH_SHORT}
     *                 {@link android.widget.Toast#LENGTH_LONG}
     * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(View view, String message, int duration)}
     * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(String message)}
     */
    public void showToast(String message, int duration) {
        LayoutInflater inflater = LayoutInflater.from(context);
        View toast_view = inflater.inflate(R.layout.custom_toast_center, null);
        ((TextView) toast_view.findViewById(R.id.toast_message)).setText(message);
        Toast toast = new Toast(context);
        toast.setView(toast_view);
        toast.setGravity(Gravity.CENTER, 0, 0);
        toast.setDuration(duration);
        toast.show();
    }


    /**
     * 判断进程是否处于正在运行状态.
     *
     * @return 正在运行返回true, 反之返回false.
     */
    public boolean isProessRunning(String proessName) {
        boolean isRunning = false;
        ActivityManager am = (ActivityManager) context
                .getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> lists = am.getRunningAppProcesses();
        for (ActivityManager.RunningAppProcessInfo info : lists) {
            if (info.processName.equals(proessName)) {
                isRunning = true;
            }
        }
        return isRunning;
    }


    /**
     * 将一张图片(Bitmap资源)设置为手机桌面壁纸.
     * <p>
     * 需要权限:{@code
     * <uses-permission android:name = "android.permission.SET_WALLPAPER"/>}
     *
     * @param bitmap 要设置为桌面的资源.
     * @throws IOException 资源不存在或资源损坏导致的IO异常.
     */
    public void setWallPaper(Bitmap bitmap) throws IOException {
        WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
        wallpaperManager.setBitmap(bitmap);
    }

    /**
     * 获取SD卡或者内置存储空间可以保存资源的路径.
     * <em>此处未实现对存储空间是否充足进行判断</em>.
     *
     * @return 返回保存数据的路径, 有SD卡则是SD上的路径, 反之内置存储空间上的路径.
     */
    private String getSDPath() {
        boolean hasSDCard = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
        if (hasSDCard) {
            return Environment.getExternalStorageDirectory().toString() + "/Pictures/liangye";
        } else
            return "/data/data/com.w1520.liangye/liangye";
    }

    /**
     * 判断当前系统LEVEL
     * Android在4.4之后不允许第三方发起系统广播.
     * 所以需要判断版本.
     *
     * @return 当前版本是否大于等于19[4.4].
     */
    private boolean hasKitkat() {
        return Build.VERSION.SDK_INT >= 19;
    }

    /**
     * 刷新媒体库
     *
     * @param filePath
     * @param context
     */
    private void scanPhotos(String filePath) {
        Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        Uri uri = Uri.fromFile(new File(filePath));
        intent.setData(uri);
        context.sendBroadcast(intent);
    }

    /**
     * 刷新媒体库.
     *
     * @param currFilePath
     */
    private void refreshPicture(String currFilePath) {
       /* ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DATA,currFilePath);
        values.put(MediaStore.Images.Media.MIME_TYPE,"image/jpeg");
        context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,values);*/

        if (hasKitkat()) {
            MediaScannerConnection.scanFile(context,
                    new String[]{currFilePath}, new String[]{"image/*"},
                    new MediaScannerConnection.OnScanCompletedListener() {
                        public void onScanCompleted(String path, Uri uri) {
                            context.sendBroadcast(new Intent(android.hardware.Camera.ACTION_NEW_PICTURE, uri));
                            context.sendBroadcast(new Intent("com.android.camera.NEW_PICTURE", uri));
                        }
                    });
            scanPhotos(currFilePath);
        } else {
            context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + currFilePath)));
        }
    }


    /**
     * 转换ImageView为Bitmap.
     *
     * @param view 需要转换的View,通常是ImageView或其它类似的类.
     * @return 转换后的Bitmap.
     */
    public Bitmap convertViewToBitmap(View view) {
        view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        view.buildDrawingCache();
        Bitmap bitmap = view.getDrawingCache();
        return bitmap;
    }


    /**
     * <p>保存图片到本地
     * <p/>
     * <p>需要权限:</p>
     * {@code
     * <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     * <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
     * }
     *
     * @param imgView 要保存的ImageView对象.
     * @throws IOException           写文件时导致的异常.
     * @throws FileNotFoundException 文件未找到时,或路径不存在时抛出此异常.
     * @see {@link com.w1520.liangye.utils.NetworkUtils#saveImages(Bitmap bitmap)}
     */
    public void saveImages(ImageView imgView) {
        Bitmap bitmap = convertViewToBitmap(imgView);
        String strPath = getSDPath();
        DateUtils dateutils = DateUtils.getInstance();
        String strFileName = dateutils.getCurrentTimeById() + ".jpg";
        FileOutputStream fos = null;
        try {
            File destDir = new File(strPath);
            if (!destDir.exists()) {
                destDir.mkdirs();
            }
            final String filePaths = strPath + "/" + strFileName;
            File imageFile = new File(filePaths);
            imageFile.createNewFile();
            fos = new FileOutputStream(imageFile);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos);
            fos.flush();
            showToast("已成功保存到相册", Toast.LENGTH_SHORT);
            refreshPicture(filePaths);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 保存图片到本地
     * <p/>
     * <p>需要权限:</p>{@code
     * <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     * <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>}
     *
     * @param bitmap 要保存的图片.
     * @throws IOException           写文件时导致的异常.
     * @throws FileNotFoundException 文件未找到时,或路径不存在时抛出此异常.
     * @see {@link com.w1520.liangye.utils.NetworkUtils#saveImages(ImageView imgView)}
     */
    public void saveImages(Bitmap bitmap) {
        String strPath = getSDPath();
        DateUtils dateutils = DateUtils.getInstance();
        String strFileName = dateutils.getCurrentTimeById() + ".jpg";
        FileOutputStream fos = null;
        try {
            File destDir = new File(strPath);
            if (!destDir.exists()) {
                destDir.mkdirs();
            }
            final String filePaths = strPath + "/" + strFileName;
            File imageFile = new File(filePaths);
            imageFile.createNewFile();
            fos = new FileOutputStream(imageFile);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos);
            fos.flush();
            showToast("已成功保存到相册", Toast.LENGTH_SHORT);
            refreshPicture(filePaths);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 图片按比例大小压缩方法
     *
     * @param image
     * @return
     * @see {@link http://104zz.iteye.com/blog/1694762 }
     */
    public Bitmap comp(Bitmap image) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        if (baos.toByteArray().length / 1024 > 1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出
            baos.reset();//重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, 50, baos);//这里压缩50%,把压缩后的数据存放到baos中
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        //开始读入图片,此时把options.inJustDecodeBounds 设回true了
        newOpts.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
        float hh = 800f;//这里设置高度为800f
        float ww = 480f;//这里设置宽度为480f
        //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
        int be = 1;//be=1表示不缩放
        if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0)
            be = 1;
        newOpts.inSampleSize = be;//设置缩放比例
        //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
        isBm = new ByteArrayInputStream(baos.toByteArray());
        bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
        return compressImage(bitmap);//压缩好比例大小后再进行质量压缩
    }

    /**
     * 质量压缩
     *
     * @param image
     * @return
     */
    private Bitmap compressImage(Bitmap image) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
        int options = 100;
        while (baos.toByteArray().length / 1024 > 100) {  //循环判断如果压缩后图片是否大于100kb,大于继续压缩
            baos.reset();//重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
            options -= 10;//每次都减少10
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片
        return bitmap;
    }


    /**
     * 复制文本
     *
     * @param label 标记.
     * @param data  要被复制的文本.
     */
    public void copyText(String label, String data) {
        ClipboardManager clipManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText(label, data);
        clipManager.setPrimaryClip(clip);
        showToast("复制成功", Toast.LENGTH_SHORT);
    }


    /**
     * 判断email格式是否正确
     *
     * @param email
     * @return
     */
    public boolean isEmail(String email) {
        String str = "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$";
        Pattern p = Pattern.compile(str);
        Matcher m = p.matcher(email);
        return m.matches();
    }


}

可能是因为代码的原因,好久都没”写”这么长的文了…偶然有点不习惯……

嗯,没错–你的人生永远不会辜负你的。那些转错的弯,那些走错的路,那些流下的泪水,那些滴下的汗水,那些留下的伤痕,全都让你成为独一无二的自己。by 朱学恒

《Android:第一个Android APP发布》上有4条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

*

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据